From e12580f6010c7ac752f559e7425fbed108ae481e Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 29 Jan 2025 19:09:32 +0100 Subject: [PATCH 01/45] chore: update go version and packages --- .github/workflows/api-specs-pr.yml | 18 +- .github/workflows/go.yml | 9 +- .github/workflows/golangci-lint.yml | 30 +- .github/workflows/releases.yml | 4 +- go.mod | 92 ++-- go.sum | 624 ++++++---------------------- 6 files changed, 200 insertions(+), 577 deletions(-) diff --git a/.github/workflows/api-specs-pr.yml b/.github/workflows/api-specs-pr.yml index 442f4637..647ead53 100644 --- a/.github/workflows/api-specs-pr.yml +++ b/.github/workflows/api-specs-pr.yml @@ -2,19 +2,21 @@ name: Scheduled API Specs Pull Request on: schedule: - cron: '0 */12 * * *' - jobs: api-specs-pr: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: 1.19 - - run: | - git config --global user.name "algolia-ci" - git config --global user.email "noreply@algolia.com" - - run: make api-specs-pr + - name: Install Go-task + uses: arduino/setup-task@v2 + with: + version: v3.x + repo-token: ${{ secrets.GH_TOKEN }} + - name: Update Search API spec + run: task api-specs-pr env: - GH_TOKEN: ${{ secrets.GH_SECRET }} \ No newline at end of file + GH_TOKEN: ${{ secrets.GH_SECRET }} diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9c40c6f8..6e2b0f46 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,24 +1,19 @@ name: Go - on: push: branches: [main] pull_request: branches: [main] - jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: 1.19 - - name: Build run: go build -v ./... - - name: Test run: go test -v ./... diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 20de0822..4812dbba 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -14,31 +14,11 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: - go-version: 1.18 - - uses: actions/checkout@v3 + go-version: 1.19 + - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.47.3 - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the all caching functionality will be complete disabled, - # takes precedence over all other caching options. - # skip-cache: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true \ No newline at end of file + version: v1.63.4 diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 2745a68b..f946a8fd 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -30,9 +30,9 @@ jobs: env: CHOCOLATEY_VERSION: 1.2.0 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2.8.0 + uses: goreleaser/goreleaser-action@v6 with: - version: "~1.13.1" + version: "~> v2" args: release env: GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }} diff --git a/go.mod b/go.mod index 549a4987..d6d1aa21 100644 --- a/go.mod +++ b/go.mod @@ -1,67 +1,79 @@ module github.com/algolia/cli -go 1.19 +go 1.23 + +toolchain go1.23.4 require ( - github.com/AlecAivazis/survey/v2 v2.3.5 - github.com/BurntSushi/toml v1.2.0 + github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/BurntSushi/toml v1.4.0 github.com/MakeNowJust/heredoc v1.0.0 - github.com/algolia/algoliasearch-client-go/v3 v3.30.0 + github.com/algolia/algoliasearch-client-go/v4 v4.12.1 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 - github.com/briandowns/spinner v1.19.0 - github.com/cli/safeexec v1.0.0 - github.com/dustin/go-humanize v1.0.0 + github.com/briandowns/spinner v1.23.2 + github.com/cli/safeexec v1.0.1 + github.com/dustin/go-humanize v1.0.1 github.com/getkin/kin-openapi v0.100.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/hashicorp/go-version v1.6.0 - github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.16 + github.com/hashicorp/go-version v1.7.0 + github.com/mattn/go-colorable v0.1.14 + github.com/mattn/go-isatty v0.0.20 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/reflow v0.3.0 - github.com/muesli/termenv v0.12.0 - github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.5.0 - github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.13.0 - github.com/stretchr/testify v1.8.0 + github.com/muesli/termenv v0.15.2 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.8.1 + github.com/spf13/pflag v1.0.6 + github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.10.0 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c - golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 + golang.org/x/term v0.29.0 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/client-go v0.25.0 + k8s.io/client-go v0.32.1 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.24.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.3.4 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect - github.com/segmentio/backo-go v1.0.1 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect - golang.org/x/text v0.3.8 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/segmentio/backo-go v1.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index e8916e06..b49afe0e 100644 --- a/go.sum +++ b/go.sum @@ -1,207 +1,103 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= -github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/algolia/algoliasearch-client-go/v3 v3.30.0 h1:sZvynLYzLrDok2uqQoN3JmUkRUbs4HvLmdf1uvGFXL8= -github.com/algolia/algoliasearch-client-go/v3 v3.30.0/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/algolia/algoliasearch-client-go/v4 v4.12.0 h1:YbLMyYZ7ohBTCEBIl3frF2Ga92ulGFev1tGe8SopF7Y= +github.com/algolia/algoliasearch-client-go/v4 v4.12.0/go.mod h1:UsYoWx3gl5nyWalsOI85b7NGW4t1uaN05vydu9aPfGM= +github.com/algolia/algoliasearch-client-go/v4 v4.12.1 h1:eBQ88S1byCry69iB/PqWyps8kUSfrPtTBoWFo44OnyM= +github.com/algolia/algoliasearch-client-go/v4 v4.12.1/go.mod h1:UsYoWx3gl5nyWalsOI85b7NGW4t1uaN05vydu9aPfGM= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E= -github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= -github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= +github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= +github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= +github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/getkin/kin-openapi v0.100.0 h1:8L9xNFNJFDqIRjZwwFjWhTTmTAxPRn/BVTzPn+hOA2s= github.com/getkin/kin-openapi v0.100.0/go.mod h1:w4lRPHiyOdwGbOkLIyk+P0qCwlu7TXPCHD/64nSXzgE= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -211,380 +107,118 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= -github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N++y4= -github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/segmentio/backo-go v1.1.0 h1:cJIfHQUdmLsd8t9IXqf5J8SdrOMn9vMa7cIvOavHAhc= +github.com/segmentio/backo-go v1.1.0/go.mod h1:ckenwdf+v/qbyhVdNPWHnqh2YdJBED1O9cidYyM5J18= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= -github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= +golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34= +golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= From 5ec691a76d406a77ada9662f4e3cdd03b57aeb0f Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 29 Jan 2025 18:39:40 +0100 Subject: [PATCH 02/45] feat: add devbox and task Adds devbox for easy environment management. In combination with `direnv`, the env is started automatically. Without `direnv`, just do `devbox shell`. Running Make as a task runner is phony. A Taskfile is better suited for this. --- .envrc | 9 + .gitignore | 2 +- .golangci.yml | 5 + Taskfile.yml | 114 ++++++ api/crawler/client.go | 23 +- api/insights/client.go | 108 ++--- devbox.json | 11 + devbox.lock | 417 ++++++++++++++++++++ internal/docs/yaml.go | 4 +- pkg/auth/auth_check.go | 56 +-- pkg/auth/auth_check_test.go | 9 +- pkg/cmd/dictionary/entries/import/import.go | 40 +- pkg/cmd/factory/default.go | 33 +- pkg/cmd/indices/config/config.go | 2 +- pkg/cmd/indices/config/export/export.go | 26 +- pkg/cmd/indices/list/list.go | 53 ++- pkg/cmd/objects/import/import.go | 2 +- pkg/cmd/objects/import/import_test.go | 2 +- pkg/cmd/objects/operations/operations.go | 31 +- pkg/cmd/objects/update/update.go | 35 +- pkg/cmd/open/open.go | 49 +-- pkg/cmd/root/root_help.go | 2 +- pkg/cmd/rules/browse/browse.go | 44 ++- pkg/cmd/rules/import/import.go | 73 ++-- pkg/cmd/rules/import/import_test.go | 2 +- pkg/cmd/shared/config/config.go | 24 +- pkg/cmd/shared/handler/indices/config.go | 40 +- pkg/cmd/shared/handler/synonyms/synonyms.go | 4 +- pkg/cmd/synonyms/browse/browse.go | 40 +- pkg/cmd/synonyms/import/import.go | 2 +- pkg/cmd/synonyms/import/import_test.go | 46 ++- pkg/cmd/synonyms/save/messages.go | 28 +- pkg/cmd/synonyms/save/messages_test.go | 18 +- pkg/cmd/synonyms/save/save.go | 65 +-- pkg/cmdutil/file_input.go | 5 +- pkg/cmdutil/jsonpath_flags.go | 15 +- pkg/config/profile.go | 6 +- pkg/iostreams/iostreams.go | 12 +- pkg/telemetry/telemetry.go | 6 +- pkg/telemetry/telemetry_test.go | 2 +- pkg/text/truncate.go | 8 + pkg/validators/cmd.go | 9 +- 42 files changed, 1170 insertions(+), 312 deletions(-) create mode 100644 .envrc create mode 100644 .golangci.yml create mode 100644 Taskfile.yml create mode 100644 devbox.json create mode 100644 devbox.lock diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..2f05af98 --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +#!/bin/bash + +# Automatically sets up your devbox environment whenever you cd into this +# directory via our direnv integration: + +eval "$(devbox generate direnv --print-envrc)" + +# check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/ +# for more details diff --git a/.gitignore b/.gitignore index 7f7bde97..3bca6d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ vendor/ # local build -algolia \ No newline at end of file +algolia diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..6beda9ee --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,5 @@ +linters: + enable: + - gosec + - gofumpt + - stylecheck diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 00000000..4aea624c --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,114 @@ +version: 3 +output: prefixed +vars: + # The URL to the "old" Algolia docs repo + docs_remote: https://github.com/algolia/doc.git + # The temporary folder for the generated YML files + docs_local: docs + cli_ref_path: app_data/cli/commands + yml_folder: tmp +tasks: + build: + desc: Build the binary + deps: [generate] + cmd: go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version={{ .VERSION }}" -o algolia cmd/algolia/main.go + vars: + VERSION: main + test: + desc: Run unit tests + run: always + cmd: go test ./... + lint: + desc: Lint code + cmd: golangci-lint run + format: + desc: Format code + cmds: + - gofumpt -w pkg cmd test internal api + - golines -w pkg cmd test internal api + ci: + desc: Run everything + aliases: + - default + deps: + - build + - test + - lint + api-specs-pr: + desc: Create a new PR or update existing one for updating the Search API spec + summary: | + This task downloads the latest Search API OpenAPI spec from the api-clients-automation repo, + generates the flags, and makes a new PR to the CLI GitHub repo. + deps: [download-spec-file, generate] + preconditions: + - git status --porcelain + cmds: + - | + original="$(git branch --show-current)" + git checkout -B {{ .branch }} + git add . + git commit --message "chore: update search api spec" + git push --force --set-upstream origin {{ .branch }} + gh pr list --base main --head {{ .branch }} | grep -q . || gh pr create --title '{{ .pr-title }}' --description '{{ .pr-description }}' + git switch "${original}" + vars: + branch: feat/api-specs + pr-title: "chore: Update Search API spec" + pr-description: "Update Search API spec" + env: + GIT_COMMITTER_NAME: algolia-ci + GIT_AUTHOR_NAME: algolia-ci + GIT_COMMITTER_EMAIL: noreply@algolia.com + GIT_AUTHOR_EMAIL: noreply@algolia.com + download-spec-file: + desc: Download the latest Search API spec from GitHub + internal: true + cmd: curl -fsSL -o {{ .destination }} {{ .source }} + vars: + source: https://raw.githubusercontent.com/algolia/api-clients-automation/main/specs/bundled/search.yml + destination: ./api/specs/search.yml + generate: + desc: Generate command flags + internal: true + cmds: + - go generate ./... + update-docs: + desc: Update the CLI command reference in the Algolia docs + deps: + - clone-docs + - generate-command-reference + cmds: + - task: update-command-reference + - task: cleanup + clone-docs: + desc: Clone the Algolia docs + internal: true + cmd: git clone --depth=1 {{ .docs_remote }} {{ .docs_local }} + generate-command-reference: + desc: Generate updated YML files for the CLI command reference + internal: true + cmd: go run ./cmd/docs --app_data-path {{ .yml_folder }} + update-command-reference: + desc: Add the updated YML files to the docs + summary: | + This task clones the Algolia docs repo, + adds the updated CLI reference yml files to it, + and pushes a new PR to the GitHub repo. + internal: true + cmds: + - | + git -C {{ .docs_local }} checkout -B chore/cli-$(git rev-parse --short HEAD) + git -C {{ .docs_local }} rm "{{ .cli_ref_path }}/*.yml" + mkdir -p {{ .docs_local }}/{{ .cli_ref_path }} + mv {{ .yml_folder }}/*.yml {{ .docs_local }}/{{ .cli_ref_path }}/ + git -C {{ .docs_local }} add "{{ .cli_ref_path }}/*.yml" + git -C {{ .docs_local }} commit --message 'chore: Update CLI command reference' + env: + GIT_COMMITTER_NAME: algolia-ci + GIT_AUTHOR_NAME: algolia-ci + GIT_COMMITTER_EMAIL: noreply@algolia.com + GIT_AUTHOR_EMAIL: noreply@algolia.com + cleanup: + desc: Cleanup the docs files + internal: true + cmd: rm -rf {{ .docs_local }} {{ .yml_folder }} || true diff --git a/api/crawler/client.go b/api/crawler/client.go index 8ac9a101..44924c54 100644 --- a/api/crawler/client.go +++ b/api/crawler/client.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" ) @@ -43,7 +42,13 @@ func NewClientWithHTTPClient(userID, apiKey string, client *http.Client) *Client // Request sends an HTTP request and returns an HTTP response. // It unmarshals the response body to the given interface. -func (c *Client) request(res interface{}, method string, path string, body interface{}, urlParams map[string]string) error { +func (c *Client) request( + res interface{}, + method string, + path string, + body interface{}, + urlParams map[string]string, +) error { r, err := c.buildRequest(method, path, body, urlParams) if err != nil { return err @@ -99,14 +104,18 @@ func (c *Client) buildRequestWithBody(method, url string, body interface{}) (*ht return nil, err } - r = ioutil.NopCloser(bytes.NewReader(b)) + r = io.NopCloser(bytes.NewReader(b)) } return http.NewRequest(method, url, r) } // buildRequest builds an HTTP request. -func (c *Client) buildRequest(method, path string, body interface{}, urlParams map[string]string) (req *http.Request, err error) { +func (c *Client) buildRequest( + method, path string, + body interface{}, + urlParams map[string]string, +) (req *http.Request, err error) { url := DefaultBaseURL + path if body == nil { @@ -275,7 +284,11 @@ func (c *Client) Stats(crawlerID string) (*StatsResponse, error) { // CrawlURLs crawls the specified URLs on the specified Crawler. // It returns the Task ID if successful. -func (c *Client) CrawlURLs(crawlerID string, URLs []string, save, saveSpecified bool) (string, error) { +func (c *Client) CrawlURLs( + crawlerID string, + URLs []string, + save, saveSpecified bool, +) (string, error) { var res TaskIDResponse path := fmt.Sprintf("crawlers/%s/urls/crawl", crawlerID) diff --git a/api/insights/client.go b/api/insights/client.go index 1b7446b8..f9833ffa 100644 --- a/api/insights/client.go +++ b/api/insights/client.go @@ -1,66 +1,80 @@ package insights import ( + "encoding/json" "fmt" - "net/http" "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/call" - "github.com/algolia/algoliasearch-client-go/v3/algolia/compression" - _insights "github.com/algolia/algoliasearch-client-go/v3/algolia/insights" - "github.com/algolia/algoliasearch-client-go/v3/algolia/transport" + algoliaInsights "github.com/algolia/algoliasearch-client-go/v4/algolia/insights" + "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" + "github.com/algolia/cli/pkg/version" ) -// Client provides methods to interact with the Algolia Insights API. +// Client wraps the default Insights API client so that we can declare methods on it type Client struct { - appID string - transport *transport.Transport + *algoliaInsights.APIClient } -// NewClient instantiates a new client able to interact with the Algolia -// Insights API. -func NewClient(appID, apiKey string) *Client { - return NewClientWithConfig( - _insights.Configuration{ - AppID: appID, - APIKey: apiKey, +// NewClient instantiates a new Insights API client +func NewClient(appID, apiKey string, region algoliaInsights.Region) (*Client, error) { + // Get the default user agent + userAgent, err := getUserAgentInfo(appID, apiKey, region, version.Version) + if err != nil { + return nil, err + } + if userAgent == "" { + return nil, fmt.Errorf("user agent info must not be empty") + } + clientConfig := algoliaInsights.InsightsConfiguration{ + Configuration: transport.Configuration{ + AppID: appID, + ApiKey: apiKey, + UserAgent: userAgent, }, - ) + } + client, err := algoliaInsights.NewClientWithConfig(clientConfig) + if err != nil { + return nil, err + } + return &Client{client}, nil } -// NewClientWithConfig instantiates a new client able to interact with the -// Algolia Insights API. -func NewClientWithConfig(config _insights.Configuration) *Client { - var hosts []*transport.StatefulHost - - if config.Hosts == nil { - hosts = defaultHosts(config.Region) - } else { - for _, h := range config.Hosts { - hosts = append(hosts, transport.NewStatefulHost(h, call.IsReadWrite)) - } +// GetEvents retrieves a number of events from the Algolia Insights API. +func (c *Client) GetEvents(startDate, endDate time.Time, limit int) (*EventsRes, error) { + layout := "2006-01-02T15:04:05.000Z" + params := map[string]any{ + "startDate": startDate.Format(layout), + "endDate": endDate.Format(layout), + "limit": limit, } - - return &Client{ - appID: config.AppID, - transport: transport.New( - hosts, - config.Requester, - config.AppID, - config.APIKey, - config.ReadTimeout, - config.WriteTimeout, - config.Headers, - config.ExtraUserAgent, - compression.None, - ), + res, err := c.CustomGet(c.NewApiCustomGetRequest("1/events").WithParameters(params)) + if err != nil { + return nil, err } + tmp, err := json.Marshal(res) + if err != nil { + return nil, err + } + var eventsRes EventsRes + err = json.Unmarshal(tmp, &eventsRes) + if err != nil { + return nil, err + } + + return &eventsRes, err } -// FetchEvents retrieves events from the Algolia Insights API. -func (c *Client) FetchEvents(startDate, endDate time.Time, limit int) (EventsRes, error) { - var res EventsRes - path := fmt.Sprintf("/1/events?startDate=%s&endDate=%s&limit=%d", startDate.Format("2006-01-02T15:04:05.000Z"), endDate.Format("2006-01-02T15:04:05.000Z"), limit) - err := c.transport.Request(&res, http.MethodGet, path, nil, call.Read, nil) - return res, err +// getUserAgentInfo returns the user agent string for the Insights client in the CLI +func getUserAgentInfo( + appID string, + apiKey string, + region algoliaInsights.Region, + appVersion string, +) (string, error) { + client, err := algoliaInsights.NewClient(appID, apiKey, region) + if err != nil { + return "", err + } + + return client.GetConfiguration().UserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), nil } diff --git a/devbox.json b/devbox.json new file mode 100644 index 00000000..0d4bde6d --- /dev/null +++ b/devbox.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json", + "packages": [ + "go-task@latest", + "go@1.23.4", + "golangci-lint@1.63.4", + "gofumpt@latest", + "golines@latest", + "gh@latest" + ] +} diff --git a/devbox.lock b/devbox.lock new file mode 100644 index 00000000..a45b4a51 --- /dev/null +++ b/devbox.lock @@ -0,0 +1,417 @@ +{ + "lockfile_version": "1", + "packages": { + "curl@latest": { + "last_modified": "2025-02-05T14:25:15Z", + "resolved": "github:NixOS/nixpkgs/ccfae3057498f5a740be4c5a13aa800813a13084#curl", + "source": "devbox-search", + "version": "8.11.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/4wb2gw5y4d1s9lmll744k7mdkgza1mz1-curl-8.11.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/02mrnrg0mqps1x6sqaw5dp79gcr010rh-curl-8.11.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/bqkra3fwg9jn301gi14gcizpyrmxqb20-curl-8.11.1" + }, + { + "name": "dev", + "path": "/nix/store/2qfdjp13bid9g5axp74q1sas73wk4kmv-curl-8.11.1-dev" + }, + { + "name": "devdoc", + "path": "/nix/store/l88q3phl5gvv848slpzp9j5kl3gb7qi2-curl-8.11.1-devdoc" + } + ], + "store_path": "/nix/store/4wb2gw5y4d1s9lmll744k7mdkgza1mz1-curl-8.11.1-bin" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/b4bfdh987ankyl4dl8hyqka0icbm7ch5-curl-8.11.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/cz541skcfa3sv96d7fgfl72jln28ai55-curl-8.11.1-man", + "default": true + }, + { + "name": "debug", + "path": "/nix/store/3nxwcrw2nz4psxfa298gz2rvxcxkgwrj-curl-8.11.1-debug" + }, + { + "name": "dev", + "path": "/nix/store/hmrxq7fax0jb25f0p6v77c9idl369wf3-curl-8.11.1-dev" + }, + { + "name": "devdoc", + "path": "/nix/store/xhyfvy56bpwk2xg0sjy0a72p86dbzzny-curl-8.11.1-devdoc" + }, + { + "name": "out", + "path": "/nix/store/5nr2v31bm8zdxky9jci5m7kjqc5dqjvz-curl-8.11.1" + } + ], + "store_path": "/nix/store/b4bfdh987ankyl4dl8hyqka0icbm7ch5-curl-8.11.1-bin" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/778ny8fq90spak0p7gwlfa78dxqj4x0s-curl-8.11.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/w8wfvgpmpiyvmyn94x34bs2sl7w66iqk-curl-8.11.1-man", + "default": true + }, + { + "name": "dev", + "path": "/nix/store/j43v0gxd8n29mlm9v79fnwcnshc73kyg-curl-8.11.1-dev" + }, + { + "name": "devdoc", + "path": "/nix/store/mq7bgy6r8bjjj450b63p1l68k1pmr7lp-curl-8.11.1-devdoc" + }, + { + "name": "out", + "path": "/nix/store/05i854l3qagyd129f1fzg5ybz5fbs8lb-curl-8.11.1" + } + ], + "store_path": "/nix/store/778ny8fq90spak0p7gwlfa78dxqj4x0s-curl-8.11.1-bin" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "bin", + "path": "/nix/store/wzlmn9jq15pbw61nmqcy5nnwv6dhpq09-curl-8.11.1-bin", + "default": true + }, + { + "name": "man", + "path": "/nix/store/qabwv9rmysj9q6pxv9f85zavmw26y9dx-curl-8.11.1-man", + "default": true + }, + { + "name": "out", + "path": "/nix/store/gj70mjxd5zy310wq81znzr73rx6badsl-curl-8.11.1" + }, + { + "name": "debug", + "path": "/nix/store/nidhaw2lykjd4svj0xhh0a7p492r3n05-curl-8.11.1-debug" + }, + { + "name": "dev", + "path": "/nix/store/azfhp07yysbxvdhm78n43cjiwh1lb111-curl-8.11.1-dev" + }, + { + "name": "devdoc", + "path": "/nix/store/fb5dprl6p0rk1hv76wrg695yh2dzhrhv-curl-8.11.1-devdoc" + } + ], + "store_path": "/nix/store/wzlmn9jq15pbw61nmqcy5nnwv6dhpq09-curl-8.11.1-bin" + } + } + }, + "gh@latest": { + "last_modified": "2025-02-01T15:12:02Z", + "resolved": "github:NixOS/nixpkgs/102a39bfee444533e6b4e8611d7e92aa39b7bec1#gh", + "source": "devbox-search", + "version": "2.66.1", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/dvy3rk4jxx10ll5mhh0g8a260l2m1c1c-gh-2.66.1", + "default": true + } + ], + "store_path": "/nix/store/dvy3rk4jxx10ll5mhh0g8a260l2m1c1c-gh-2.66.1" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/k6dk8k0wi06k8wrphrkq8pypcql2jx9w-gh-2.66.1", + "default": true + } + ], + "store_path": "/nix/store/k6dk8k0wi06k8wrphrkq8pypcql2jx9w-gh-2.66.1" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/yhlzsr96n9a5807r8glh89mj66dbk58z-gh-2.66.1", + "default": true + } + ], + "store_path": "/nix/store/yhlzsr96n9a5807r8glh89mj66dbk58z-gh-2.66.1" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/3laqs1ys46ryva9mcmk1q5yvn1k3xhyc-gh-2.66.1", + "default": true + } + ], + "store_path": "/nix/store/3laqs1ys46ryva9mcmk1q5yvn1k3xhyc-gh-2.66.1" + } + } + }, + "go-task@latest": { + "last_modified": "2025-01-19T08:16:51Z", + "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#go-task", + "source": "devbox-search", + "version": "3.41.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/v4vvksxrgyxdyhb1hvvap5sqp4ycl7xv-go-task-3.41.0", + "default": true + } + ], + "store_path": "/nix/store/v4vvksxrgyxdyhb1hvvap5sqp4ycl7xv-go-task-3.41.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/iq7vnz78z8ff1px700ra1j4iinfsvhjg-go-task-3.41.0", + "default": true + } + ], + "store_path": "/nix/store/iq7vnz78z8ff1px700ra1j4iinfsvhjg-go-task-3.41.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/bf1zwbad0cqwhpcjylynpx18c8ylm6nx-go-task-3.41.0", + "default": true + } + ], + "store_path": "/nix/store/bf1zwbad0cqwhpcjylynpx18c8ylm6nx-go-task-3.41.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/cbz45j0wbsp929rd8381ifbiqxy7jp71-go-task-3.41.0", + "default": true + } + ], + "store_path": "/nix/store/cbz45j0wbsp929rd8381ifbiqxy7jp71-go-task-3.41.0" + } + } + }, + "go@1.23.4": { + "last_modified": "2025-01-19T08:16:51Z", + "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#go", + "source": "devbox-search", + "version": "1.23.4", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/lg7l1czmd5hqwwhjszprdz342q98zmkw-go-1.23.4", + "default": true + } + ], + "store_path": "/nix/store/lg7l1czmd5hqwwhjszprdz342q98zmkw-go-1.23.4" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2x7k6yj9z37s3d3x5j3kkz6bcak8k9qr-go-1.23.4", + "default": true + } + ], + "store_path": "/nix/store/2x7k6yj9z37s3d3x5j3kkz6bcak8k9qr-go-1.23.4" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/8x0ag0v37rfy9z58a8yy3hakj3dzdr9c-go-1.23.4", + "default": true + } + ], + "store_path": "/nix/store/8x0ag0v37rfy9z58a8yy3hakj3dzdr9c-go-1.23.4" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/0b6vsy4fa4i4qpk1011hi6251nwdg5y8-go-1.23.4", + "default": true + } + ], + "store_path": "/nix/store/0b6vsy4fa4i4qpk1011hi6251nwdg5y8-go-1.23.4" + } + } + }, + "gofumpt@latest": { + "last_modified": "2025-01-19T08:16:51Z", + "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#gofumpt", + "source": "devbox-search", + "version": "0.7.0", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/nvipsp6sdl4z4ayfz2csja78admp7hh3-gofumpt-0.7.0", + "default": true + } + ], + "store_path": "/nix/store/nvipsp6sdl4z4ayfz2csja78admp7hh3-gofumpt-0.7.0" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/5pmc8wbhd00z4s5qwipmhxnmldg7glzr-gofumpt-0.7.0", + "default": true + } + ], + "store_path": "/nix/store/5pmc8wbhd00z4s5qwipmhxnmldg7glzr-gofumpt-0.7.0" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/vg38xm3d1rp1qij1m5cc28b4yx3z0in4-gofumpt-0.7.0", + "default": true + } + ], + "store_path": "/nix/store/vg38xm3d1rp1qij1m5cc28b4yx3z0in4-gofumpt-0.7.0" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/yzmdkhg1iv08ffzwbxym5pxn3mjsn5x1-gofumpt-0.7.0", + "default": true + } + ], + "store_path": "/nix/store/yzmdkhg1iv08ffzwbxym5pxn3mjsn5x1-gofumpt-0.7.0" + } + } + }, + "golangci-lint@1.63.4": { + "last_modified": "2025-01-19T08:16:51Z", + "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#golangci-lint", + "source": "devbox-search", + "version": "1.63.4", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/p81vg4szd85xb1m5r17fn6h2cd433xv9-golangci-lint-1.63.4", + "default": true + } + ], + "store_path": "/nix/store/p81vg4szd85xb1m5r17fn6h2cd433xv9-golangci-lint-1.63.4" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/45k8x4b0ygqwpf7di7rcglxsz8ykxz9z-golangci-lint-1.63.4", + "default": true + } + ], + "store_path": "/nix/store/45k8x4b0ygqwpf7di7rcglxsz8ykxz9z-golangci-lint-1.63.4" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/2xslqh1iyjbhwyynxw09ygfaxzfhmakp-golangci-lint-1.63.4", + "default": true + } + ], + "store_path": "/nix/store/2xslqh1iyjbhwyynxw09ygfaxzfhmakp-golangci-lint-1.63.4" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/71a493qsawf2gsh3s31zsbrw5pg8psmc-golangci-lint-1.63.4", + "default": true + } + ], + "store_path": "/nix/store/71a493qsawf2gsh3s31zsbrw5pg8psmc-golangci-lint-1.63.4" + } + } + }, + "golines@latest": { + "last_modified": "2025-01-19T08:16:51Z", + "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#golines", + "source": "devbox-search", + "version": "0.12.2", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/j7f5yj2q07vw1jzgn4gqlaz74dg8c2rj-golines-0.12.2", + "default": true + } + ], + "store_path": "/nix/store/j7f5yj2q07vw1jzgn4gqlaz74dg8c2rj-golines-0.12.2" + }, + "aarch64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/s2r29b8wxa98lv8334jd1q24nq6ipaix-golines-0.12.2", + "default": true + } + ], + "store_path": "/nix/store/s2r29b8wxa98lv8334jd1q24nq6ipaix-golines-0.12.2" + }, + "x86_64-darwin": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/33ybn8q0ry3wnrwczd8zwnz37l9p83zn-golines-0.12.2", + "default": true + } + ], + "store_path": "/nix/store/33ybn8q0ry3wnrwczd8zwnz37l9p83zn-golines-0.12.2" + }, + "x86_64-linux": { + "outputs": [ + { + "name": "out", + "path": "/nix/store/46r6pzh2k9a9fqsmfswgbwwly42h6q5w-golines-0.12.2", + "default": true + } + ], + "store_path": "/nix/store/46r6pzh2k9a9fqsmfswgbwwly42h6q5w-golines-0.12.2" + } + } + } + } +} diff --git a/internal/docs/yaml.go b/internal/docs/yaml.go index 7e0c86a4..c58a00a3 100644 --- a/internal/docs/yaml.go +++ b/internal/docs/yaml.go @@ -14,8 +14,8 @@ func GenYamlTree(cmd *cobra.Command, dir string) error { commands := getCommands(cmd) for _, c := range commands { - command_path := strings.ReplaceAll(c.Name, " ", "_") - filename := filepath.Join(dir, fmt.Sprintf("%s.yml", command_path)) + commandPath := strings.ReplaceAll(c.Name, " ", "_") + filename := filepath.Join(dir, fmt.Sprintf("%s.yml", commandPath)) f, err := os.Create(filename) if err != nil { return err diff --git a/pkg/auth/auth_check.go b/pkg/auth/auth_check.go index defd6da4..5186eec9 100644 --- a/pkg/auth/auth_check.go +++ b/pkg/auth/auth_check.go @@ -12,33 +12,34 @@ import ( "github.com/algolia/cli/pkg/utils" ) -var ( - WriteAPIKeyDefaultACLs = []string{ - "search", - "browse", - "seeUnretrievableAttributes", - "listIndexes", - "analytics", - "logs", - "addObject", - "deleteObject", - "deleteIndex", - "settings", - "editSettings", - "recommendation", - } -) +var WriteAPIKeyDefaultACLs = []string{ + "search", + "browse", + "seeUnretrievableAttributes", + "listIndexes", + "analytics", + "logs", + "addObject", + "deleteObject", + "deleteIndex", + "settings", + "editSettings", + "recommendation", +} // errMissingACLs return an error with the missing ACLs func errMissingACLs(missing []string) error { - err := fmt.Sprintf("Missing API Key ACL(s): %s\n", strings.Join(missing, ", ")) - err += "Either edit your profile or use the `--api-key` flag to provide an API Key with the missing ACLs.\n" - err += "See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information.\n" + err := fmt.Sprintf("Missing API key ACL(s): %s\n", strings.Join(missing, ", ")) + err += "Edit your profile or use the `--api-key` flag to provide an API key with the missing ACLs.\n" + err += "See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information" + return errors.New(err) } // errAdminAPIKeyRequired is returned when the command requires an admin API Key -var errAdminAPIKeyRequired = errors.New("This command requires an admin API Key. Please use the `--api-key` flag to provide a valid admin API Key.\n") +var errAdminAPIKeyRequired = errors.New( + "this command requires an admin API key. Use the `--api-key` flag with a valid admin API key", +) func DisableAuthCheck(cmd *cobra.Command) { if cmd.Annotations == nil { @@ -81,7 +82,7 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { if err != nil { return err } - _, err = client.ListAPIKeys() + _, err = client.ListApiKeys() if err == nil { return nil // Admin API Key, no need to check ACLs } @@ -92,12 +93,21 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { } // Check the ACLs of the provided API Key - apiKey, err := client.GetAPIKey(f.Config.Profile().GetAPIKey()) + key, err := f.Config.Profile().GetAPIKey() if err != nil { return err } + apiKey, err := client.GetApiKey(client.NewApiGetApiKeyRequest(key)) + if err != nil { + return err + } + + var hasAcls []string + for _, acl := range apiKey.Acl { + hasAcls = append(hasAcls, string(acl)) + } - missingACLs := utils.Differences(neededACLs, apiKey.ACL) + missingACLs := utils.Differences(neededACLs, hasAcls) if len(missingACLs) > 0 { return errMissingACLs(missingACLs) } diff --git a/pkg/auth/auth_check_test.go b/pkg/auth/auth_check_test.go index b3887fbe..60cc790a 100644 --- a/pkg/auth/auth_check_test.go +++ b/pkg/auth/auth_check_test.go @@ -39,7 +39,7 @@ func Test_CheckACLs(t *testing.T) { adminKey: false, ACLs: []string{}, wantErr: true, - wantErrMessage: "This command requires an admin API Key. Please use the `--api-key` flag to provide a valid admin API Key.\n", + wantErrMessage: "this command requires an admin API key. Use the `--api-key` flag with a valid admin API key", }, { name: "need admin key, admin key", @@ -63,10 +63,9 @@ func Test_CheckACLs(t *testing.T) { adminKey: false, ACLs: []string{}, wantErr: true, - wantErrMessage: `Missing API Key ACL(s): search -Either edit your profile or use the ` + "`--api-key`" + ` flag to provide an API Key with the missing ACLs. -See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information. -`, + wantErrMessage: `Missing API key ACL(s): search +Edit your profile or use the ` + "`--api-key`" + ` flag to provide an API key with the missing ACLs. +See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information`, }, { name: "need ACLs, has ACLs", diff --git a/pkg/cmd/dictionary/entries/import/import.go b/pkg/cmd/dictionary/entries/import/import.go index 84a0b9d8..58b98fab 100644 --- a/pkg/cmd/dictionary/entries/import/import.go +++ b/pkg/cmd/dictionary/entries/import/import.go @@ -83,11 +83,14 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co }, } - cmd.Flags().StringVarP(&opts.File, "file", "F", "", "Read entries to import from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&opts.File, "file", "F", "", "Read entries to import from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the operation to complete before returning") - cmd.Flags().BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue importing entries even if some entries are invalid.") + cmd.Flags(). + BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the operation to complete before returning") + cmd.Flags(). + BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue importing entries even if some entries are invalid.") return cmd } @@ -155,7 +158,7 @@ func runImportCmd(opts *ImportOptions) error { // No entries found if len(entries) == 0 { if len(errors) > 0 { - return fmt.Errorf(errorMsg) + return fmt.Errorf("%s", errorMsg) } return fmt.Errorf("%s No entries found in the file", cs.FailureIcon()) } @@ -177,7 +180,13 @@ func runImportCmd(opts *ImportOptions) error { } // Import entries - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Updating %s entries on %s", cs.Bold(fmt.Sprint(len(entries))), cs.Bold(opts.DictionaryName))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf( + "Updating %s entries on %s", + cs.Bold(fmt.Sprint(len(entries))), + cs.Bold(opts.DictionaryName), + ), + ) res, err := client.SaveDictionaryEntries(search.DictionaryName(opts.DictionaryName), entries) if err != nil { @@ -195,7 +204,14 @@ func runImportCmd(opts *ImportOptions) error { } opts.IO.StopProgressIndicator() - _, err = fmt.Fprintf(opts.IO.Out, "%s Successfully imported %s entries on %s in %v\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(len(entries))), cs.Bold(opts.DictionaryName), time.Since(elapsed)) + _, err = fmt.Fprintf( + opts.IO.Out, + "%s Successfully imported %s entries on %s in %v\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(len(entries))), + cs.Bold(opts.DictionaryName), + time.Since(elapsed), + ) return err } @@ -213,12 +229,20 @@ func ValidateDictionaryEntry(entry shared.DictionaryEntry, currentLine int) erro return nil } -func createDictionaryEntry(dictionaryName string, entry shared.DictionaryEntry) (search.DictionaryEntry, error) { +func createDictionaryEntry( + dictionaryName string, + entry shared.DictionaryEntry, +) (search.DictionaryEntry, error) { switch dictionaryName { case string(search.Plurals): return search.NewPlural(entry.ObjectID, entry.Language, entry.Words), nil case string(search.Compounds): - return search.NewCompound(entry.ObjectID, entry.Language, entry.Word, entry.Decomposition), nil + return search.NewCompound( + entry.ObjectID, + entry.Language, + entry.Word, + entry.Decomposition, + ), nil case string(search.Stopwords): return search.NewStopword(entry.ObjectID, entry.Language, entry.Word, entry.State), nil } diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index 011f0b67..dbc00642 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/cmdutil" @@ -18,12 +20,13 @@ func New(appVersion string, cfg config.IConfig) *cmdutil.Factory { } f.IOStreams = ioStreams(f) f.SearchClient = searchClient(f, appVersion) + f.V4SearchClient = v4searchClient(f, appVersion) f.CrawlerClient = crawlerClient(f) return f } -func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams { +func ioStreams(_ *cmdutil.Factory) *iostreams.IOStreams { io := iostreams.System() return io } @@ -49,6 +52,34 @@ func searchClient(f *cmdutil.Factory, appVersion string) func() (*search.Client, } } +func v4searchClient(f *cmdutil.Factory, appVersion string) func() (*v4.APIClient, error) { + return func() (*v4.APIClient, error) { + appID, err := f.Config.Profile().GetApplicationID() + if err != nil { + return nil, err + } + apiKey, err := f.Config.Profile().GetAPIKey() + if err != nil { + return nil, err + } + + defaultClient, _ := v4.NewClient(appID, apiKey) + defaultUserAgent := defaultClient.GetConfiguration().UserAgent + + // TODO: Doesn't support custom `search_hosts` yet. + // To support it, it's best to transform the GetSearchHosts() function + clientConf := v4.SearchConfiguration{ + Configuration: transport.Configuration{ + AppID: appID, + ApiKey: apiKey, + UserAgent: defaultUserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), + }, + } + + return v4.NewClientWithConfig(clientConf) + } +} + func crawlerClient(f *cmdutil.Factory) func() (*crawler.Client, error) { return func() (*crawler.Client, error) { userID, err := f.Config.Profile().GetCrawlerUserID() diff --git a/pkg/cmd/indices/config/config.go b/pkg/cmd/indices/config/config.go index bf906275..a733fcee 100644 --- a/pkg/cmd/indices/config/config.go +++ b/pkg/cmd/indices/config/config.go @@ -8,7 +8,7 @@ import ( "github.com/algolia/cli/pkg/cmdutil" ) -// NewConfigCmd returns a new command for indice config management +// NewConfigCmd returns a new command for index config management func NewConfigCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "config", diff --git a/pkg/cmd/indices/config/export/export.go b/pkg/cmd/indices/config/export/export.go index 2960ba15..9416f9c1 100644 --- a/pkg/cmd/indices/config/export/export.go +++ b/pkg/cmd/indices/config/export/export.go @@ -8,7 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" - indiceConfig "github.com/algolia/cli/pkg/cmd/shared/config" + indexConfig "github.com/algolia/cli/pkg/cmd/shared/config" "github.com/algolia/cli/pkg/cmd/shared/handler" config "github.com/algolia/cli/pkg/cmd/shared/handler/indices" "github.com/algolia/cli/pkg/cmdutil" @@ -75,9 +75,11 @@ func NewExportCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().StringVarP(&opts.Directory, "directory", "d", "", "Directory path of the output file (default: current folder)") + cmd.Flags(). + StringVarP(&opts.Directory, "directory", "d", "", "Directory path of the output file (default: current folder)") _ = cmd.MarkFlagDirname("directory") - cmd.Flags().StringSliceVarP(&opts.Scope, "scope", "s", []string{"settings", "synonyms", "rules"}, "Scope to export (default: all)") + cmd.Flags(). + StringSliceVarP(&opts.Scope, "scope", "s", []string{"settings", "synonyms", "rules"}, "Scope to export (default: all)") _ = cmd.RegisterFlagCompletionFunc("scope", cmdutil.StringSliceCompletionFunc(map[string]string{ "settings": "settings", @@ -96,14 +98,18 @@ func runExportCmd(opts *config.ExportOptions) error { } indice := client.InitIndex(opts.Indice) - configJson, err := indiceConfig.GetIndiceConfig(indice, opts.Scope, cs) + configJson, err := indexConfig.GetIndiceConfig(indice, opts.Scope, cs) if err != nil { return err } configJsonIndented, err := json.MarshalIndent(configJson, "", " ") if err != nil { - return fmt.Errorf("%s An error occurred when creating the config json: %w", cs.FailureIcon(), err) + return fmt.Errorf( + "%s An error occurred when creating the config json: %w", + cs.FailureIcon(), + err, + ) } filePath := config.GetConfigFileName(opts.Directory, opts.Indice, indice.GetAppID()) @@ -116,9 +122,13 @@ func runExportCmd(opts *config.ExportOptions) error { if opts.Directory != "" { rootPath = currentDir } - fmt.Printf("%s '%s' Index config (%s) successfully exported to %s\n", - cs.SuccessIcon(), opts.Indice, utils.SliceToReadableString(opts.Scope), fmt.Sprintf("%s/%s", rootPath, filePath)) + fmt.Printf( + "%s '%s' Index config (%s) successfully exported to %s\n", + cs.SuccessIcon(), + opts.Indice, + utils.SliceToReadableString(opts.Scope), + fmt.Sprintf("%s/%s", rootPath, filePath), + ) return nil - } diff --git a/pkg/cmd/indices/list/list.go b/pkg/cmd/indices/list/list.go index e7c6251f..423934e0 100644 --- a/pkg/cmd/indices/list/list.go +++ b/pkg/cmd/indices/list/list.go @@ -2,9 +2,11 @@ package list import ( "fmt" + "strconv" + "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/dustin/go-humanize" "github.com/spf13/cobra" @@ -19,7 +21,7 @@ type ListOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) PrintFlags *cmdutil.PrintFlags } @@ -61,7 +63,7 @@ func runListCmd(opts *ListOptions) error { } opts.IO.StartProgressIndicatorWithLabel("Fetching indices") - res, err := client.ListIndices() + res, err := client.ListIndices(client.NewApiListIndicesRequest()) opts.IO.StopProgressIndicator() if err != nil { return err @@ -94,15 +96,50 @@ func runListCmd(opts *ListOptions) error { } for _, index := range res.Items { + var primary string + if index.Primary == nil { + primary = "" + } else { + primary = *index.Primary + } + updatedAt, err := parseTime(index.UpdatedAt) + if err != nil { + return fmt.Errorf("can't parse %s into a time struct", index.UpdatedAt) + } + createdAt, err := parseTime(index.CreatedAt) + if err != nil { + return fmt.Errorf("can't parse %s into a time struct", index.CreatedAt) + } + // Prevent integer overflow + if index.DataSize < 0 { + index.DataSize = 0 + } table.AddField(index.Name, nil, nil) - table.AddField(humanize.Comma(index.Entries), nil, nil) + table.AddField(humanize.Comma(int64(index.Entries)), nil, nil) table.AddField(humanize.Bytes(uint64(index.DataSize)), nil, nil) - table.AddField(humanize.Time(index.UpdatedAt), nil, nil) - table.AddField(humanize.Time(index.CreatedAt), nil, nil) - table.AddField(index.LastBuildTime.String(), nil, nil) - table.AddField(index.Primary, nil, nil) + table.AddField(updatedAt, nil, nil) + table.AddField(createdAt, nil, nil) + table.AddField(strconv.Itoa(int(index.LastBuildTimeS))+"s", nil, nil) + table.AddField(primary, nil, nil) table.AddField(fmt.Sprintf("%v", index.Replicas), nil, nil) table.EndRow() } return table.Render() } + +// parseTime parses the string from the API response into a relative time string +func parseTime(timeAsString string) (string, error) { + const layout = "2006-01-02T15:04:05.999Z" + + // This *should* restore the previous behavior when UpdatedAt is empty + if timeAsString == "" { + return "a long while ago", nil + } + + t, err := time.Parse(layout, timeAsString) + if err != nil { + return "", err + } + + return humanize.Time(t), nil +} diff --git a/pkg/cmd/objects/import/import.go b/pkg/cmd/objects/import/import.go index 3fc2f555..bfea9192 100644 --- a/pkg/cmd/objects/import/import.go +++ b/pkg/cmd/objects/import/import.go @@ -1,4 +1,4 @@ -package importRecords +package importrecords import ( "bufio" diff --git a/pkg/cmd/objects/import/import_test.go b/pkg/cmd/objects/import/import_test.go index 87aa521b..a3655d68 100644 --- a/pkg/cmd/objects/import/import_test.go +++ b/pkg/cmd/objects/import/import_test.go @@ -1,4 +1,4 @@ -package importRecords +package importrecords import ( "fmt" diff --git a/pkg/cmd/objects/operations/operations.go b/pkg/cmd/objects/operations/operations.go index 60d9d834..a984a6ff 100644 --- a/pkg/cmd/objects/operations/operations.go +++ b/pkg/cmd/objects/operations/operations.go @@ -75,11 +75,14 @@ func NewOperationsCmd(f *cmdutil.Factory, runF func(*OperationsOptions) error) * }, } - cmd.Flags().StringVarP(&opts.File, "file", "F", "", "The file to read the indexing operations from (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&opts.File, "file", "F", "", "The file to read the indexing operations from (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the indexing operation(s) to complete before returning.") - cmd.Flags().BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue processing operations even if some operations are invalid.") + cmd.Flags(). + BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the indexing operation(s) to complete before returning.") + cmd.Flags(). + BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue processing operations even if some operations are invalid.") return cmd } @@ -111,7 +114,13 @@ func runOperationsCmd(opts *OperationsOptions) error { } totalOperations++ - opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Read %s from %s", utils.Pluralize(totalOperations, "operation"), opts.File)) + opts.IO.UpdateProgressIndicatorLabel( + fmt.Sprintf( + "Read %s from %s", + utils.Pluralize(totalOperations, "operation"), + opts.File, + ), + ) var batchOperation search.BatchOperationIndexed if err := json.Unmarshal([]byte(line), &batchOperation); err != nil { @@ -142,7 +151,7 @@ func runOperationsCmd(opts *OperationsOptions) error { // No operations found if len(operations) == 0 { if len(errors) > 0 { - return fmt.Errorf(errorMsg) + return fmt.Errorf("%s", errorMsg) } return fmt.Errorf("%s No operations found in the file", cs.FailureIcon()) } @@ -164,7 +173,9 @@ func runOperationsCmd(opts *OperationsOptions) error { } // Process operations - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Processing %s operations", cs.Bold(fmt.Sprint(len(operations))))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Processing %s operations", cs.Bold(fmt.Sprint(len(operations)))), + ) res, err := client.MultipleBatch(operations) if err != nil { opts.IO.StopProgressIndicator() @@ -181,7 +192,13 @@ func runOperationsCmd(opts *OperationsOptions) error { } opts.IO.StopProgressIndicator() - _, err = fmt.Fprintf(opts.IO.Out, "%s Successfully processed %s operations in %v\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(len(operations))), time.Since(elapsed)) + _, err = fmt.Fprintf( + opts.IO.Out, + "%s Successfully processed %s operations in %v\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(len(operations))), + time.Since(elapsed), + ) return err } diff --git a/pkg/cmd/objects/update/update.go b/pkg/cmd/objects/update/update.go index cdf0da86..f1aa54ff 100644 --- a/pkg/cmd/objects/update/update.go +++ b/pkg/cmd/objects/update/update.go @@ -88,13 +88,17 @@ func NewUpdateCmd(f *cmdutil.Factory, runF func(*UpdateOptions) error) *cobra.Co }, } - cmd.Flags().StringVarP(&opts.File, "file", "F", "", "Read objects to update from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&opts.File, "file", "F", "", "Read objects to update from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVarP(&opts.CreateIfNotExists, "create-if-not-exists", "c", false, "If provided, updating a nonexistent object will create a new object with the objectID and the attributes defined in the object") - cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the operation to complete before returning") + cmd.Flags(). + BoolVarP(&opts.CreateIfNotExists, "create-if-not-exists", "c", false, "If provided, updating a nonexistent object will create a new object with the objectID and the attributes defined in the object") + cmd.Flags(). + BoolVarP(&opts.Wait, "wait", "w", false, "Wait for the operation to complete before returning") - cmd.Flags().BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue updating objects even if some objects are invalid.") + cmd.Flags(). + BoolVarP(&opts.ContinueOnError, "continue-on-error", "C", false, "Continue updating objects even if some objects are invalid.") return cmd } @@ -127,7 +131,9 @@ func runUpdateCmd(opts *UpdateOptions) error { } totalObjects++ - opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Read %s from %s", utils.Pluralize(totalObjects, "object"), opts.File)) + opts.IO.UpdateProgressIndicatorLabel( + fmt.Sprintf("Read %s from %s", utils.Pluralize(totalObjects, "object"), opts.File), + ) var obj Object if err := json.Unmarshal([]byte(line), &obj); err != nil { @@ -153,7 +159,7 @@ func runUpdateCmd(opts *UpdateOptions) error { // No objects found if len(objects) == 0 { if len(errors) > 0 { - return fmt.Errorf(errorMsg) + return fmt.Errorf("%s", errorMsg) } return fmt.Errorf("%s No objects found in the file", cs.FailureIcon()) } @@ -175,7 +181,13 @@ func runUpdateCmd(opts *UpdateOptions) error { } // Update the objects - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Updating %s objects on %s", cs.Bold(fmt.Sprint(len(objects))), cs.Bold(opts.Index))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf( + "Updating %s objects on %s", + cs.Bold(fmt.Sprint(len(objects))), + cs.Bold(opts.Index), + ), + ) res, err := index.PartialUpdateObjects(objects, opt.CreateIfNotExists(opts.CreateIfNotExists)) if err != nil { opts.IO.StopProgressIndicator() @@ -192,6 +204,13 @@ func runUpdateCmd(opts *UpdateOptions) error { } opts.IO.StopProgressIndicator() - _, err = fmt.Fprintf(opts.IO.Out, "%s Successfully updated %s objects on %s in %v\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(len(objects))), cs.Bold(opts.Index), time.Since(elapsed)) + _, err = fmt.Fprintf( + opts.IO.Out, + "%s Successfully updated %s objects on %s in %v\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(len(objects))), + cs.Bold(opts.Index), + time.Since(elapsed), + ) return err } diff --git a/pkg/cmd/open/open.go b/pkg/cmd/open/open.go index eb9473b1..f97dc9c8 100644 --- a/pkg/cmd/open/open.go +++ b/pkg/cmd/open/open.go @@ -15,43 +15,49 @@ import ( "github.com/algolia/cli/pkg/printers" ) -type OpenUrl struct { +type OpenURL struct { Default string - WithAppId string + WithAppID string } -var openUrlMap = map[string]OpenUrl{ - "api": {Default: "https://www.algolia.com/doc/api-reference/rest-api/"}, - "codex": {Default: "https://www.algolia.com/developers/code-exchange/"}, - "cli-docs": {Default: "https://algolia.com/doc/tools/cli/get-started/overview/"}, - "cli-repo": {Default: "https://github.com/algolia/cli"}, - "dashboard": {Default: "https://www.algolia.com/dashboard", WithAppId: "https://www.algolia.com/apps/%s/dashboard"}, +var openURLMap = map[string]OpenURL{ + "api": {Default: "https://www.algolia.com/doc/api-reference/rest-api/"}, + "codex": {Default: "https://www.algolia.com/developers/code-exchange/"}, + "cli-docs": {Default: "https://algolia.com/doc/tools/cli/get-started/overview/"}, + "cli-repo": {Default: "https://github.com/algolia/cli"}, + "dashboard": { + Default: "https://www.algolia.com/dashboard", + WithAppID: "https://www.algolia.com/apps/%s/dashboard", + }, "devhub": {Default: "https://www.algolia.com/developers/"}, "docs": {Default: "https://algolia.com/doc/"}, "languages": {Default: "https://alg.li/supported-languages"}, - "status": {Default: "https://status.algolia.com/", WithAppId: "https://www.algolia.com/apps/%s/monitoring/status"}, + "status": { + Default: "https://status.algolia.com/", + WithAppID: "https://www.algolia.com/apps/%s/monitoring/status", + }, } func openNames() []string { - keys := make([]string, 0, len(openUrlMap)) - for k := range openUrlMap { + keys := make([]string, 0, len(openURLMap)) + for k := range openURLMap { keys = append(keys, k) } return keys } -func getNameUrlMap(applicationID string) map[string]string { - nameUrlMap := make(map[string]string) +func getNameURLMap(applicationID string) map[string]string { + nameURLMap := make(map[string]string) for _, openName := range openNames() { - url := openUrlMap[openName].Default - if applicationID != "" && openUrlMap[openName].WithAppId != "" { - url = fmt.Sprintf(openUrlMap[openName].WithAppId, applicationID) + url := openURLMap[openName].Default + if applicationID != "" && openURLMap[openName].WithAppID != "" { + url = fmt.Sprintf(openURLMap[openName].WithAppID, applicationID) } - nameUrlMap[openName] = url + nameURLMap[openName] = url } - return nameUrlMap + return nameURLMap } // OpenOptions represents the options for the open command @@ -119,7 +125,7 @@ func NewOpenCmd(f *cmdutil.Factory) *cobra.Command { func runOpenCmd(opts *OpenOptions) error { profile := opts.config.Profile() applicationID, _ := profile.GetApplicationID() - nameUrlMap := getNameUrlMap(applicationID) + nameURLMap := getNameURLMap(applicationID) if opts.List || opts.Shortcut == "" { fmt.Println("open quickly opens Algolia pages. To use, run 'algolia open '.") @@ -136,7 +142,7 @@ func runOpenCmd(opts *OpenOptions) error { table.EndRow() } - for shortcutName, url := range nameUrlMap { + for shortcutName, url := range nameURLMap { table.AddField(shortcutName, nil, nil) table.AddField(url, nil, nil) table.EndRow() @@ -146,9 +152,8 @@ func runOpenCmd(opts *OpenOptions) error { } var err error - if url, ok := nameUrlMap[opts.Shortcut]; ok { + if url, ok := nameURLMap[opts.Shortcut]; ok { err = open.Browser(url) - if err != nil { return err } diff --git a/pkg/cmd/root/root_help.go b/pkg/cmd/root/root_help.go index d046ffd8..5e38dca9 100644 --- a/pkg/cmd/root/root_help.go +++ b/pkg/cmd/root/root_help.go @@ -12,7 +12,7 @@ import ( "github.com/algolia/cli/pkg/cmdutil" ) -func Test_printError(t *testing.T) { +func TestPrintError(t *testing.T) { cmd := &cobra.Command{} type args struct { diff --git a/pkg/cmd/rules/browse/browse.go b/pkg/cmd/rules/browse/browse.go index b6b13a00..23c79cd6 100644 --- a/pkg/cmd/rules/browse/browse.go +++ b/pkg/cmd/rules/browse/browse.go @@ -1,9 +1,9 @@ package browse import ( - "io" + "fmt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/MakeNowJust/heredoc" @@ -18,14 +18,14 @@ type ExportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string PrintFlags *cmdutil.PrintFlags } -// NewBrowseCmd creates and returns a browse command for indice's rules +// NewBrowseCmd creates and returns a browse command for Rules func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &ExportOptions{ IO: f.IOStreams, @@ -37,6 +37,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "browse ", Args: validators.ExactArgs(1), + Aliases: []string{"list"}, ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Short: "List all the rules of an index", Annotations: map[string]string{ @@ -51,7 +52,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { $ algolia rules browse MOVIES -o json > rules.ndjson `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] return runListCmd(opts) }, @@ -68,27 +69,32 @@ func runListCmd(opts *ExportOptions) error { return err } - indice := client.InitIndex(opts.Indice) - res, err := indice.BrowseRules() + // Check if index exists because the API just returns an empty list if it doesn't + exists, err := client.IndexExists(opts.Index) if err != nil { return err } + if !exists { + return fmt.Errorf("index %s doesn't exist", opts.Index) + } p, err := opts.PrintFlags.ToPrinter() if err != nil { return err } - - for { - iObject, err := res.Next() - if err != nil { - if err == io.EOF { - return nil + err = client.BrowseRules( + opts.Index, + *search.NewEmptySearchRulesParams(), + search.WithAggregator(func(res any, _ error) { + for _, rule := range res.(*search.SearchRulesResponse).Hits { + if err = p.Print(opts.IO, rule); err != nil { + continue + } } - return err - } - if err = p.Print(opts.IO, iObject); err != nil { - return err - } + }), + ) + if err != nil { + return err } + return nil } diff --git a/pkg/cmd/rules/import/import.go b/pkg/cmd/rules/import/import.go index ce312c0a..2eec929b 100644 --- a/pkg/cmd/rules/import/import.go +++ b/pkg/cmd/rules/import/import.go @@ -6,8 +6,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -21,9 +20,9 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string ForwardToReplicas bool ClearExistingRules bool Scanner *bufio.Scanner @@ -36,7 +35,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -45,7 +44,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -68,11 +67,13 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co $ algolia rules import MOVIES -F rules.ndjson -f=false `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] if !confirm && opts.ClearExistingRules { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -93,11 +94,14 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") - cmd.Flags().StringVarP(&file, "file", "F", "", "Read rules to import from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&file, "file", "F", "", "Read rules to import from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the rules to the index replicas") - cmd.Flags().BoolVarP(&opts.ClearExistingRules, "clear-existing-rules", "c", false, "Clear existing rules before importing new ones") + cmd.Flags(). + BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the rules to the index replicas") + cmd.Flags(). + BoolVarP(&opts.ClearExistingRules, "clear-existing-rules", "c", false, "Clear existing rules before importing new ones") return cmd } @@ -105,7 +109,13 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co func runImportCmd(opts *ImportOptions) error { if opts.DoConfirm { var confirmed bool - err := prompt.Confirm(fmt.Sprintf("Are you sure you want to replace all the existing rules on %q?", opts.Indice), &confirmed) + err := prompt.Confirm( + fmt.Sprintf( + "Are you sure you want to replace all the existing rules on %q?", + opts.Index, + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -119,24 +129,15 @@ func runImportCmd(opts *ImportOptions) error { return err } - indice := client.InitIndex(opts.Indice) - defaultBatchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - } - // Only clear existing rules on the first batch - batchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - opt.ClearExistingRules(opts.ClearExistingRules), - } - // Move the following code to another module? var ( batchSize = 1000 - batch = make([]search.Rule, 0, batchSize) + rules = make([]search.Rule, 0, batchSize) count = 0 totalCount = 0 ) + clearExistingRules := opts.ClearExistingRules opts.IO.StartProgressIndicatorWithLabel("Importing rules") for opts.Scanner.Scan() { line := opts.Scanner.Text() @@ -150,30 +151,36 @@ func runImportCmd(opts *ImportOptions) error { return err } - batch = append(batch, rule) + rules = append(rules, rule) count++ + // If requested, only clear existing rules the first time if count == batchSize { - if _, err := indice.SaveRules(batch, batchOptions...); err != nil { + _, err := client.SaveRules( + client.NewApiSaveRulesRequest(opts.Index, rules). + WithClearExistingRules(clearExistingRules). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { return err } - batchOptions = defaultBatchOptions - batch = make([]search.Rule, 0, batchSize) + rules = make([]search.Rule, 0, batchSize) totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d rules", totalCount)) count = 0 + clearExistingRules = false } } if count > 0 { totalCount += count - if _, err := indice.SaveRules(batch, batchOptions...); err != nil { + if _, err := client.SaveRules(client.NewApiSaveRulesRequest(opts.Index, rules).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } // Clear rules if 0 rules are imported and the clear existing is set if totalCount == 0 && opts.ClearExistingRules { - if _, err := indice.ClearRules(); err != nil { + if _, err := client.ClearRules(client.NewApiClearRulesRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } @@ -186,7 +193,13 @@ func runImportCmd(opts *ImportOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully imported %s rules to %s\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(totalCount)), opts.Indice) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully imported %s rules to %s\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(totalCount)), + opts.Index, + ) } return nil diff --git a/pkg/cmd/rules/import/import_test.go b/pkg/cmd/rules/import/import_test.go index 009352ba..bc6aecfb 100644 --- a/pkg/cmd/rules/import/import_test.go +++ b/pkg/cmd/rules/import/import_test.go @@ -1,4 +1,4 @@ -package importRules +package importrules import ( "fmt" diff --git a/pkg/cmd/shared/config/config.go b/pkg/cmd/shared/config/config.go index defdf78c..bcd7867f 100644 --- a/pkg/cmd/shared/config/config.go +++ b/pkg/cmd/shared/config/config.go @@ -61,13 +61,21 @@ type ExportConfigJson struct { Synonyms []search.Synonym `json:"synonyms,omitempty"` } -func GetIndiceConfig(indice *search.Index, scope []string, cs *iostreams.ColorScheme) (*ExportConfigJson, error) { +func GetIndiceConfig( + indice *search.Index, + scope []string, + cs *iostreams.ColorScheme, +) (*ExportConfigJson, error) { var configJson ExportConfigJson if utils.Contains(scope, "synonyms") { rawSynonyms, err := GetSynonyms(indice) if err != nil { - return nil, fmt.Errorf("%s An error occurred when retrieving synonyms: %w", cs.FailureIcon(), err) + return nil, fmt.Errorf( + "%s An error occurred when retrieving synonyms: %w", + cs.FailureIcon(), + err, + ) } configJson.Synonyms = rawSynonyms } @@ -75,7 +83,11 @@ func GetIndiceConfig(indice *search.Index, scope []string, cs *iostreams.ColorSc if utils.Contains(scope, "rules") { rawRules, err := GetRules(indice) if err != nil { - return nil, fmt.Errorf("%s An error occurred when retrieving rules: %w", cs.FailureIcon(), err) + return nil, fmt.Errorf( + "%s An error occurred when retrieving rules: %w", + cs.FailureIcon(), + err, + ) } configJson.Rules = rawRules } @@ -83,7 +95,11 @@ func GetIndiceConfig(indice *search.Index, scope []string, cs *iostreams.ColorSc if utils.Contains(scope, "settings") { rawSettings, err := indice.GetSettings() if err != nil { - return nil, fmt.Errorf("%s An error occurred when retrieving settings: %w", cs.FailureIcon(), err) + return nil, fmt.Errorf( + "%s An error occurred when retrieving settings: %w", + cs.FailureIcon(), + err, + ) } configJson.Settings = &rawSettings } diff --git a/pkg/cmd/shared/handler/indices/config.go b/pkg/cmd/shared/handler/indices/config.go index ca2e3479..5a261257 100644 --- a/pkg/cmd/shared/handler/indices/config.go +++ b/pkg/cmd/shared/handler/indices/config.go @@ -3,7 +3,7 @@ package config import ( "encoding/json" "fmt" - "io/ioutil" + "io" "os" "path/filepath" "strconv" @@ -68,7 +68,13 @@ func GetConfigFileName(path string, indiceName string, appId string) string { rootPath = path + "/" } - return fmt.Sprintf("%sexport-%s-%s-%s.json", rootPath, indiceName, appId, strconv.FormatInt(time.Now().UTC().Unix(), 10)) + return fmt.Sprintf( + "%sexport-%s-%s-%s.json", + rootPath, + indiceName, + appId, + strconv.FormatInt(time.Now().UTC().Unix(), 10), + ) } type ImportOptions struct { @@ -123,10 +129,16 @@ func ValidateImportConfigFlags(opts *ImportOptions) error { } // Scope and replace/clear existing options if opts.ClearExistingRules && !utils.Contains(opts.Scope, "rules") { - return fmt.Errorf("%s Cannot clear existing rules if rules are not in scope", cs.FailureIcon()) + return fmt.Errorf( + "%s Cannot clear existing rules if rules are not in scope", + cs.FailureIcon(), + ) } if opts.ClearExistingSynonyms && !utils.Contains(opts.Scope, "synonyms") { - return fmt.Errorf("%s Cannot clear existing synonyms if synonyms are not in scope", cs.FailureIcon()) + return fmt.Errorf( + "%s Cannot clear existing synonyms if synonyms are not in scope", + cs.FailureIcon(), + ) } // Scope and config if (utils.Contains(opts.Scope, "settings") && opts.ImportConfig.Settings != nil) || @@ -134,7 +146,11 @@ func ValidateImportConfigFlags(opts *ImportOptions) error { (utils.Contains(opts.Scope, "synonyms") && len(opts.ImportConfig.Synonyms) > 0) { return nil } - return fmt.Errorf("%s No %s found in config file", cs.FailureIcon(), utils.SliceToReadableString(opts.Scope)) + return fmt.Errorf( + "%s No %s found in config file", + cs.FailureIcon(), + utils.SliceToReadableString(opts.Scope), + ) } func AskImportConfig(opts *ImportOptions) error { @@ -239,13 +255,21 @@ func readConfigFromFile(cs *iostreams.ColorScheme, filePath string) (*ImportConf return nil, fmt.Errorf("%s An error occurred when opening file: %w", cs.FailureIcon(), err) } defer jsonFile.Close() - byteValue, err := ioutil.ReadAll(jsonFile) + byteValue, err := io.ReadAll(jsonFile) if err != nil { - return nil, fmt.Errorf("%s An error occurred when reading JSON file: %w", cs.FailureIcon(), err) + return nil, fmt.Errorf( + "%s An error occurred when reading JSON file: %w", + cs.FailureIcon(), + err, + ) } err = json.Unmarshal(byteValue, &config) if err != nil { - return nil, fmt.Errorf("%s An error occurred when parsing JSON file: %w", cs.FailureIcon(), err) + return nil, fmt.Errorf( + "%s An error occurred when parsing JSON file: %w", + cs.FailureIcon(), + err, + ) } return config, nil diff --git a/pkg/cmd/shared/handler/synonyms/synonyms.go b/pkg/cmd/shared/handler/synonyms/synonyms.go index 4e57d1d1..74ec5a10 100644 --- a/pkg/cmd/shared/handler/synonyms/synonyms.go +++ b/pkg/cmd/shared/handler/synonyms/synonyms.go @@ -69,7 +69,7 @@ func AskSynonym(flags *shared.SynonymFlags, cmd *cobra.Command) error { replacementsProvided: cmd.Flags().Changed("repalcements"), } - err := AskSynonymIdQuestion(flags, flagsProvided) + err := AskSynonymIDQuestion(flags, flagsProvided) if err != nil { return err } @@ -93,7 +93,7 @@ func AskSynonym(flags *shared.SynonymFlags, cmd *cobra.Command) error { } } -func AskSynonymIdQuestion(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { +func AskSynonymIDQuestion(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { if flagsProvided.idProvided { return nil } diff --git a/pkg/cmd/synonyms/browse/browse.go b/pkg/cmd/synonyms/browse/browse.go index 5cbe217f..53678bee 100644 --- a/pkg/cmd/synonyms/browse/browse.go +++ b/pkg/cmd/synonyms/browse/browse.go @@ -1,10 +1,10 @@ package browse import ( - "io" + "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -17,9 +17,9 @@ type BrowseOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string PrintFlags *cmdutil.PrintFlags } @@ -35,6 +35,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "browse ", + Aliases: []string{"list"}, Args: validators.ExactArgs(1), ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Short: "List all the the synonyms of the given index", @@ -50,7 +51,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { $ algolia synonyms browse MOVIES > synonyms.json `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] return runBrowseCmd(opts) }, @@ -66,28 +67,31 @@ func runBrowseCmd(opts *BrowseOptions) error { if err != nil { return err } - - indice := client.InitIndex(opts.Indice) - res, err := indice.BrowseSynonyms() + // Check if index exists, because the API just returns an empty list if it doesn't + exists, err := client.IndexExists(opts.Index) if err != nil { return err } + if !exists { + return fmt.Errorf("index %s doesn't exist", opts.Index) + } p, err := opts.PrintFlags.ToPrinter() if err != nil { return err } - for { - iObject, err := res.Next() - if err != nil { - if err == io.EOF { - return nil + err = client.BrowseSynonyms( + opts.Index, + *search.NewEmptySearchSynonymsParams(), + search.WithAggregator(func(res any, _ error) { + for _, synonym := range res.(*search.SearchSynonymsResponse).Hits { + p.Print(opts.IO, synonym) } - return err - } - if err = p.Print(opts.IO, iObject); err != nil { - return err - } + }), + ) + if err != nil { + return err } + return nil } diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index 394d8a85..e9055f18 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -1,4 +1,4 @@ -package importSynonyms +package importsynonyms import ( "bufio" diff --git a/pkg/cmd/synonyms/import/import_test.go b/pkg/cmd/synonyms/import/import_test.go index 31570535..b2815e1a 100644 --- a/pkg/cmd/synonyms/import/import_test.go +++ b/pkg/cmd/synonyms/import/import_test.go @@ -1,9 +1,9 @@ -package importSynonyms +package importsynonyms import ( "fmt" - "io/ioutil" "net/http" + "os" "path/filepath" "strings" "testing" @@ -21,7 +21,11 @@ import ( func TestNewImportCmd(t *testing.T) { file := filepath.Join(t.TempDir(), "synonyms.ndjson") - _ = ioutil.WriteFile(file, []byte("{\"objectID\":\"test\", \"type\": \"synonym\", \"synonyms\": [\"test\"]}"), 0o600) + _ = os.WriteFile( + file, + []byte("{\"objectID\":\"test\", \"type\": \"synonym\", \"synonyms\": [\"test\"]}"), + 0o600, + ) tests := []struct { name string @@ -106,12 +110,18 @@ func TestNewImportCmd(t *testing.T) { func Test_runExportCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "synonyms.json") - err := ioutil.WriteFile(tmpFile, []byte("{\"objectID\":\"test\", \"type\": \"synonym\", \"synonyms\": [\"test\"]}"), 0o600) + err := os.WriteFile( + tmpFile, + []byte("{\"objectID\":\"test\", \"type\": \"synonym\", \"synonyms\": [\"test\"]}"), + 0o600, + ) require.NoError(t, err) var largeBatchBuilder strings.Builder for i := 0; i < 1001; i += 1 { - largeBatchBuilder.Write([]byte("{\"objectID\":\"test\",\"type\":\"synonym\",\"synonyms\":[\"test\"]}\n")) + largeBatchBuilder.Write( + []byte("{\"objectID\":\"test\",\"type\":\"synonym\",\"synonyms\":[\"test\"]}\n"), + ) } tests := []struct { @@ -128,7 +138,10 @@ func Test_runExportCmd(t *testing.T) { stdin: `{"objectID":"test", "type": "synonym", "synonyms": ["test"]}`, wantOut: "✓ Successfully imported 1 synonyms to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -136,7 +149,10 @@ func Test_runExportCmd(t *testing.T) { cli: fmt.Sprintf("foo -F '%s'", tmpFile), wantOut: "✓ Successfully imported 1 synonyms to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -152,7 +168,10 @@ func Test_runExportCmd(t *testing.T) { cli: fmt.Sprintf("foo -F '%s' -f", tmpFile), wantOut: "✓ Successfully imported 1 synonyms to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -161,7 +180,10 @@ func Test_runExportCmd(t *testing.T) { stdin: "", wantOut: "✓ Successfully imported 0 synonyms to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/synonyms/clear"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/clear"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -178,10 +200,12 @@ func Test_runExportCmd(t *testing.T) { wantOut: "✓ Successfully imported 1001 synonyms to foo\n", setup: func(r *httpmock.Registry) { r.Register(httpmock.Matcher(func(req *http.Request) bool { - return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && req.URL.Query().Get("replaceExistingSynonyms") == "true" + return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && + req.URL.Query().Get("replaceExistingSynonyms") == "true" }), httpmock.JSONResponse(search.UpdateTaskRes{})) r.Register(httpmock.Matcher(func(req *http.Request) bool { - return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && req.URL.Query().Get("replaceExistingSynonyms") == "" + return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && + req.URL.Query().Get("replaceExistingSynonyms") == "" }), httpmock.JSONResponse(search.UpdateTaskRes{})) }, }, diff --git a/pkg/cmd/synonyms/save/messages.go b/pkg/cmd/synonyms/save/messages.go index 98d852cb..fe7258a9 100644 --- a/pkg/cmd/synonyms/save/messages.go +++ b/pkg/cmd/synonyms/save/messages.go @@ -13,24 +13,24 @@ import ( type SuccessMessage struct { Icon string Type string - Id string + ID string Values string - Indice string + Index string } -const successTemplate = `{{ .Type}} '{{ .Id}}' successfully saved with {{ .Values}} to {{ .Indice}}` +const successTemplate = `{{ .Type }} '{{ .ID }}' successfully saved with {{ .Values }} to {{ .Index }}` -func GetSuccessMessage(flags shared.SynonymFlags, indice string) (error, string) { +func GetSuccessMessage(flags shared.SynonymFlags, index string) (string, error) { var successMessage SuccessMessage if flags.SynonymType == "" || flags.SynonymType == shared.Regular { successMessage = SuccessMessage{ Type: "Synonym", - Id: flags.SynonymID, + ID: flags.SynonymID, Values: fmt.Sprintf("%s (%s)", utils.Pluralize(len(flags.Synonyms), "synonym"), strings.Join(flags.Synonyms, ", ")), - Indice: indice, + Index: index, } } @@ -38,22 +38,22 @@ func GetSuccessMessage(flags shared.SynonymFlags, indice string) (error, string) case shared.OneWay: successMessage = SuccessMessage{ Type: "One way synonym", - Id: flags.SynonymID, + ID: flags.SynonymID, Values: fmt.Sprintf("input '%s' and %s (%s)", flags.SynonymInput, utils.Pluralize(len(flags.Synonyms), "synonym"), strings.Join(flags.Synonyms, ", ")), - Indice: indice, + Index: index, } case shared.Placeholder: successMessage = SuccessMessage{ Type: "Placeholder synonym", - Id: flags.SynonymID, + ID: flags.SynonymID, Values: fmt.Sprintf("placeholder '%s' and %s (%s)", flags.SynonymPlaceholder, utils.Pluralize(len(flags.SynonymReplacements), "replacement"), strings.Join(flags.SynonymReplacements, ", ")), - Indice: indice, + Index: index, } case shared.AltCorrection1, shared.AltCorrection2: altCorrectionType := "1" @@ -63,12 +63,12 @@ func GetSuccessMessage(flags shared.SynonymFlags, indice string) (error, string) altCorrectionType = "Alt correction " + altCorrectionType + " synonym" successMessage = SuccessMessage{ Type: altCorrectionType, - Id: flags.SynonymID, + ID: flags.SynonymID, Values: fmt.Sprintf("word '%s' and %s (%s)", flags.SynonymWord, utils.Pluralize(len(flags.SynonymCorrections), "correction"), strings.Join(flags.SynonymCorrections, ", ")), - Indice: indice, + Index: index, } } @@ -76,7 +76,7 @@ func GetSuccessMessage(flags shared.SynonymFlags, indice string) (error, string) var tpl bytes.Buffer if err := t.Execute(&tpl, successMessage); err != nil { - return err, "" + return "", err } - return nil, tpl.String() + "\n" + return tpl.String() + "\n", nil } diff --git a/pkg/cmd/synonyms/save/messages_test.go b/pkg/cmd/synonyms/save/messages_test.go index 73238e65..3db665f4 100644 --- a/pkg/cmd/synonyms/save/messages_test.go +++ b/pkg/cmd/synonyms/save/messages_test.go @@ -26,7 +26,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { Synonyms: []string{"mj", "goat"}, }, saveOptions: SaveOptions{ - Indice: "legends", + Index: "legends", }, wantsOutput: "✓ Synonym '23' successfully saved with 2 synonyms (mj, goat) to legends\n", }, @@ -39,7 +39,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { SynonymInput: "michael", }, saveOptions: SaveOptions{ - Indice: "legends", + Index: "legends", }, wantsOutput: "✓ One way synonym '23' successfully saved with input 'michael' and 2 synonyms (mj, goat) to legends\n", }, @@ -52,7 +52,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { SynonymPlaceholder: "michael", }, saveOptions: SaveOptions{ - Indice: "legends", + Index: "legends", }, wantsOutput: "✓ Placeholder synonym '23' successfully saved with placeholder 'michael' and 2 replacements (mj, goat) to legends\n", }, @@ -65,7 +65,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { SynonymWord: "michael", }, saveOptions: SaveOptions{ - Indice: "legends", + Index: "legends", }, wantsOutput: "✓ Alt correction 1 synonym '23' successfully saved with word 'michael' and 2 corrections (mj, goat) to legends\n", }, @@ -78,7 +78,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { SynonymWord: "michael", }, saveOptions: SaveOptions{ - Indice: "legends", + Index: "legends", }, wantsOutput: "✓ Alt correction 2 synonym '23' successfully saved with word 'michael' and 2 corrections (mj, goat) to legends\n", }, @@ -91,10 +91,14 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { IOStreams: io, } - err, message := GetSuccessMessage(tt.synonymFlags, tt.saveOptions.Indice) + message, err := GetSuccessMessage(tt.synonymFlags, tt.saveOptions.Index) assert.Equal(t, err, nil) - assert.Equal(t, tt.wantsOutput, fmt.Sprintf("%s %s", f.IOStreams.ColorScheme().SuccessIcon(), message)) + assert.Equal( + t, + tt.wantsOutput, + fmt.Sprintf("%s %s", f.IOStreams.ColorScheme().SuccessIcon(), message), + ) }) } } diff --git a/pkg/cmd/synonyms/save/save.go b/pkg/cmd/synonyms/save/save.go index 9407b8c8..e964143f 100644 --- a/pkg/cmd/synonyms/save/save.go +++ b/pkg/cmd/synonyms/save/save.go @@ -1,11 +1,11 @@ package save import ( + "encoding/json" "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/shared/handler" @@ -20,11 +20,11 @@ type SaveOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string ForwardToReplicas bool - Synonym search.Synonym + Synonym search.SynonymHit SuccessMessage string } @@ -33,7 +33,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman opts := &SaveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } flags := &shared.SynonymFlags{} @@ -41,7 +41,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "save --id --synonyms ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -56,7 +56,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman $ algolia synonyms save MOVIES --id 1 --synonyms foo,bar `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] flagsHandler := &handler.SynonymHandler{ Flags: flags, @@ -72,14 +72,25 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman if err != nil { return err } - // Correct flags are passed - opts.Synonym = synonym + // Map to object + tmp, err := json.Marshal(synonym) + if err != nil { + return err + } + err = json.Unmarshal(tmp, &opts.Synonym) + if err != nil { + return err + } - err, successMessage := GetSuccessMessage(*flags, opts.Indice) + err, successMessage := GetSuccessMessage(*flags, opts.Index) if err != nil { return err } - opts.SuccessMessage = fmt.Sprintf("%s %s", f.IOStreams.ColorScheme().SuccessIcon(), successMessage) + opts.SuccessMessage = fmt.Sprintf( + "%s %s", + f.IOStreams.ColorScheme().SuccessIcon(), + successMessage, + ) if runF != nil { return runF(opts) @@ -91,7 +102,8 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman // Common cmd.Flags().StringVarP(&flags.SynonymID, "id", "i", "", "Synonym ID to save") - cmd.Flags().StringVarP(&flags.SynonymType, "type", "t", "", "Synonym type to save (default to regular)") + cmd.Flags(). + StringVarP(&flags.SynonymType, "type", "t", "", "Synonym type to save (default to regular)") _ = cmd.RegisterFlagCompletionFunc("type", cmdutil.StringCompletionFunc(map[string]string{ shared.Regular: "(default) Used when you want a word or phrase to find its synonyms or the other way around.", @@ -100,17 +112,23 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman shared.AltCorrection2: "Used when you want records with an exact query match to rank higher than a synonym match. (will return matches with two typos)", shared.Placeholder: "Used to place not-yet-defined “tokens” (that can take any value from a list of defined words).", })) - cmd.Flags().BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the save request to the replicas") + cmd.Flags(). + BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the save request to the replicas") // Regular synonym cmd.Flags().StringSliceVarP(&flags.Synonyms, "synonyms", "s", nil, "Synonyms to save") // One way synonym - cmd.Flags().StringVarP(&flags.SynonymInput, "input", "n", "", "Word of phrases to appear in query strings (one way synonyms only)") + cmd.Flags(). + StringVarP(&flags.SynonymInput, "input", "n", "", "Word of phrases to appear in query strings (one way synonyms only)") // Placeholder synonym - cmd.Flags().StringVarP(&flags.SynonymPlaceholder, "placeholder", "l", "", "A single word, used as the basis for the below array of replacements (placeholder synonyms only)") - cmd.Flags().StringSliceVarP(&flags.SynonymReplacements, "replacements", "r", nil, "An list of replacements of the placeholder (placeholder synonyms only)") + cmd.Flags(). + StringVarP(&flags.SynonymPlaceholder, "placeholder", "l", "", "A single word, used as the basis for the below array of replacements (placeholder synonyms only)") + cmd.Flags(). + StringSliceVarP(&flags.SynonymReplacements, "replacements", "r", nil, "An list of replacements of the placeholder (placeholder synonyms only)") // Alt correction synonym - cmd.Flags().StringVarP(&flags.SynonymWord, "word", "w", "", "A single word, used as the basis for the array of corrections (alt correction synonyms only)") - cmd.Flags().StringSliceVarP(&flags.SynonymCorrections, "corrections", "c", nil, "A list of corrections of the word (alt correction synonyms only)") + cmd.Flags(). + StringVarP(&flags.SynonymWord, "word", "w", "", "A single word, used as the basis for the array of corrections (alt correction synonyms only)") + cmd.Flags(). + StringSliceVarP(&flags.SynonymCorrections, "corrections", "c", nil, "A list of corrections of the word (alt correction synonyms only)") return cmd } @@ -120,11 +138,10 @@ func runSaveCmd(opts *SaveOptions) error { if err != nil { return err } - - indice := client.InitIndex(opts.Indice) - forwardToReplicas := opt.ForwardToReplicas(opts.ForwardToReplicas) - - _, err = indice.SaveSynonym(opts.Synonym, forwardToReplicas) + _, err = client.SaveSynonym( + client.NewApiSaveSynonymRequest(opts.Index, opts.Synonym.ObjectID, &opts.Synonym). + WithForwardToReplicas(opts.ForwardToReplicas), + ) if err != nil { err = fmt.Errorf("failed to save synonym: %w", err) return err diff --git a/pkg/cmdutil/file_input.go b/pkg/cmdutil/file_input.go index ba9a16d9..6821f39b 100644 --- a/pkg/cmdutil/file_input.go +++ b/pkg/cmdutil/file_input.go @@ -3,7 +3,6 @@ package cmdutil import ( "bufio" "io" - "io/ioutil" "os" ) @@ -11,12 +10,12 @@ const maxCapacity = 1024 * 1024 // 1MB func ReadFile(filename string, stdin io.ReadCloser) ([]byte, error) { if filename == "-" { - b, err := ioutil.ReadAll(stdin) + b, err := io.ReadAll(stdin) _ = stdin.Close() return b, err } - return ioutil.ReadFile(filename) + return os.ReadFile(filename) } func ScanFile(filename string, stdin io.ReadCloser) (*bufio.Scanner, error) { diff --git a/pkg/cmdutil/jsonpath_flags.go b/pkg/cmdutil/jsonpath_flags.go index cb2ad440..595e4076 100644 --- a/pkg/cmdutil/jsonpath_flags.go +++ b/pkg/cmdutil/jsonpath_flags.go @@ -2,7 +2,7 @@ package cmdutil import ( "fmt" - "io/ioutil" + "os" "sort" "strings" @@ -64,7 +64,10 @@ func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (printers.Printer, } if _, supportedFormat := jsonFormats[templateFormat]; !supportedFormat { - return nil, NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()} + return nil, NoCompatiblePrinterError{ + OutputFormat: &templateFormat, + AllowedFormats: f.AllowedFormats(), + } } if len(templateValue) == 0 { @@ -72,7 +75,7 @@ func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (printers.Printer, } if templateFormat == "jsonpath-file" { - data, err := ioutil.ReadFile(templateValue) + data, err := os.ReadFile(templateValue) if err != nil { return nil, fmt.Errorf("error reading --template %s, %v", templateValue, err) } @@ -103,12 +106,14 @@ func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (printers.Printer, // flags related to template printing to it func (f *JSONPathPrintFlags) AddFlags(c *cobra.Command) { if f.TemplateArgument != nil { - c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when --output=jsonpath, --output=jsonpath-file.") + c.Flags(). + StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when --output=jsonpath, --output=jsonpath-file.") _ = c.Flags().SetAnnotation("template", "IsPrint", []string{"true"}) _ = c.MarkFlagFilename("template") } if f.AllowMissingKeys != nil { - c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.") + c.Flags(). + BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.") _ = c.Flags().SetAnnotation("allow-missing-template-keys", "IsPrint", []string{"true"}) } } diff --git a/pkg/config/profile.go b/pkg/config/profile.go index a04d8d78..42f7457f 100644 --- a/pkg/config/profile.go +++ b/pkg/config/profile.go @@ -47,9 +47,9 @@ func (p *Profile) GetApplicationID() (string, error) { } if err := viper.ReadInConfig(); err == nil { - appId := viper.GetString(p.GetFieldName("application_id")) - if appId != "" { - return appId, nil + appID := viper.GetString(p.GetFieldName("application_id")) + if appID != "" { + return appID, nil } } diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index 3d23bb49..49f3a169 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "os/exec" "strconv" @@ -253,7 +252,12 @@ func (s *IOStreams) StartProgressIndicatorWithLabel(label string) { // https://github.com/briandowns/spinner#available-character-sets dotStyle := spinner.CharSets[11] - sp := spinner.New(dotStyle, 120*time.Millisecond, spinner.WithWriter(s.ErrOut), spinner.WithColor("fgCyan")) + sp := spinner.New( + dotStyle, + 120*time.Millisecond, + spinner.WithWriter(s.ErrOut), + spinner.WithColor("fgCyan"), + ) if label != "" { sp.Prefix = label + " " } @@ -370,7 +374,7 @@ func (s *IOStreams) TempFile(dir, pattern string) (*os.File, error) { if s.TempFileOverride != nil { return s.TempFileOverride, nil } - return ioutil.TempFile(dir, pattern) + return os.CreateTemp(dir, pattern) } func System() *IOStreams { @@ -411,7 +415,7 @@ func Test() (*IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) { out := &bytes.Buffer{} errOut := &bytes.Buffer{} return &IOStreams{ - In: ioutil.NopCloser(in), + In: io.NopCloser(in), Out: out, ErrOut: errOut, ttySize: func() (int, int, error) { diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index 37227156..ada3ad05 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -2,7 +2,7 @@ package telemetry import ( "context" - "crypto/md5" + "crypto/md5" // nolint:gosec "fmt" "log" "net" @@ -80,7 +80,7 @@ func anonymousID() string { } a := a.HardwareAddr.String() if a != "" { - return fmt.Sprintf("%x", md5.Sum([]byte(a))) + return fmt.Sprintf("%x", md5.Sum([]byte(a))) // nolint:gosec } } return "" @@ -90,7 +90,7 @@ type NoOpTelemetryClient struct{} type CLIAnalyticsEventMetadata struct { AnonymousID string // the anonymous id is the hash of the mac address of the machine - UserId string // TODO: Once we implement OAuth + UserID string // TODO: Once we implement OAuth InvocationID string // the invocation id is unique to each context object and represents all events coming from one command ConfiguredApplicationsNb int // the number of configured applications AppID string // the app id with which the command was called diff --git a/pkg/telemetry/telemetry_test.go b/pkg/telemetry/telemetry_test.go index 03e7e6be..c1ce9d30 100644 --- a/pkg/telemetry/telemetry_test.go +++ b/pkg/telemetry/telemetry_test.go @@ -13,7 +13,7 @@ import ( func TestEventMetadataWithGet(t *testing.T) { ctx := context.Background() event := &CLIAnalyticsEventMetadata{ - UserId: "user-id", + UserID: "user-id", InvocationID: "invocation-id", OS: "os", CLIVersion: "cli-version", diff --git a/pkg/text/truncate.go b/pkg/text/truncate.go index 2de01612..0cf07410 100644 --- a/pkg/text/truncate.go +++ b/pkg/text/truncate.go @@ -29,6 +29,14 @@ func Truncate(maxWidth int, s string) string { tail = ellipsis } + // Guard against overflow + if maxWidth < 0 { + maxWidth = 0 + } + + // Seems to be a false positive from gosec + // since max(uint) > max(int) && maxWidth >= 0 at this point + // nolint:gosec r := truncate.StringWithTail(s, uint(maxWidth), tail) if DisplayWidth(r) < maxWidth { r += " " diff --git a/pkg/validators/cmd.go b/pkg/validators/cmd.go index 260cae11..52564c3f 100644 --- a/pkg/validators/cmd.go +++ b/pkg/validators/cmd.go @@ -13,7 +13,7 @@ import ( func ExactArgsWithMsg(n int, msg string) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) != n { - return cmdutil.FlagErrorf(msg) + return cmdutil.FlagErrorf("%s", msg) } return nil @@ -49,7 +49,6 @@ func ExactArgs(n int) cobra.PositionalArgs { return extractArgs(cmd, args) } - } // AtLeastNArgs is a validator for commands to print an error with a custom message @@ -62,11 +61,11 @@ func AtLeastNArgs(n int) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { if len(args) < n { - return cmdutil.FlagErrorf( - fmt.Sprintf("`%s` requires at least %d %s.", cmd.CommandPath(), n, argument)) + msg := fmt.Sprintf("`%s` requires at least %d %s.", cmd.CommandPath(), n, argument) + + return cmdutil.FlagErrorf("%s", msg) } return nil } - } From 178d052b6bd229ab41aaf781e1c70b3e263c2945 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 31 Jan 2025 15:49:40 +0100 Subject: [PATCH 03/45] fix: format all go files With gofumpt and golines for more readability --- Taskfile.yml | 99 ++------------- api/insights/client.go | 113 ++++++++---------- api/insights/utils.go | 5 +- cmd/docs/main.go | 10 +- devbox.json | 3 +- internal/analyze/analyze.go | 8 +- internal/docs/docs.go | 1 - internal/update/update.go | 9 +- pkg/ask/ask.go | 45 ++++++- pkg/auth/auth_check.go | 24 ++-- pkg/auth/auth_check_test.go | 1 - pkg/cmd/apikeys/create/create.go | 36 +++--- pkg/cmd/apikeys/create/create_test.go | 5 +- pkg/cmd/apikeys/delete/delete.go | 16 ++- pkg/cmd/apikeys/delete/delete_test.go | 10 +- pkg/cmd/crawler/crawl/crawl.go | 16 ++- pkg/cmd/crawler/crawl/crawl_test.go | 5 +- pkg/cmd/crawler/create/create.go | 11 +- pkg/cmd/crawler/create/create_test.go | 4 +- pkg/cmd/crawler/get/get.go | 3 +- pkg/cmd/crawler/pause/pause.go | 11 +- pkg/cmd/crawler/reindex/reindex.go | 14 ++- pkg/cmd/crawler/run/run.go | 7 +- pkg/cmd/crawler/test/test.go | 7 +- pkg/cmd/crawler/unblock/unblock.go | 13 +- pkg/cmd/dictionary/entries/browse/browse.go | 30 ++++- .../dictionary/entries/browse/browse_test.go | 14 ++- pkg/cmd/dictionary/entries/clear/clear.go | 41 +++++-- .../dictionary/entries/clear/clear_test.go | 14 ++- pkg/cmd/dictionary/entries/delete/delete.go | 21 +++- .../dictionary/entries/delete/delete_test.go | 13 +- .../dictionary/entries/import/import_test.go | 13 +- pkg/cmd/dictionary/settings/set/set.go | 42 +++++-- pkg/cmd/dictionary/shared/constants.go | 22 ++-- pkg/cmd/events/tail/tail.go | 14 ++- pkg/cmd/indices/analyze/analyze.go | 14 ++- pkg/cmd/indices/config/export/export.go | 2 +- pkg/cmd/indices/config/import/confirm.go | 23 +++- pkg/cmd/indices/config/import/confirm_test.go | 6 +- pkg/cmd/objects/browse/browse_test.go | 9 +- pkg/cmd/objects/delete/delete.go | 14 ++- pkg/cmd/objects/delete/delete_test.go | 26 ++-- pkg/cmd/objects/import/import.go | 23 +++- pkg/cmd/objects/import/import_test.go | 7 +- pkg/cmd/objects/operations/operations_test.go | 19 ++- pkg/cmd/objects/update/object_test.go | 5 +- pkg/cmd/objects/update/update_test.go | 7 +- pkg/cmd/open/open.go | 34 +++--- pkg/cmd/profile/add/add.go | 54 ++++++--- pkg/cmd/profile/list/list.go | 9 +- pkg/cmd/profile/remove/remove.go | 9 +- pkg/cmd/profile/remove/remove_test.go | 31 +++-- pkg/cmd/root/help.go | 40 ++++++- pkg/cmd/root/root.go | 31 +++-- pkg/cmd/rules/browse/browse_test.go | 9 +- pkg/cmd/rules/delete/delete.go | 24 +++- pkg/cmd/rules/delete/delete_test.go | 10 +- pkg/cmd/rules/import/import.go | 48 ++++---- pkg/cmd/rules/import/import_test.go | 21 +++- pkg/cmd/search/search.go | 4 +- pkg/cmd/settings/import/import.go | 3 +- pkg/cmd/settings/import/import_test.go | 8 +- pkg/cmd/settings/set/set.go | 10 +- pkg/cmd/settings/set/set_test.go | 5 +- pkg/cmd/shared/handler/synonyms/synonyms.go | 41 ++++++- .../shared/handler/synonyms/synonyms_test.go | 9 +- pkg/cmd/synonyms/browse/browse_test.go | 18 ++- pkg/cmd/synonyms/delete/delete.go | 24 +++- pkg/cmd/synonyms/delete/delete_test.go | 10 +- pkg/cmd/synonyms/import/import.go | 17 ++- pkg/cmd/synonyms/save/save.go | 38 +++--- pkg/cmd/synonyms/save/save_test.go | 8 +- pkg/cmd/synonyms/shared/flags_to_synonym.go | 4 +- .../synonyms/shared/flags_to_synonym_test.go | 6 +- pkg/cmdutil/flags_completion.go | 38 ++++-- pkg/cmdutil/flags_completion_test.go | 7 +- pkg/cmdutil/json_flags.go | 5 +- pkg/cmdutil/print_flags.go | 18 ++- pkg/cmdutil/usage.go | 52 ++++++-- pkg/config/config.go | 4 +- pkg/gen/gen_flags.go | 2 +- pkg/httpmock/stub.go | 6 +- pkg/httpmock/v4/stub.go | 67 +++++++++++ pkg/iostreams/tty_size.go | 3 +- pkg/iostreams/tty_size_windows.go | 1 + pkg/open/open.go | 1 - pkg/printers/table_printer.go | 6 +- pkg/printers/template.go | 6 +- pkg/telemetry/telemetry.go | 6 +- pkg/utils/utils.go | 8 +- test/helpers.go | 7 +- 91 files changed, 1107 insertions(+), 510 deletions(-) create mode 100644 pkg/httpmock/v4/stub.go diff --git a/Taskfile.yml b/Taskfile.yml index 4aea624c..12bf24c3 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,26 +1,27 @@ version: 3 output: prefixed -vars: - # The URL to the "old" Algolia docs repo - docs_remote: https://github.com/algolia/doc.git - # The temporary folder for the generated YML files - docs_local: docs - cli_ref_path: app_data/cli/commands - yml_folder: tmp tasks: build: desc: Build the binary deps: [generate] - cmd: go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version={{ .VERSION }}" -o algolia cmd/algolia/main.go + cmds: + - go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version={{ .VERSION }}" -o algolia cmd/algolia/main.go vars: VERSION: main + generate: + desc: Generate command flags + internal: true + cmds: + - go generate ./... test: desc: Run unit tests run: always - cmd: go test ./... + cmds: + - go test ./... lint: desc: Lint code - cmd: golangci-lint run + cmds: + - golangci-lint run format: desc: Format code cmds: @@ -34,81 +35,3 @@ tasks: - build - test - lint - api-specs-pr: - desc: Create a new PR or update existing one for updating the Search API spec - summary: | - This task downloads the latest Search API OpenAPI spec from the api-clients-automation repo, - generates the flags, and makes a new PR to the CLI GitHub repo. - deps: [download-spec-file, generate] - preconditions: - - git status --porcelain - cmds: - - | - original="$(git branch --show-current)" - git checkout -B {{ .branch }} - git add . - git commit --message "chore: update search api spec" - git push --force --set-upstream origin {{ .branch }} - gh pr list --base main --head {{ .branch }} | grep -q . || gh pr create --title '{{ .pr-title }}' --description '{{ .pr-description }}' - git switch "${original}" - vars: - branch: feat/api-specs - pr-title: "chore: Update Search API spec" - pr-description: "Update Search API spec" - env: - GIT_COMMITTER_NAME: algolia-ci - GIT_AUTHOR_NAME: algolia-ci - GIT_COMMITTER_EMAIL: noreply@algolia.com - GIT_AUTHOR_EMAIL: noreply@algolia.com - download-spec-file: - desc: Download the latest Search API spec from GitHub - internal: true - cmd: curl -fsSL -o {{ .destination }} {{ .source }} - vars: - source: https://raw.githubusercontent.com/algolia/api-clients-automation/main/specs/bundled/search.yml - destination: ./api/specs/search.yml - generate: - desc: Generate command flags - internal: true - cmds: - - go generate ./... - update-docs: - desc: Update the CLI command reference in the Algolia docs - deps: - - clone-docs - - generate-command-reference - cmds: - - task: update-command-reference - - task: cleanup - clone-docs: - desc: Clone the Algolia docs - internal: true - cmd: git clone --depth=1 {{ .docs_remote }} {{ .docs_local }} - generate-command-reference: - desc: Generate updated YML files for the CLI command reference - internal: true - cmd: go run ./cmd/docs --app_data-path {{ .yml_folder }} - update-command-reference: - desc: Add the updated YML files to the docs - summary: | - This task clones the Algolia docs repo, - adds the updated CLI reference yml files to it, - and pushes a new PR to the GitHub repo. - internal: true - cmds: - - | - git -C {{ .docs_local }} checkout -B chore/cli-$(git rev-parse --short HEAD) - git -C {{ .docs_local }} rm "{{ .cli_ref_path }}/*.yml" - mkdir -p {{ .docs_local }}/{{ .cli_ref_path }} - mv {{ .yml_folder }}/*.yml {{ .docs_local }}/{{ .cli_ref_path }}/ - git -C {{ .docs_local }} add "{{ .cli_ref_path }}/*.yml" - git -C {{ .docs_local }} commit --message 'chore: Update CLI command reference' - env: - GIT_COMMITTER_NAME: algolia-ci - GIT_AUTHOR_NAME: algolia-ci - GIT_COMMITTER_EMAIL: noreply@algolia.com - GIT_AUTHOR_EMAIL: noreply@algolia.com - cleanup: - desc: Cleanup the docs files - internal: true - cmd: rm -rf {{ .docs_local }} {{ .yml_folder }} || true diff --git a/api/insights/client.go b/api/insights/client.go index f9833ffa..96bffeed 100644 --- a/api/insights/client.go +++ b/api/insights/client.go @@ -1,80 +1,71 @@ package insights import ( - "encoding/json" "fmt" + "net/http" "time" - algoliaInsights "github.com/algolia/algoliasearch-client-go/v4/algolia/insights" - "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" - "github.com/algolia/cli/pkg/version" + "github.com/algolia/algoliasearch-client-go/v3/algolia/call" + "github.com/algolia/algoliasearch-client-go/v3/algolia/compression" + _insights "github.com/algolia/algoliasearch-client-go/v3/algolia/insights" + "github.com/algolia/algoliasearch-client-go/v3/algolia/transport" ) -// Client wraps the default Insights API client so that we can declare methods on it +// Client provides methods to interact with the Algolia Insights API. type Client struct { - *algoliaInsights.APIClient + appID string + transport *transport.Transport } -// NewClient instantiates a new Insights API client -func NewClient(appID, apiKey string, region algoliaInsights.Region) (*Client, error) { - // Get the default user agent - userAgent, err := getUserAgentInfo(appID, apiKey, region, version.Version) - if err != nil { - return nil, err - } - if userAgent == "" { - return nil, fmt.Errorf("user agent info must not be empty") - } - clientConfig := algoliaInsights.InsightsConfiguration{ - Configuration: transport.Configuration{ - AppID: appID, - ApiKey: apiKey, - UserAgent: userAgent, +// NewClient instantiates a new client able to interact with the Algolia +// Insights API. +func NewClient(appID, apiKey string) *Client { + return NewClientWithConfig( + _insights.Configuration{ + AppID: appID, + APIKey: apiKey, }, - } - client, err := algoliaInsights.NewClientWithConfig(clientConfig) - if err != nil { - return nil, err - } - return &Client{client}, nil + ) } -// GetEvents retrieves a number of events from the Algolia Insights API. -func (c *Client) GetEvents(startDate, endDate time.Time, limit int) (*EventsRes, error) { - layout := "2006-01-02T15:04:05.000Z" - params := map[string]any{ - "startDate": startDate.Format(layout), - "endDate": endDate.Format(layout), - "limit": limit, - } - res, err := c.CustomGet(c.NewApiCustomGetRequest("1/events").WithParameters(params)) - if err != nil { - return nil, err - } - tmp, err := json.Marshal(res) - if err != nil { - return nil, err - } - var eventsRes EventsRes - err = json.Unmarshal(tmp, &eventsRes) - if err != nil { - return nil, err - } +// NewClientWithConfig instantiates a new client able to interact with the +// Algolia Insights API. +func NewClientWithConfig(config _insights.Configuration) *Client { + var hosts []*transport.StatefulHost - return &eventsRes, err -} + if config.Hosts == nil { + hosts = defaultHosts(config.Region) + } else { + for _, h := range config.Hosts { + hosts = append(hosts, transport.NewStatefulHost(h, call.IsReadWrite)) + } + } -// getUserAgentInfo returns the user agent string for the Insights client in the CLI -func getUserAgentInfo( - appID string, - apiKey string, - region algoliaInsights.Region, - appVersion string, -) (string, error) { - client, err := algoliaInsights.NewClient(appID, apiKey, region) - if err != nil { - return "", err + return &Client{ + appID: config.AppID, + transport: transport.New( + hosts, + config.Requester, + config.AppID, + config.APIKey, + config.ReadTimeout, + config.WriteTimeout, + config.Headers, + config.ExtraUserAgent, + compression.None, + ), } +} - return client.GetConfiguration().UserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), nil +// FetchEvents retrieves events from the Algolia Insights API. +func (c *Client) FetchEvents(startDate, endDate time.Time, limit int) (EventsRes, error) { + var res EventsRes + path := fmt.Sprintf( + "/1/events?startDate=%s&endDate=%s&limit=%d", + startDate.Format("2006-01-02T15:04:05.000Z"), + endDate.Format("2006-01-02T15:04:05.000Z"), + limit, + ) + err := c.transport.Request(&res, http.MethodGet, path, nil, call.Read, nil) + return res, err } diff --git a/api/insights/utils.go b/api/insights/utils.go index fc132f2e..a02e3881 100644 --- a/api/insights/utils.go +++ b/api/insights/utils.go @@ -11,7 +11,10 @@ import ( func defaultHosts(r region.Region) (hosts []*transport.StatefulHost) { switch r { case region.DE, region.US: - hosts = append(hosts, transport.NewStatefulHost(fmt.Sprintf("insights.%s.algolia.io", r), call.IsReadWrite)) + hosts = append( + hosts, + transport.NewStatefulHost(fmt.Sprintf("insights.%s.algolia.io", r), call.IsReadWrite), + ) default: hosts = append(hosts, transport.NewStatefulHost("insights.algolia.io", call.IsReadWrite)) } diff --git a/cmd/docs/main.go b/cmd/docs/main.go index c06c8b6e..d9476351 100644 --- a/cmd/docs/main.go +++ b/cmd/docs/main.go @@ -23,7 +23,12 @@ func main() { func run(args []string) error { flags := pflag.NewFlagSet("", pflag.ContinueOnError) - dir := flags.StringP("app_data-path", "", "", "Path directory where you want generate documentation data files") + dir := flags.StringP( + "app_data-path", + "", + "", + "Path directory where you want generate documentation data files", + ) help := flags.BoolP("help", "h", false, "Help about any command") target := flags.StringP("target", "T", "old", "target old or new documentation website") @@ -51,7 +56,7 @@ func run(args []string) error { }) rootCmd.InitDefaultHelpCmd() - if err := os.MkdirAll(*dir, 0755); err != nil { + if err := os.MkdirAll(*dir, 0o755); err != nil { return err } @@ -66,5 +71,4 @@ func run(args []string) error { } return nil - } diff --git a/devbox.json b/devbox.json index 0d4bde6d..7aff8ab8 100644 --- a/devbox.json +++ b/devbox.json @@ -5,7 +5,6 @@ "go@1.23.4", "golangci-lint@1.63.4", "gofumpt@latest", - "golines@latest", - "gh@latest" + "golines@latest" ] } diff --git a/internal/analyze/analyze.go b/internal/analyze/analyze.go index 8d83796a..11abe641 100644 --- a/internal/analyze/analyze.go +++ b/internal/analyze/analyze.go @@ -47,7 +47,13 @@ type Stats struct { } // ComputeStats computes the stats for the given index. -func ComputeStats(i iterator.Iterator, s search.Settings, limit int, only string, counter chan int) (*Stats, error) { +func ComputeStats( + i iterator.Iterator, + s search.Settings, + limit int, + only string, + counter chan int, +) (*Stats, error) { settingsMap := settingsAsMap(s) stats := &Stats{ Attributes: make(map[string]*AttributeStats), diff --git a/internal/docs/docs.go b/internal/docs/docs.go index 6b0cb22d..e228f8a9 100644 --- a/internal/docs/docs.go +++ b/internal/docs/docs.go @@ -83,7 +83,6 @@ func newCommand(cmd *cobra.Command) Command { if categoryFlagSet.Print.HasAvailableFlags() { flags["Output formatting flags"] = newFlags(categoryFlagSet.Print) - } command.Flags = flags diff --git a/internal/update/update.go b/internal/update/update.go index 3a6ff661..be3814d6 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -33,7 +33,10 @@ type StateEntry struct { } // CheckForUpdate checks whether this software has had a newer release on GitHub -func CheckForUpdate(client *http.Client, stateFilePath, currentVersion string) (*ReleaseInfo, error) { +func CheckForUpdate( + client *http.Client, + stateFilePath, currentVersion string, +) (*ReleaseInfo, error) { stateEntry, _ := getStateEntry(stateFilePath) if stateEntry != nil && time.Since(stateEntry.CheckedForUpdateAt).Hours() < 24 { return nil, nil @@ -97,12 +100,12 @@ func setStateEntry(stateFilePath string, t time.Time, r ReleaseInfo) error { return err } - err = os.MkdirAll(filepath.Dir(stateFilePath), 0755) + err = os.MkdirAll(filepath.Dir(stateFilePath), 0o755) if err != nil { return err } - err = os.WriteFile(stateFilePath, content, 0600) + err = os.WriteFile(stateFilePath, content, 0o600) return err } diff --git a/pkg/ask/ask.go b/pkg/ask/ask.go index db1c989f..b72eb662 100644 --- a/pkg/ask/ask.go +++ b/pkg/ask/ask.go @@ -16,7 +16,12 @@ func (my *StringSlice) WriteAnswer(name string, value interface{}) error { return nil } -func AskCommaSeparatedInputQuestion(message string, storage *[]string, defaultValues []string, opts ...survey.AskOpt) error { +func AskCommaSeparatedInputQuestion( + message string, + storage *[]string, + defaultValues []string, + opts ...survey.AskOpt, +) error { stringSlice := StringSlice{} err := survey.AskOne( &survey.Input{ @@ -30,7 +35,13 @@ func AskCommaSeparatedInputQuestion(message string, storage *[]string, defaultVa return err } -func AskMultiSelectQuestion(message string, defaultValues []string, storage *[]string, options []string, opts ...survey.AskOpt) error { +func AskMultiSelectQuestion( + message string, + defaultValues []string, + storage *[]string, + options []string, + opts ...survey.AskOpt, +) error { err := survey.AskOne( &survey.MultiSelect{ Message: message, @@ -43,7 +54,13 @@ func AskMultiSelectQuestion(message string, defaultValues []string, storage *[]s return err } -func AskSelectQuestion(message string, storage *string, options []string, defaultValue string, opts ...survey.AskOpt) error { +func AskSelectQuestion( + message string, + storage *string, + options []string, + defaultValue string, + opts ...survey.AskOpt, +) error { return survey.AskOne(&survey.Select{ Message: message, Options: options, @@ -51,14 +68,25 @@ func AskSelectQuestion(message string, storage *string, options []string, defaul }, storage, opts...) } -func AskInputQuestion(message string, storage *string, defaultValue string, opts ...survey.AskOpt) error { +func AskInputQuestion( + message string, + storage *string, + defaultValue string, + opts ...survey.AskOpt, +) error { return survey.AskOne(&survey.Input{ Message: message, Default: defaultValue, }, storage, opts...) } -func AskInputQuestionWithSuggestion(message string, storage *string, defaultValue string, suggest func(toComplete string) []string, opts ...survey.AskOpt) error { +func AskInputQuestionWithSuggestion( + message string, + storage *string, + defaultValue string, + suggest func(toComplete string) []string, + opts ...survey.AskOpt, +) error { return survey.AskOne(&survey.Input{ Message: message, Default: defaultValue, @@ -66,7 +94,12 @@ func AskInputQuestionWithSuggestion(message string, storage *string, defaultValu }, storage, opts...) } -func AskBooleanQuestion(message string, storage *bool, defaultValue bool, opts ...survey.AskOpt) error { +func AskBooleanQuestion( + message string, + storage *bool, + defaultValue bool, + opts ...survey.AskOpt, +) error { return survey.AskOne(&survey.Confirm{ Message: message, Default: defaultValue, diff --git a/pkg/auth/auth_check.go b/pkg/auth/auth_check.go index 5186eec9..52df4fdc 100644 --- a/pkg/auth/auth_check.go +++ b/pkg/auth/auth_check.go @@ -29,16 +29,15 @@ var WriteAPIKeyDefaultACLs = []string{ // errMissingACLs return an error with the missing ACLs func errMissingACLs(missing []string) error { - err := fmt.Sprintf("Missing API key ACL(s): %s\n", strings.Join(missing, ", ")) - err += "Edit your profile or use the `--api-key` flag to provide an API key with the missing ACLs.\n" - err += "See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information" - + err := fmt.Sprintf("Missing API Key ACL(s): %s\n", strings.Join(missing, ", ")) + err += "Either edit your profile or use the `--api-key` flag to provide an API Key with the missing ACLs.\n" + err += "See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information.\n" return errors.New(err) } // errAdminAPIKeyRequired is returned when the command requires an admin API Key var errAdminAPIKeyRequired = errors.New( - "this command requires an admin API key. Use the `--api-key` flag with a valid admin API key", + "This command requires an admin API Key. Please use the `--api-key` flag to provide a valid admin API Key.\n", ) func DisableAuthCheck(cmd *cobra.Command) { @@ -82,7 +81,7 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { if err != nil { return err } - _, err = client.ListApiKeys() + _, err = client.ListAPIKeys() if err == nil { return nil // Admin API Key, no need to check ACLs } @@ -93,21 +92,12 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { } // Check the ACLs of the provided API Key - key, err := f.Config.Profile().GetAPIKey() - if err != nil { - return err - } - apiKey, err := client.GetApiKey(client.NewApiGetApiKeyRequest(key)) + apiKey, err := client.GetAPIKey(f.Config.Profile().GetAPIKey()) if err != nil { return err } - var hasAcls []string - for _, acl := range apiKey.Acl { - hasAcls = append(hasAcls, string(acl)) - } - - missingACLs := utils.Differences(neededACLs, hasAcls) + missingACLs := utils.Differences(neededACLs, apiKey.ACL) if len(missingACLs) > 0 { return errMissingACLs(missingACLs) } diff --git a/pkg/auth/auth_check_test.go b/pkg/auth/auth_check_test.go index 60cc790a..dd82354f 100644 --- a/pkg/auth/auth_check_test.go +++ b/pkg/auth/auth_check_test.go @@ -113,5 +113,4 @@ See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restriction } }) } - } diff --git a/pkg/cmd/apikeys/create/create.go b/pkg/cmd/apikeys/create/create.go index aac5f274..e39436f7 100644 --- a/pkg/cmd/apikeys/create/create.go +++ b/pkg/cmd/apikeys/create/create.go @@ -99,21 +99,27 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co Used for informative purposes only. It has no impact on the functionality of the API key.`, )) - _ = cmd.RegisterFlagCompletionFunc("indices", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - client, err := f.SearchClient() - if err != nil { - return nil, cobra.ShellCompDirectiveError - } - indicesRes, err := client.ListIndices() - if err != nil { - return nil, cobra.ShellCompDirectiveError - } - allowedIndices := make([]string, 0, len(indicesRes.Items)) - for _, index := range indicesRes.Items { - allowedIndices = append(allowedIndices, fmt.Sprintf("%s\t%s records", index.Name, humanize.Comma(index.Entries))) - } - return allowedIndices, cobra.ShellCompDirectiveNoFileComp - }) + _ = cmd.RegisterFlagCompletionFunc( + "indices", + func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + client, err := f.SearchClient() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + indicesRes, err := client.ListIndices() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + allowedIndices := make([]string, 0, len(indicesRes.Items)) + for _, index := range indicesRes.Items { + allowedIndices = append( + allowedIndices, + fmt.Sprintf("%s\t%s records", index.Name, humanize.Comma(index.Entries)), + ) + } + return allowedIndices, cobra.ShellCompDirectiveNoFileComp + }, + ) _ = cmd.RegisterFlagCompletionFunc("acl", cmdutil.StringSliceCompletionFunc(map[string]string{ diff --git a/pkg/cmd/apikeys/create/create_test.go b/pkg/cmd/apikeys/create/create_test.go index cb8fd1ca..778dedad 100644 --- a/pkg/cmd/apikeys/create/create_test.go +++ b/pkg/cmd/apikeys/create/create_test.go @@ -105,7 +105,10 @@ func Test_runCreateCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/keys"), httpmock.JSONResponse(search.CreateKeyRes{Key: "foo"})) + r.Register( + httpmock.REST("POST", "1/keys"), + httpmock.JSONResponse(search.CreateKeyRes{Key: "foo"}), + ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") cmd := NewCreateCmd(f, nil) diff --git a/pkg/cmd/apikeys/delete/delete.go b/pkg/cmd/apikeys/delete/delete.go index 05eccfc1..0932fcf1 100644 --- a/pkg/cmd/apikeys/delete/delete.go +++ b/pkg/cmd/apikeys/delete/delete.go @@ -45,7 +45,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts.APIKey = args[0] if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -77,7 +79,10 @@ func runDeleteCmd(opts *DeleteOptions) error { if opts.DoConfirm { var confirmed bool - err = prompt.Confirm(fmt.Sprintf("Delete the following API key: %s?", opts.APIKey), &confirmed) + err = prompt.Confirm( + fmt.Sprintf("Delete the following API key: %s?", opts.APIKey), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -93,7 +98,12 @@ func runDeleteCmd(opts *DeleteOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s API key successfully deleted: %s\n", cs.SuccessIcon(), opts.APIKey) + fmt.Fprintf( + opts.IO.Out, + "%s API key successfully deleted: %s\n", + cs.SuccessIcon(), + opts.APIKey, + ) } return nil } diff --git a/pkg/cmd/apikeys/delete/delete_test.go b/pkg/cmd/apikeys/delete/delete_test.go index a5fa6e57..5ddc68b9 100644 --- a/pkg/cmd/apikeys/delete/delete_test.go +++ b/pkg/cmd/apikeys/delete/delete_test.go @@ -103,8 +103,14 @@ func Test_runDeleteCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("GET", fmt.Sprintf("1/keys/%s", tt.key)), httpmock.JSONResponse(search.Key{Value: "foo"})) - r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/keys/%s", tt.key)), httpmock.JSONResponse(search.DeleteKeyRes{})) + r.Register( + httpmock.REST("GET", fmt.Sprintf("1/keys/%s", tt.key)), + httpmock.JSONResponse(search.Key{Value: "foo"}), + ) + r.Register( + httpmock.REST("DELETE", fmt.Sprintf("1/keys/%s", tt.key)), + httpmock.JSONResponse(search.DeleteKeyRes{}), + ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") cmd := NewDeleteCmd(f, nil) diff --git a/pkg/cmd/crawler/crawl/crawl.go b/pkg/cmd/crawler/crawl/crawl.go index c04306ea..a0f86513 100644 --- a/pkg/cmd/crawler/crawl/crawl.go +++ b/pkg/cmd/crawler/crawl/crawl.go @@ -86,7 +86,13 @@ func runCrawlCmd(opts *CrawlOptions) error { } cs := opts.IO.ColorScheme() - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Requesting crawl for %s on crawler %s", utils.Pluralize(len(opts.URLs), "URL"), opts.ID)) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf( + "Requesting crawl for %s on crawler %s", + utils.Pluralize(len(opts.URLs), "URL"), + opts.ID, + ), + ) _, err = client.CrawlURLs(opts.ID, opts.URLs, opts.Save, opts.SaveSpecified) opts.IO.StopProgressIndicator() if err != nil { @@ -94,7 +100,13 @@ func runCrawlCmd(opts *CrawlOptions) error { } if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully requested crawl for %s on crawler %s\n", cs.SuccessIconWithColor(cs.Green), utils.Pluralize(len(opts.URLs), "URL"), opts.ID) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully requested crawl for %s on crawler %s\n", + cs.SuccessIconWithColor(cs.Green), + utils.Pluralize(len(opts.URLs), "URL"), + opts.ID, + ) } return nil diff --git a/pkg/cmd/crawler/crawl/crawl_test.go b/pkg/cmd/crawler/crawl/crawl_test.go index d45c248d..1fb0e655 100644 --- a/pkg/cmd/crawler/crawl/crawl_test.go +++ b/pkg/cmd/crawler/crawl/crawl_test.go @@ -154,7 +154,10 @@ func Test_runCrawlCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.wantErr == "" { - r.Register(httpmock.REST("POST", "api/1/crawlers/"+tt.id+"/urls/crawl"), httpmock.JSONResponse(crawler.TaskIDResponse{TaskID: "taskID"})) + r.Register( + httpmock.REST("POST", "api/1/crawlers/"+tt.id+"/urls/crawl"), + httpmock.JSONResponse(crawler.TaskIDResponse{TaskID: "taskID"}), + ) } else { r.Register(httpmock.REST("POST", "api/1/crawlers/"+tt.id+"/urls/crawl"), httpmock.ErrorResponseWithBody(crawler.ErrResponse{Err: crawler.Err{Code: "not-found", Message: "Crawler not-found not found"}})) } diff --git a/pkg/cmd/crawler/create/create.go b/pkg/cmd/crawler/create/create.go index cd55c41c..d7dad3ff 100644 --- a/pkg/cmd/crawler/create/create.go +++ b/pkg/cmd/crawler/create/create.go @@ -67,7 +67,8 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co }, } - cmd.Flags().StringVarP(&configFile, "file", "F", "", "Path to the configuration file (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&configFile, "file", "F", "", "Path to the configuration file (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") return cmd @@ -88,7 +89,13 @@ func runCreateCmd(opts *CreateOptions) error { } if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Crawler %s created: %s\n", cs.SuccessIconWithColor(cs.Green), cs.Bold(opts.Name), cs.Bold(id)) + fmt.Fprintf( + opts.IO.Out, + "%s Crawler %s created: %s\n", + cs.SuccessIconWithColor(cs.Green), + cs.Bold(opts.Name), + cs.Bold(id), + ) } return nil diff --git a/pkg/cmd/crawler/create/create_test.go b/pkg/cmd/crawler/create/create_test.go index a4d6d357..97f318cf 100644 --- a/pkg/cmd/crawler/create/create_test.go +++ b/pkg/cmd/crawler/create/create_test.go @@ -19,7 +19,7 @@ import ( func TestNewCreateCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "config.json") - err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0600) + err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0o600) require.NoError(t, err) tests := []struct { @@ -81,7 +81,7 @@ func TestNewCreateCmd(t *testing.T) { func Test_runCreateCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "config.json") - err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0600) + err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0o600) require.NoError(t, err) tests := []struct { diff --git a/pkg/cmd/crawler/get/get.go b/pkg/cmd/crawler/get/get.go index 27cc3eb1..fb8037f7 100644 --- a/pkg/cmd/crawler/get/get.go +++ b/pkg/cmd/crawler/get/get.go @@ -57,7 +57,8 @@ func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command }, } - cmd.Flags().BoolVarP(&opts.ConfigOnly, "config-only", "c", false, "Display only the crawler configuration") + cmd.Flags(). + BoolVarP(&opts.ConfigOnly, "config-only", "c", false, "Display only the crawler configuration") return cmd } diff --git a/pkg/cmd/crawler/pause/pause.go b/pkg/cmd/crawler/pause/pause.go index 766a9f3a..278febe1 100644 --- a/pkg/cmd/crawler/pause/pause.go +++ b/pkg/cmd/crawler/pause/pause.go @@ -64,7 +64,9 @@ func runPauseCmd(opts *PauseOptions) error { } cs := opts.IO.ColorScheme() - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Pausing %s", utils.Pluralize(len(opts.IDs), "crawler"))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Pausing %s", utils.Pluralize(len(opts.IDs), "crawler")), + ) for _, id := range opts.IDs { if _, err := client.Reindex(id); err != nil { opts.IO.StopProgressIndicator() @@ -74,7 +76,12 @@ func runPauseCmd(opts *PauseOptions) error { opts.IO.StopProgressIndicator() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s %s\n", cs.SuccessIconWithColor(cs.Green), fmt.Sprintf("Successfully paused %s", utils.Pluralize(len(opts.IDs), "crawler"))) + fmt.Fprintf( + opts.IO.Out, + "%s %s\n", + cs.SuccessIconWithColor(cs.Green), + fmt.Sprintf("Successfully paused %s", utils.Pluralize(len(opts.IDs), "crawler")), + ) } return nil diff --git a/pkg/cmd/crawler/reindex/reindex.go b/pkg/cmd/crawler/reindex/reindex.go index a4dc25cf..26f3becb 100644 --- a/pkg/cmd/crawler/reindex/reindex.go +++ b/pkg/cmd/crawler/reindex/reindex.go @@ -64,7 +64,9 @@ func runReindexCmd(opts *ReindexOptions) error { } cs := opts.IO.ColorScheme() - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Reindexing %s", utils.Pluralize(len(opts.IDs), "crawler"))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Reindexing %s", utils.Pluralize(len(opts.IDs), "crawler")), + ) for _, id := range opts.IDs { if _, err := client.Reindex(id); err != nil { opts.IO.StopProgressIndicator() @@ -74,7 +76,15 @@ func runReindexCmd(opts *ReindexOptions) error { opts.IO.StopProgressIndicator() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s %s\n", cs.SuccessIconWithColor(cs.Green), fmt.Sprintf("Successfully requested reindexing for %s", utils.Pluralize(len(opts.IDs), "crawler"))) + fmt.Fprintf( + opts.IO.Out, + "%s %s\n", + cs.SuccessIconWithColor(cs.Green), + fmt.Sprintf( + "Successfully requested reindexing for %s", + utils.Pluralize(len(opts.IDs), "crawler"), + ), + ) } return nil diff --git a/pkg/cmd/crawler/run/run.go b/pkg/cmd/crawler/run/run.go index 9429f068..c88b27aa 100644 --- a/pkg/cmd/crawler/run/run.go +++ b/pkg/cmd/crawler/run/run.go @@ -68,7 +68,12 @@ func runRunCmd(opts *RunOptions) error { } if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Crawler %s started\n", cs.SuccessIconWithColor(cs.Green), opts.ID) + fmt.Fprintf( + opts.IO.Out, + "%s Crawler %s started\n", + cs.SuccessIconWithColor(cs.Green), + opts.ID, + ) } return nil diff --git a/pkg/cmd/crawler/test/test.go b/pkg/cmd/crawler/test/test.go index b01a0d53..7d0135c3 100644 --- a/pkg/cmd/crawler/test/test.go +++ b/pkg/cmd/crawler/test/test.go @@ -79,7 +79,8 @@ func NewTestCmd(f *cmdutil.Factory, runF func(*TestOptions) error) *cobra.Comman cmd.Flags().StringVarP(&opts.URL, "url", "u", "", "The URL to test.") _ = cmd.MarkFlagRequired("url") - cmd.Flags().StringVarP(&configFile, "config", "F", "", "The configuration file to use to override the crawler's configuration. (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&configFile, "config", "F", "", "The configuration file to use to override the crawler's configuration. (use \"-\" to read from standard input)") return cmd } @@ -91,7 +92,9 @@ func runTestCmd(opts *TestOptions) error { } cs := opts.IO.ColorScheme() - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Testing URL %s on crawler %s", cs.Bold(opts.URL), cs.Bold(opts.ID))) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Testing URL %s on crawler %s", cs.Bold(opts.URL), cs.Bold(opts.ID)), + ) res, err := client.Test(opts.ID, opts.URL, opts.config) if err != nil { opts.IO.StopProgressIndicator() diff --git a/pkg/cmd/crawler/unblock/unblock.go b/pkg/cmd/crawler/unblock/unblock.go index b64bfb1b..387eac95 100644 --- a/pkg/cmd/crawler/unblock/unblock.go +++ b/pkg/cmd/crawler/unblock/unblock.go @@ -56,7 +56,9 @@ func NewUnblockCmd(f *cmdutil.Factory, runF func(*UnblockOptions) error) *cobra. if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -86,7 +88,14 @@ func runUnblockCmd(opts *UnblockOptions) error { if opts.DoConfirm { var confirmed bool - err := prompt.Confirm(fmt.Sprintf("Are you sure you want to unblock the crawler %q? \nBlocking error is: %s", opts.ID, crawler.BlockingError), &confirmed) + err := prompt.Confirm( + fmt.Sprintf( + "Are you sure you want to unblock the crawler %q? \nBlocking error is: %s", + opts.ID, + crawler.BlockingError, + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } diff --git a/pkg/cmd/dictionary/entries/browse/browse.go b/pkg/cmd/dictionary/entries/browse/browse.go index 8c1d40c4..d7705aac 100644 --- a/pkg/cmd/dictionary/entries/browse/browse.go +++ b/pkg/cmd/dictionary/entries/browse/browse.go @@ -77,11 +77,17 @@ func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co `), RunE: func(cmd *cobra.Command, args []string) error { if opts.All && len(args) > 0 || !opts.All && len(args) == 0 { - return cmdutil.FlagErrorf("Either specify dictionaries' names or use --all to browse all dictionaries") + return cmdutil.FlagErrorf( + "Either specify dictionaries' names or use --all to browse all dictionaries", + ) } if opts.All { - opts.Dictionaries = []search.DictionaryName{search.Stopwords, search.Plurals, search.Compounds} + opts.Dictionaries = []search.DictionaryName{ + search.Stopwords, + search.Plurals, + search.Compounds, + } } else { opts.Dictionaries = make([]search.DictionaryName, len(args)) for i, dictionary := range args { @@ -94,7 +100,8 @@ func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co } cmd.Flags().BoolVarP(&opts.All, "all", "a", false, "browse all dictionaries") - cmd.Flags().BoolVarP(&opts.IncludeDefaultStopwords, "include-defaults", "d", false, "include default stopwords") + cmd.Flags(). + BoolVarP(&opts.IncludeDefaultStopwords, "include-defaults", "d", false, "include default stopwords") opts.PrintFlags.AddFlags(cmd) @@ -122,7 +129,12 @@ func runBrowseCmd(opts *BrowseOptions) error { // implement infinite pagination for pageCount < maxPages { - res, err := client.SearchDictionaryEntries(dictionary, "", opt.HitsPerPage(1000), opt.Page(pageCount)) + res, err := client.SearchDictionaryEntries( + dictionary, + "", + opt.HitsPerPage(1000), + opt.Page(pageCount), + ) if err != nil { return err } @@ -131,13 +143,19 @@ func runBrowseCmd(opts *BrowseOptions) error { data, err := json.Marshal(res.Hits) if err != nil { - return fmt.Errorf("cannot unmarshal dictionary entries: error while marshalling original dictionary entries: %v", err) + return fmt.Errorf( + "cannot unmarshal dictionary entries: error while marshalling original dictionary entries: %v", + err, + ) } var entries []DictionaryEntry err = json.Unmarshal(data, &entries) if err != nil { - return fmt.Errorf("cannot unmarshal dictionary entries: error while unmarshalling original dictionary entries: %v", err) + return fmt.Errorf( + "cannot unmarshal dictionary entries: error while unmarshalling original dictionary entries: %v", + err, + ) } if len(entries) != 0 { diff --git a/pkg/cmd/dictionary/entries/browse/browse_test.go b/pkg/cmd/dictionary/entries/browse/browse_test.go index dc445def..7fce0311 100644 --- a/pkg/cmd/dictionary/entries/browse/browse_test.go +++ b/pkg/cmd/dictionary/entries/browse/browse_test.go @@ -85,10 +85,16 @@ func Test_runBrowseCmd(t *testing.T) { if tt.entries { entries = append(entries, DictionaryEntry{Type: "custom"}) } - r.Register(httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), httpmock.JSONResponse(search.SearchDictionariesRes{ - Hits: entries, - })) - r.Register(httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), httpmock.JSONResponse(search.TaskStatusRes{})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), + httpmock.JSONResponse(search.SearchDictionariesRes{ + Hits: entries, + }), + ) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), + httpmock.JSONResponse(search.TaskStatusRes{}), + ) } f, out := test.NewFactory(tt.isTTY, &r, nil, "") diff --git a/pkg/cmd/dictionary/entries/clear/clear.go b/pkg/cmd/dictionary/entries/clear/clear.go index 7c8513a5..dd616a3c 100644 --- a/pkg/cmd/dictionary/entries/clear/clear.go +++ b/pkg/cmd/dictionary/entries/clear/clear.go @@ -70,11 +70,17 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm `), RunE: func(cmd *cobra.Command, args []string) error { if opts.All && len(args) > 0 || !opts.All && len(args) == 0 { - return cmdutil.FlagErrorf("Either specify dictionaries' names or use --all to clear all dictionaries") + return cmdutil.FlagErrorf( + "Either specify dictionaries' names or use --all to clear all dictionaries", + ) } if opts.All { - opts.Dictionaries = []search.DictionaryName{search.Stopwords, search.Plurals, search.Compounds} + opts.Dictionaries = []search.DictionaryName{ + search.Stopwords, + search.Plurals, + search.Compounds, + } } else { opts.Dictionaries = make([]search.DictionaryName, len(args)) for i, dictionary := range args { @@ -84,7 +90,9 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -137,7 +145,14 @@ func runClearCmd(opts *ClearOptions) error { if opts.DoConfirm { var confirmed bool - err = prompt.Confirm(fmt.Sprintf("Clear %d entries from %s dictionary?", totalEntries, utils.SliceToReadableString(dictionariesNames)), &confirmed) + err = prompt.Confirm( + fmt.Sprintf( + "Clear %d entries from %s dictionary?", + totalEntries, + utils.SliceToReadableString(dictionariesNames), + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -154,7 +169,13 @@ func runClearCmd(opts *ClearOptions) error { } if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully cleared %d entries from %s dictionary\n", cs.SuccessIcon(), totalEntries, utils.SliceToReadableString(dictionariesNames)) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully cleared %d entries from %s dictionary\n", + cs.SuccessIcon(), + totalEntries, + utils.SliceToReadableString(dictionariesNames), + ) } return nil @@ -167,13 +188,19 @@ func customEntriesNb(client *search.Client, dictionary search.DictionaryName) (i } data, err := json.Marshal(res.Hits) if err != nil { - return 0, fmt.Errorf("cannot unmarshal dictionary entries: error while marshalling original dictionary entries: %v", err) + return 0, fmt.Errorf( + "cannot unmarshal dictionary entries: error while marshalling original dictionary entries: %v", + err, + ) } var entries []DictionaryEntry err = json.Unmarshal(data, &entries) if err != nil { - return 0, fmt.Errorf("cannot unmarshal dictionary entries: error while unmarshalling original dictionary entries: %v", err) + return 0, fmt.Errorf( + "cannot unmarshal dictionary entries: error while unmarshalling original dictionary entries: %v", + err, + ) } var customEntriesNb int diff --git a/pkg/cmd/dictionary/entries/clear/clear_test.go b/pkg/cmd/dictionary/entries/clear/clear_test.go index 5a5c946e..daf75c08 100644 --- a/pkg/cmd/dictionary/entries/clear/clear_test.go +++ b/pkg/cmd/dictionary/entries/clear/clear_test.go @@ -184,10 +184,16 @@ func Test_runDeleteCmd(t *testing.T) { if tt.entries { entries = append(entries, DictionaryEntry{Type: "custom"}) } - r.Register(httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), httpmock.JSONResponse(search.SearchDictionariesRes{ - Hits: entries, - })) - r.Register(httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), httpmock.JSONResponse(search.TaskStatusRes{})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), + httpmock.JSONResponse(search.SearchDictionariesRes{ + Hits: entries, + }), + ) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), + httpmock.JSONResponse(search.TaskStatusRes{}), + ) } f, out := test.NewFactory(tt.isTTY, &r, nil, "") diff --git a/pkg/cmd/dictionary/entries/delete/delete.go b/pkg/cmd/dictionary/entries/delete/delete.go index 357f56df..ce19c79c 100644 --- a/pkg/cmd/dictionary/entries/delete/delete.go +++ b/pkg/cmd/dictionary/entries/delete/delete.go @@ -62,7 +62,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts.Dictionary = search.DictionaryName(args[0]) if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -92,7 +94,14 @@ func runDeleteCmd(opts *DeleteOptions) error { if opts.DoConfirm { var confirmed bool - err = prompt.Confirm(fmt.Sprintf("Delete the %s from %s?", pluralizeEntry(len(opts.ObjectIDs)), opts.Dictionary), &confirmed) + err = prompt.Confirm( + fmt.Sprintf( + "Delete the %s from %s?", + pluralizeEntry(len(opts.ObjectIDs)), + opts.Dictionary, + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -108,7 +117,13 @@ func runDeleteCmd(opts *DeleteOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), pluralizeEntry(len(opts.ObjectIDs)), opts.Dictionary) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully deleted %s from %s\n", + cs.SuccessIcon(), + pluralizeEntry(len(opts.ObjectIDs)), + opts.Dictionary, + ) } return nil diff --git a/pkg/cmd/dictionary/entries/delete/delete_test.go b/pkg/cmd/dictionary/entries/delete/delete_test.go index 812c6a42..e7c55f8a 100644 --- a/pkg/cmd/dictionary/entries/delete/delete_test.go +++ b/pkg/cmd/dictionary/entries/delete/delete_test.go @@ -158,9 +158,18 @@ func Test_runDeleteCmd(t *testing.T) { // test is flaky since there's no guarantee of obtaining the right object using a search by objectID for _, id := range tt.objectIDs { - r.Register(httpmock.REST("GET", fmt.Sprintf("1/dictionaries/%s/search?query=%s", tt.dictionary, id)), httpmock.JSONResponse(search.SearchDictionariesRes{})) + r.Register( + httpmock.REST( + "GET", + fmt.Sprintf("1/dictionaries/%s/search?query=%s", tt.dictionary, id), + ), + httpmock.JSONResponse(search.SearchDictionariesRes{}), + ) } - r.Register(httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", tt.dictionary)), httpmock.JSONResponse(search.TaskStatusRes{})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", tt.dictionary)), + httpmock.JSONResponse(search.TaskStatusRes{}), + ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") cmd := NewDeleteCmd(f, nil) diff --git a/pkg/cmd/dictionary/entries/import/import_test.go b/pkg/cmd/dictionary/entries/import/import_test.go index 089461d4..f1a4f156 100644 --- a/pkg/cmd/dictionary/entries/import/import_test.go +++ b/pkg/cmd/dictionary/entries/import/import_test.go @@ -17,7 +17,13 @@ import ( func Test_runImportCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "entries.json") - err := os.WriteFile(tmpFile, []byte(`{"language":"en","word":"test","state":"enabled","objectID":"test","type":"custom"}`), 0600) + err := os.WriteFile( + tmpFile, + []byte( + `{"language":"en","word":"test","state":"enabled","objectID":"test","type":"custom"}`, + ), + 0o600, + ) require.NoError(t, err) tests := []struct { @@ -80,7 +86,10 @@ func Test_runImportCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.wantErr == "" { - r.Register(httpmock.REST("POST", "1/dictionaries/stopwords/batch"), httpmock.JSONResponse(search.MultipleBatchRes{})) + r.Register( + httpmock.REST("POST", "1/dictionaries/stopwords/batch"), + httpmock.JSONResponse(search.MultipleBatchRes{}), + ) } defer r.Verify(t) diff --git a/pkg/cmd/dictionary/settings/set/set.go b/pkg/cmd/dictionary/settings/set/set.go index 877349f3..c2a73674 100644 --- a/pkg/cmd/dictionary/settings/set/set.go +++ b/pkg/cmd/dictionary/settings/set/set.go @@ -58,20 +58,29 @@ func NewSetCmd(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command `), RunE: func(cmd *cobra.Command, args []string) error { // Check that either --disable-standard-entries and --enable-standard-entries or --reset-standard-entries is set - if !opts.ResetStandardEntries && (len(opts.DisableStandardEntries) == 0 && len(opts.EnableStandardEntries) == 0) { - return cmdutil.FlagErrorf("Either --disable-standard-entries and/or --enable-standard-entries or --reset-standard-entries must be set") + if !opts.ResetStandardEntries && + (len(opts.DisableStandardEntries) == 0 && len(opts.EnableStandardEntries) == 0) { + return cmdutil.FlagErrorf( + "Either --disable-standard-entries and/or --enable-standard-entries or --reset-standard-entries must be set", + ) } // Check that the user is not resetting standard entries and trying to disable or enable standard entries at the same time - if opts.ResetStandardEntries && (len(opts.DisableStandardEntries) > 0 || len(opts.EnableStandardEntries) > 0) { - return cmdutil.FlagErrorf("You cannot reset standard entries and disable or enable standard entries at the same time") + if opts.ResetStandardEntries && + (len(opts.DisableStandardEntries) > 0 || len(opts.EnableStandardEntries) > 0) { + return cmdutil.FlagErrorf( + "You cannot reset standard entries and disable or enable standard entries at the same time", + ) } // Check if the user is trying to disable and enable standard entries for the same languages at the same time for _, disableLanguage := range opts.DisableStandardEntries { for _, enableLanguage := range opts.EnableStandardEntries { if disableLanguage == enableLanguage { - return cmdutil.FlagErrorf("You cannot disable and enable standard entries for the same language: %s", disableLanguage) + return cmdutil.FlagErrorf( + "You cannot disable and enable standard entries for the same language: %s", + disableLanguage, + ) } } } @@ -84,16 +93,25 @@ func NewSetCmd(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command }, } - cmd.Flags().StringSliceVarP(&opts.DisableStandardEntries, "disable-standard-entries", "d", []string{}, "Disable standard entries for the given languages") - cmd.Flags().StringSliceVarP(&opts.EnableStandardEntries, "enable-standard-entries", "e", []string{}, "Enable standard entries for the given languages") - cmd.Flags().BoolVarP(&opts.ResetStandardEntries, "reset-standard-entries", "r", false, "Reset standard entries to their default values") + cmd.Flags(). + StringSliceVarP(&opts.DisableStandardEntries, "disable-standard-entries", "d", []string{}, "Disable standard entries for the given languages") + cmd.Flags(). + StringSliceVarP(&opts.EnableStandardEntries, "enable-standard-entries", "e", []string{}, "Enable standard entries for the given languages") + cmd.Flags(). + BoolVarP(&opts.ResetStandardEntries, "reset-standard-entries", "r", false, "Reset standard entries to their default values") SupportedLanguages := make(map[string]string, len(LanguagesWithStopwordsSupport)) for _, languageCode := range LanguagesWithStopwordsSupport { SupportedLanguages[languageCode] = Languages[languageCode] } - _ = cmd.RegisterFlagCompletionFunc("disable-standard-entries", cmdutil.StringCompletionFunc(SupportedLanguages)) - _ = cmd.RegisterFlagCompletionFunc("enable-standard-entries", cmdutil.StringCompletionFunc(SupportedLanguages)) + _ = cmd.RegisterFlagCompletionFunc( + "disable-standard-entries", + cmdutil.StringCompletionFunc(SupportedLanguages), + ) + _ = cmd.RegisterFlagCompletionFunc( + "enable-standard-entries", + cmdutil.StringCompletionFunc(SupportedLanguages), + ) return cmd } @@ -107,7 +125,9 @@ func runSetCmd(opts *SetOptions) error { var disableStandardEntriesOpt *opt.DisableStandardEntriesOption if opts.ResetStandardEntries { - disableStandardEntriesOpt = opt.DisableStandardEntries(map[string]map[string]bool{"stopwords": nil}) + disableStandardEntriesOpt = opt.DisableStandardEntries( + map[string]map[string]bool{"stopwords": nil}, + ) } if len(opts.DisableStandardEntries) > 0 || len(opts.EnableStandardEntries) > 0 { diff --git a/pkg/cmd/dictionary/shared/constants.go b/pkg/cmd/dictionary/shared/constants.go index 7a30f792..d314b708 100644 --- a/pkg/cmd/dictionary/shared/constants.go +++ b/pkg/cmd/dictionary/shared/constants.go @@ -6,8 +6,10 @@ import ( // EntryType represents the type of an entry in a dictionary. // It can be either a custom entry or a standard entry. -type EntryType string -type DictionaryType int +type ( + EntryType string + DictionaryType int +) // DictionaryEntry can be plural, compound or stopword entry. type DictionaryEntry struct { @@ -27,13 +29,11 @@ const ( StandardEntryType EntryType = "standard" ) -var ( - // DictionaryNames returns the list of available dictionaries. - DictionaryNames = func() []string { - return []string{ - string(search.Stopwords), - string(search.Compounds), - string(search.Plurals), - } +// DictionaryNames returns the list of available dictionaries. +var DictionaryNames = func() []string { + return []string{ + string(search.Stopwords), + string(search.Compounds), + string(search.Plurals), } -) +} diff --git a/pkg/cmd/events/tail/tail.go b/pkg/cmd/events/tail/tail.go index a541fedd..5e00536f 100644 --- a/pkg/cmd/events/tail/tail.go +++ b/pkg/cmd/events/tail/tail.go @@ -81,7 +81,8 @@ func NewTailCmd(f *cmdutil.Factory, runF func(*TailOptions) error) *cobra.Comman }, } - cmd.Flags().StringVarP(&opts.Region, "region", "r", string(DefaultRegion), "Region where your analytics data is stored and processed.") + cmd.Flags(). + StringVarP(&opts.Region, "region", "r", string(DefaultRegion), "Region where your analytics data is stored and processed.") _ = cmd.RegisterFlagCompletionFunc("region", cmdutil.StringCompletionFunc(map[string]string{ string(region.US): "United States", string(region.DE): "Germany (Europe)", @@ -164,6 +165,15 @@ func printEvent(io *iostreams.IOStreams, event insights.EventWrapper) error { colorizedStatus = cs.Red(fmt.Sprint(event.Status)) } - _, err := fmt.Fprintf(io.Out, "%s [%s] %s %s [%s] %s\n", cs.Bold(formatedTime), colorizedStatus, event.Event.EventType, cs.Bold(event.Event.Index), event.Event.EventName, event.Event.UserToken) + _, err := fmt.Fprintf( + io.Out, + "%s [%s] %s %s [%s] %s\n", + cs.Bold(formatedTime), + colorizedStatus, + event.Event.EventType, + cs.Bold(event.Event.Index), + event.Event.EventName, + event.Event.UserToken, + ) return err } diff --git a/pkg/cmd/indices/analyze/analyze.go b/pkg/cmd/indices/analyze/analyze.go index 46cb5d76..e03daecc 100644 --- a/pkg/cmd/indices/analyze/analyze.go +++ b/pkg/cmd/indices/analyze/analyze.go @@ -82,8 +82,10 @@ func NewAnalyzeCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().BoolVarP(&opts.NoLimit, "no-limit", "n", false, "If set, the command will not limit the number of objects to analyze. Otherwise, the default limit is 1000 objects.") - cmd.Flags().StringVarP(&opts.Only, "only", "", "", "If set, the command will only analyze the specified attribute. Chosen attribute values statistics will be shown in the output.") + cmd.Flags(). + BoolVarP(&opts.NoLimit, "no-limit", "n", false, "If set, the command will not limit the number of objects to analyze. Otherwise, the default limit is 1000 objects.") + cmd.Flags(). + StringVarP(&opts.Only, "only", "", "", "If set, the command will only analyze the specified attribute. Chosen attribute values statistics will be shown in the output.") cmdutil.AddBrowseParamsObjectFlags(cmd) opts.PrintFlags.AddFlags(cmd) @@ -222,7 +224,7 @@ func printStats(stats *analyze.Stats, opts *StatsOptions) error { for _, key := range sorted { // Print colorized output depending on the percentage // If <1%: red, if <5%: yellow - var color = func(s string) string { return s } + color := func(s string) string { return s } if stats.Attributes[key].Percentage < 1 { color = cs.Red } else if stats.Attributes[key].Percentage < 5 { @@ -265,7 +267,11 @@ func printSingleAttributeStats(stats *analyze.Stats, opts *StatsOptions) error { for _, v := range sorted { table.AddField(fmt.Sprintf("%v", v), nil, nil) table.AddField(fmt.Sprintf("%d", value.Values[v]), nil, nil) - table.AddField(fmt.Sprintf("%.2f%%", float64(value.Values[v])*100/float64(stats.TotalRecords)), nil, nil) + table.AddField( + fmt.Sprintf("%.2f%%", float64(value.Values[v])*100/float64(stats.TotalRecords)), + nil, + nil, + ) table.EndRow() } } diff --git a/pkg/cmd/indices/config/export/export.go b/pkg/cmd/indices/config/export/export.go index 9416f9c1..01bca006 100644 --- a/pkg/cmd/indices/config/export/export.go +++ b/pkg/cmd/indices/config/export/export.go @@ -113,7 +113,7 @@ func runExportCmd(opts *config.ExportOptions) error { } filePath := config.GetConfigFileName(opts.Directory, opts.Indice, indice.GetAppID()) - err = os.WriteFile(filePath, configJsonIndented, 0644) + err = os.WriteFile(filePath, configJsonIndented, 0o644) if err != nil { return fmt.Errorf("%s An error occurred when saving the file: %w", cs.FailureIcon(), err) } diff --git a/pkg/cmd/indices/config/import/confirm.go b/pkg/cmd/indices/config/import/confirm.go index 3eae60c0..ed78b67b 100644 --- a/pkg/cmd/indices/config/import/confirm.go +++ b/pkg/cmd/indices/config/import/confirm.go @@ -7,7 +7,11 @@ import ( "github.com/algolia/cli/pkg/utils" ) -func GetConfirmMessage(cs *iostreams.ColorScheme, scope []string, clearExistingRules, clearExistingSynonyms bool) string { +func GetConfirmMessage( + cs *iostreams.ColorScheme, + scope []string, + clearExistingRules, clearExistingSynonyms bool, +) string { scopeToClear := []string{} scopeToUpdate := []string{} message := "" @@ -30,12 +34,21 @@ func GetConfirmMessage(cs *iostreams.ColorScheme, scope []string, clearExistingR } } if len(scopeToClear) > 0 { - message = fmt.Sprintf("%s Your %s will be %s\n", - cs.WarningIcon(), utils.SliceToReadableString(scopeToClear), cs.Bold("CLEARED and REPLACED.")) + message = fmt.Sprintf( + "%s Your %s will be %s\n", + cs.WarningIcon(), + utils.SliceToReadableString(scopeToClear), + cs.Bold("CLEARED and REPLACED."), + ) } if len(scopeToUpdate) > 0 { - message = fmt.Sprintf("%s%s Your %s will be %s\n", - message, cs.WarningIcon(), utils.SliceToReadableString(scopeToUpdate), cs.Bold("UPDATED")) + message = fmt.Sprintf( + "%s%s Your %s will be %s\n", + message, + cs.WarningIcon(), + utils.SliceToReadableString(scopeToUpdate), + cs.Bold("UPDATED"), + ) } return message diff --git a/pkg/cmd/indices/config/import/confirm_test.go b/pkg/cmd/indices/config/import/confirm_test.go index 934fb688..41f6ad68 100644 --- a/pkg/cmd/indices/config/import/confirm_test.go +++ b/pkg/cmd/indices/config/import/confirm_test.go @@ -61,7 +61,11 @@ func TestGetConfirmMessage(t *testing.T) { io, _, _, _ := iostreams.Test() tt.cs = io.ColorScheme() - assert.Equal(t, tt.wantsOutput, GetConfirmMessage(tt.cs, tt.scope, tt.clearExistingRules, tt.clearExistingSynonyms)) + assert.Equal( + t, + tt.wantsOutput, + GetConfirmMessage(tt.cs, tt.scope, tt.clearExistingRules, tt.clearExistingSynonyms), + ) }) } } diff --git a/pkg/cmd/objects/browse/browse_test.go b/pkg/cmd/objects/browse/browse_test.go index b400cc2d..d0ff7708 100644 --- a/pkg/cmd/objects/browse/browse_test.go +++ b/pkg/cmd/objects/browse/browse_test.go @@ -34,9 +34,12 @@ func Test_runBrowseCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/indexes/foo/browse"), httpmock.JSONResponse(search.QueryRes{ - Hits: tt.hits, - })) + r.Register( + httpmock.REST("POST", "1/indexes/foo/browse"), + httpmock.JSONResponse(search.QueryRes{ + Hits: tt.hits, + }), + ) defer r.Verify(t) f, out := test.NewFactory(true, &r, nil, "") diff --git a/pkg/cmd/objects/delete/delete.go b/pkg/cmd/objects/delete/delete.go index 98eb712e..b9438d8e 100644 --- a/pkg/cmd/objects/delete/delete.go +++ b/pkg/cmd/objects/delete/delete.go @@ -79,7 +79,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -96,7 +98,8 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmdutil.AddDeleteByParamsFlags(cmd) cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") - cmd.Flags().BoolVar(&opts.Wait, "wait", false, "wait for all the operations to complete before returning") + cmd.Flags(). + BoolVar(&opts.Wait, "wait", false, "wait for all the operations to complete before returning") return cmd } @@ -147,7 +150,12 @@ func runDeleteCmd(opts *DeleteOptions) error { return nil } - objectNbMessage := fmt.Sprintf("%s %s from %s", exactOrApproximate, utils.Pluralize(nbObjectsToDelete, "object"), opts.Indice) + objectNbMessage := fmt.Sprintf( + "%s %s from %s", + exactOrApproximate, + utils.Pluralize(nbObjectsToDelete, "object"), + opts.Indice, + ) if opts.DoConfirm { var confirmed bool diff --git a/pkg/cmd/objects/delete/delete_test.go b/pkg/cmd/objects/delete/delete_test.go index 2656b4ef..1704b36b 100644 --- a/pkg/cmd/objects/delete/delete_test.go +++ b/pkg/cmd/objects/delete/delete_test.go @@ -187,18 +187,30 @@ func Test_runDeleteCmd(t *testing.T) { r := httpmock.Registry{} for _, id := range tt.objectIDs { // Checking that the object exists - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/%s", tt.indice, id)), httpmock.JSONResponse(search.QueryRes{})) + r.Register( + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/%s", tt.indice, id)), + httpmock.JSONResponse(search.QueryRes{}), + ) } if tt.nbHits > 0 { // Searching for the objects to delete (if filters are used) - r.Register(httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/query", tt.indice)), httpmock.JSONResponse(search.QueryRes{ - NbHits: tt.nbHits, - ExhaustiveNbHits: tt.exhaustiveNbHits, - })) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/query", tt.indice)), + httpmock.JSONResponse(search.QueryRes{ + NbHits: tt.nbHits, + ExhaustiveNbHits: tt.exhaustiveNbHits, + }), + ) // Deleting the objects - r.Register(httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/deleteByQuery", tt.indice)), httpmock.JSONResponse(search.BatchRes{})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/deleteByQuery", tt.indice)), + httpmock.JSONResponse(search.BatchRes{}), + ) } - r.Register(httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/batch", tt.indice)), httpmock.JSONResponse(search.BatchRes{})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/batch", tt.indice)), + httpmock.JSONResponse(search.BatchRes{}), + ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") cmd := NewDeleteCmd(f, nil) diff --git a/pkg/cmd/objects/import/import.go b/pkg/cmd/objects/import/import.go index bfea9192..2d9dd710 100644 --- a/pkg/cmd/objects/import/import.go +++ b/pkg/cmd/objects/import/import.go @@ -72,10 +72,12 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().StringVarP(&file, "file", "F", "", "Read records to import from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&file, "file", "F", "", "Read records to import from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVar(&opts.AutoGenerateObjectIDIfNotExist, "auto-generate-object-id-if-not-exist", false, "Automatically generate object ID if not exist") + cmd.Flags(). + BoolVar(&opts.AutoGenerateObjectIDIfNotExist, "auto-generate-object-id-if-not-exist", false, "Automatically generate object ID if not exist") cmd.Flags().IntVarP(&opts.BatchSize, "batch-size", "b", 1000, "Specify the upload batch size") return cmd } @@ -96,7 +98,9 @@ func runImportCmd(opts *ImportOptions) error { totalCount = 0 ) - options := []interface{}{opt.AutoGenerateObjectIDIfNotExist(opts.AutoGenerateObjectIDIfNotExist)} + options := []interface{}{ + opt.AutoGenerateObjectIDIfNotExist(opts.AutoGenerateObjectIDIfNotExist), + } opts.IO.StartProgressIndicatorWithLabel("Importing records") elapsed := time.Now() @@ -121,7 +125,9 @@ func runImportCmd(opts *ImportOptions) error { } batch = make([]interface{}, 0, batchSize) totalCount += count - opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d objects in %v", totalCount, time.Since(elapsed))) + opts.IO.UpdateProgressIndicatorLabel( + fmt.Sprintf("Imported %d objects in %v", totalCount, time.Since(elapsed)), + ) count = 0 } } @@ -141,7 +147,14 @@ func runImportCmd(opts *ImportOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully imported %s objects to %s in %v\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(totalCount)), opts.Index, time.Since(elapsed)) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully imported %s objects to %s in %v\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(totalCount)), + opts.Index, + time.Since(elapsed), + ) } return nil diff --git a/pkg/cmd/objects/import/import_test.go b/pkg/cmd/objects/import/import_test.go index a3655d68..f7aeb080 100644 --- a/pkg/cmd/objects/import/import_test.go +++ b/pkg/cmd/objects/import/import_test.go @@ -16,7 +16,7 @@ import ( func Test_runImportCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "objects.json") - err := os.WriteFile(tmpFile, []byte("{\"objectID\":\"foo\"}"), 0600) + err := os.WriteFile(tmpFile, []byte("{\"objectID\":\"foo\"}"), 0o600) require.NoError(t, err) tests := []struct { @@ -59,7 +59,10 @@ func Test_runImportCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.wantErr == "" { - r.Register(httpmock.REST("POST", "1/indexes/foo/batch"), httpmock.JSONResponse(search.BatchRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/batch"), + httpmock.JSONResponse(search.BatchRes{}), + ) } defer r.Verify(t) diff --git a/pkg/cmd/objects/operations/operations_test.go b/pkg/cmd/objects/operations/operations_test.go index 949ac163..4fbb364d 100644 --- a/pkg/cmd/objects/operations/operations_test.go +++ b/pkg/cmd/objects/operations/operations_test.go @@ -16,7 +16,13 @@ import ( func Test_runOperationsCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "operations.json") - err := os.WriteFile(tmpFile, []byte(`{"action":"addObject","indexName":"index1","body":{"firstname":"Jimmie","lastname":"Barninger"}}`), 0600) + err := os.WriteFile( + tmpFile, + []byte( + `{"action":"addObject","indexName":"index1","body":{"firstname":"Jimmie","lastname":"Barninger"}}`, + ), + 0o600, + ) require.NoError(t, err) tests := []struct { @@ -80,7 +86,10 @@ func Test_runOperationsCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.wantErr == "" { - r.Register(httpmock.REST("POST", "1/indexes/*/batch"), httpmock.JSONResponse(search.MultipleBatchRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/*/batch"), + httpmock.JSONResponse(search.MultipleBatchRes{}), + ) } defer r.Verify(t) @@ -128,8 +137,10 @@ func Test_ValidateBatchOperation(t *testing.T) { }, } - for _, act := range []string{string(search.AddObject), string(search.UpdateObject), - string(search.PartialUpdateObject), string(search.PartialUpdateObjectNoCreate)} { + for _, act := range []string{ + string(search.AddObject), string(search.UpdateObject), + string(search.PartialUpdateObject), string(search.PartialUpdateObjectNoCreate), + } { tests = append(tests, struct { name string action string diff --git a/pkg/cmd/objects/update/object_test.go b/pkg/cmd/objects/update/object_test.go index 4212205e..24aa41dd 100644 --- a/pkg/cmd/objects/update/object_test.go +++ b/pkg/cmd/objects/update/object_test.go @@ -105,7 +105,10 @@ func Test_Object_UnmarshalJSON(t *testing.T) { } }`), wantErr: false, - wantObj: Object{"objectID": "foo", "bar": search.PartialUpdateOperation{Operation: "Increment"}}, + wantObj: Object{ + "objectID": "foo", + "bar": search.PartialUpdateOperation{Operation: "Increment"}, + }, }, } diff --git a/pkg/cmd/objects/update/update_test.go b/pkg/cmd/objects/update/update_test.go index 2dea70ca..61c71997 100644 --- a/pkg/cmd/objects/update/update_test.go +++ b/pkg/cmd/objects/update/update_test.go @@ -16,7 +16,7 @@ import ( func Test_runUpdateCmd(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "objects.json") - err := os.WriteFile(tmpFile, []byte(`{"objectID":"foo"}`), 0600) + err := os.WriteFile(tmpFile, []byte(`{"objectID":"foo"}`), 0o600) require.NoError(t, err) tests := []struct { @@ -79,7 +79,10 @@ func Test_runUpdateCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.wantErr == "" { - r.Register(httpmock.REST("POST", "1/indexes/foo/batch"), httpmock.JSONResponse(search.BatchRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/batch"), + httpmock.JSONResponse(search.BatchRes{}), + ) } defer r.Verify(t) diff --git a/pkg/cmd/open/open.go b/pkg/cmd/open/open.go index f97dc9c8..60f5c3c6 100644 --- a/pkg/cmd/open/open.go +++ b/pkg/cmd/open/open.go @@ -15,49 +15,49 @@ import ( "github.com/algolia/cli/pkg/printers" ) -type OpenURL struct { +type OpenUrl struct { Default string - WithAppID string + WithAppId string } -var openURLMap = map[string]OpenURL{ +var openUrlMap = map[string]OpenUrl{ "api": {Default: "https://www.algolia.com/doc/api-reference/rest-api/"}, "codex": {Default: "https://www.algolia.com/developers/code-exchange/"}, "cli-docs": {Default: "https://algolia.com/doc/tools/cli/get-started/overview/"}, "cli-repo": {Default: "https://github.com/algolia/cli"}, "dashboard": { Default: "https://www.algolia.com/dashboard", - WithAppID: "https://www.algolia.com/apps/%s/dashboard", + WithAppId: "https://www.algolia.com/apps/%s/dashboard", }, "devhub": {Default: "https://www.algolia.com/developers/"}, "docs": {Default: "https://algolia.com/doc/"}, "languages": {Default: "https://alg.li/supported-languages"}, "status": { Default: "https://status.algolia.com/", - WithAppID: "https://www.algolia.com/apps/%s/monitoring/status", + WithAppId: "https://www.algolia.com/apps/%s/monitoring/status", }, } func openNames() []string { - keys := make([]string, 0, len(openURLMap)) - for k := range openURLMap { + keys := make([]string, 0, len(openUrlMap)) + for k := range openUrlMap { keys = append(keys, k) } return keys } -func getNameURLMap(applicationID string) map[string]string { - nameURLMap := make(map[string]string) +func getNameUrlMap(applicationID string) map[string]string { + nameUrlMap := make(map[string]string) for _, openName := range openNames() { - url := openURLMap[openName].Default - if applicationID != "" && openURLMap[openName].WithAppID != "" { - url = fmt.Sprintf(openURLMap[openName].WithAppID, applicationID) + url := openUrlMap[openName].Default + if applicationID != "" && openUrlMap[openName].WithAppId != "" { + url = fmt.Sprintf(openUrlMap[openName].WithAppId, applicationID) } - nameURLMap[openName] = url + nameUrlMap[openName] = url } - return nameURLMap + return nameUrlMap } // OpenOptions represents the options for the open command @@ -125,7 +125,7 @@ func NewOpenCmd(f *cmdutil.Factory) *cobra.Command { func runOpenCmd(opts *OpenOptions) error { profile := opts.config.Profile() applicationID, _ := profile.GetApplicationID() - nameURLMap := getNameURLMap(applicationID) + nameUrlMap := getNameUrlMap(applicationID) if opts.List || opts.Shortcut == "" { fmt.Println("open quickly opens Algolia pages. To use, run 'algolia open '.") @@ -142,7 +142,7 @@ func runOpenCmd(opts *OpenOptions) error { table.EndRow() } - for shortcutName, url := range nameURLMap { + for shortcutName, url := range nameUrlMap { table.AddField(shortcutName, nil, nil) table.AddField(url, nil, nil) table.EndRow() @@ -152,7 +152,7 @@ func runOpenCmd(opts *OpenOptions) error { } var err error - if url, ok := nameURLMap[opts.Shortcut]; ok { + if url, ok := nameUrlMap[opts.Shortcut]; ok { err = open.Browser(url) if err != nil { return err diff --git a/pkg/cmd/profile/add/add.go b/pkg/cmd/profile/add/add.go index 99ce75a3..eaf8f9ad 100644 --- a/pkg/cmd/profile/add/add.go +++ b/pkg/cmd/profile/add/add.go @@ -8,7 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/pkg/auth" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" @@ -56,7 +56,9 @@ func NewAddCmd(f *cmdutil.Factory, runF func(*AddOptions) error) *cobra.Command opts.Interactive = !(nameProvided && appIDProvided && APIKeyProvided) if opts.Interactive && !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("`--name`, `--app-id` and `--api-key` required when not running interactively") + return cmdutil.FlagErrorf( + "`--name`, `--app-id` and `--api-key` required when not running interactively", + ) } if !opts.Interactive { @@ -80,9 +82,12 @@ func NewAddCmd(f *cmdutil.Factory, runF func(*AddOptions) error) *cobra.Command } cmd.Flags().StringVarP(&opts.Profile.Name, "name", "n", "", heredoc.Doc(`Name of the profile.`)) - cmd.Flags().StringVar(&opts.Profile.ApplicationID, "app-id", "", heredoc.Doc(`ID of the application.`)) - cmd.Flags().StringVar(&opts.Profile.APIKey, "api-key", "", heredoc.Doc(`API Key of the application.`)) - cmd.Flags().BoolVarP(&opts.Profile.Default, "default", "d", false, heredoc.Doc(`Set the profile as the default one.`)) + cmd.Flags(). + StringVar(&opts.Profile.ApplicationID, "app-id", "", heredoc.Doc(`ID of the application.`)) + cmd.Flags(). + StringVar(&opts.Profile.APIKey, "api-key", "", heredoc.Doc(`API Key of the application.`)) + cmd.Flags(). + BoolVarP(&opts.Profile.Default, "default", "d", false, heredoc.Doc(`Set the profile as the default one.`)) return cmd } @@ -104,7 +109,10 @@ func runAddCmd(opts *AddOptions) error { Message: "Name:", Default: opts.Profile.Name, }, - Validate: survey.ComposeValidators(survey.Required, validators.ProfileNameExists(opts.config)), + Validate: survey.ComposeValidators( + survey.Required, + validators.ProfileNameExists(opts.config), + ), }, { Name: "applicationID", @@ -112,7 +120,10 @@ func runAddCmd(opts *AddOptions) error { Message: "Application ID:", Default: opts.Profile.ApplicationID, }, - Validate: survey.ComposeValidators(survey.Required, validators.ApplicationIDExists(opts.config)), + Validate: survey.ComposeValidators( + survey.Required, + validators.ApplicationIDExists(opts.config), + ), }, { Name: "APIKey", @@ -136,31 +147,42 @@ func runAddCmd(opts *AddOptions) error { } } - client := search.NewClient(opts.Profile.ApplicationID, opts.Profile.APIKey) + client, err := search.NewClient(opts.Profile.ApplicationID, opts.Profile.APIKey) + if err != nil { + return err + } var isAdminAPIKey bool // Check if the provided API Key is an admin API Key - _, err := client.ListAPIKeys() + _, err = client.ListApiKeys() if err == nil { isAdminAPIKey = true } // Check the ACLs of the provided API Key - apiKey, err := search.NewClient(opts.Profile.ApplicationID, opts.Profile.APIKey).GetAPIKey(opts.Profile.APIKey) + apiKey, err := client.GetApiKey(client.NewApiGetApiKeyRequest(opts.Profile.APIKey)) if err != nil { return errors.New("invalid application credentials") } - if len(apiKey.ACL) == 0 { + if len(apiKey.Acl) == 0 { return errors.New("the provided API key has no ACLs") } + var stringACLs []string + for _, a := range apiKey.Acl { + stringACLs = append(stringACLs, string(a)) + } // We should have at least the ACLs for a write key, otherwise warns the user, but still allows to add the profile. // If it's an admin API Key, we don't need to check ACLs, but we still warn the user. var warning string if !isAdminAPIKey { - missingACLs := utils.Differences(auth.WriteAPIKeyDefaultACLs, apiKey.ACL) + missingACLs := utils.Differences(auth.WriteAPIKeyDefaultACLs, stringACLs) if len(missingACLs) > 0 { - warning = fmt.Sprintf("%s The provided API key might be missing some ACLs: %s", opts.IO.ColorScheme().WarningIcon(), missingACLs) + warning = fmt.Sprintf( + "%s The provided API key might be missing some ACLs: %s", + opts.IO.ColorScheme().WarningIcon(), + missingACLs, + ) warning += "\n See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restrictions for more information." warning += "\n You can still add the profile, but some commands might not be available." } @@ -202,7 +224,11 @@ func runAddCmd(opts *AddOptions) error { if opts.Profile.Default { if defaultProfile != nil { - extra = fmt.Sprintf(". Default profile changed from '%s' to '%s'.", cs.Bold(defaultProfile.Name), cs.Bold(opts.Profile.Name)) + extra = fmt.Sprintf( + ". Default profile changed from '%s' to '%s'.", + cs.Bold(defaultProfile.Name), + cs.Bold(opts.Profile.Name), + ) } else { extra = " and set as default." } diff --git a/pkg/cmd/profile/list/list.go b/pkg/cmd/profile/list/list.go index 9c4cbe6f..b23b7052 100644 --- a/pkg/cmd/profile/list/list.go +++ b/pkg/cmd/profile/list/list.go @@ -6,7 +6,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" "github.com/algolia/cli/pkg/iostreams" @@ -76,8 +76,11 @@ func runListCmd(opts *ListOptions) error { apiKey = profile.AdminAPIKey // Legacy } - client := search.NewClient(profile.ApplicationID, apiKey) - res, err := client.ListIndices() + client, err := search.NewClient(profile.ApplicationID, apiKey) + if err != nil { + table.AddField(err.Error(), nil, nil) + } + res, err := client.ListIndices(client.NewApiListIndicesRequest()) if err != nil { table.AddField(err.Error(), nil, nil) } else { diff --git a/pkg/cmd/profile/remove/remove.go b/pkg/cmd/profile/remove/remove.go index 1e0e069d..5b87d7f3 100644 --- a/pkg/cmd/profile/remove/remove.go +++ b/pkg/cmd/profile/remove/remove.go @@ -54,7 +54,9 @@ func NewRemoveCmd(f *cmdutil.Factory, runF func(*RemoveOptions) error) *cobra.Co if !confirm && opts.Profile == opts.DefaultProfile { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -80,7 +82,10 @@ func NewRemoveCmd(f *cmdutil.Factory, runF func(*RemoveOptions) error) *cobra.Co func runRemoveCmd(opts *RemoveOptions) error { if opts.DoConfirm { var confirmed bool - err := prompt.Confirm(fmt.Sprintf("Are you sure you want to remove '%s', the default profile?", opts.Profile), &confirmed) + err := prompt.Confirm( + fmt.Sprintf("Are you sure you want to remove '%s', the default profile?", opts.Profile), + &confirmed, + ) if err != nil { return err } diff --git a/pkg/cmd/profile/remove/remove_test.go b/pkg/cmd/profile/remove/remove_test.go index 21b5f029..6fc6d5a6 100644 --- a/pkg/cmd/profile/remove/remove_test.go +++ b/pkg/cmd/profile/remove/remove_test.go @@ -97,21 +97,30 @@ func Test_runRemoveCmd(t *testing.T) { wantOut string }{ { - name: "existing profile (default)", - cli: "default --confirm", - profiles: []config.Profile{{Name: "default", Default: true}, {Name: "foo", Default: false}}, - wantOut: "✓ 'default' removed successfully. Set a new default profile with 'algolia profile setdefault'.\n", + name: "existing profile (default)", + cli: "default --confirm", + profiles: []config.Profile{ + {Name: "default", Default: true}, + {Name: "foo", Default: false}, + }, + wantOut: "✓ 'default' removed successfully. Set a new default profile with 'algolia profile setdefault'.\n", }, { - name: "existing profile (non-default)", - cli: "foo --confirm", - profiles: []config.Profile{{Name: "default", Default: true}, {Name: "foo", Default: false}}, - wantOut: "✓ 'foo' removed successfully.\n", + name: "existing profile (non-default)", + cli: "foo --confirm", + profiles: []config.Profile{ + {Name: "default", Default: true}, + {Name: "foo", Default: false}, + }, + wantOut: "✓ 'foo' removed successfully.\n", }, { - name: "non-existant profile", - cli: "bar --confirm", - profiles: []config.Profile{{Name: "default", Default: true}, {Name: "foo", Default: false}}, + name: "non-existant profile", + cli: "bar --confirm", + profiles: []config.Profile{ + {Name: "default", Default: true}, + {Name: "foo", Default: false}, + }, wantsErr: "the specified profile does not exist: 'bar'", }, { diff --git a/pkg/cmd/root/help.go b/pkg/cmd/root/help.go index 69fc5e6a..ad966ccb 100644 --- a/pkg/cmd/root/help.go +++ b/pkg/cmd/root/help.go @@ -10,7 +10,10 @@ import ( "github.com/algolia/cli/pkg/iostreams" ) -func rootUsageFunc(IOStreams *iostreams.IOStreams, command *cobra.Command) func(cmd *cobra.Command) error { +func rootUsageFunc( + IOStreams *iostreams.IOStreams, + command *cobra.Command, +) func(cmd *cobra.Command) error { return cmdutil.UsageFuncDefault(IOStreams, command) } @@ -85,10 +88,20 @@ func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) { if len(categoryFlagSet.Categories) > 0 { for _, categoryName := range categoryFlagSet.SortedCategoryNames() { groupName := fmt.Sprintf("%s Flags", categoryName) - helpEntries.AddEntry(cmdutil.UsageEntry{Title: groupName, Body: cmdutil.Dedent(categoryFlagSet.Categories[categoryName].FlagUsages())}) + helpEntries.AddEntry( + cmdutil.UsageEntry{ + Title: groupName, + Body: cmdutil.Dedent(categoryFlagSet.Categories[categoryName].FlagUsages()), + }, + ) } if categoryFlagSet.Others.FlagUsages() != "" { - helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("Other Flags"), Body: cmdutil.Dedent(categoryFlagSet.Others.FlagUsages())}) + helpEntries.AddEntry( + cmdutil.UsageEntry{ + Title: cs.Bold("Other Flags"), + Body: cmdutil.Dedent(categoryFlagSet.Others.FlagUsages()), + }, + ) } } else { helpEntries.AddFlags(f.IOStreams, command, cmdutil.Dedent(categoryFlagSet.Others.FlagUsages())) @@ -96,17 +109,32 @@ func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) { printFlagUsages := categoryFlagSet.Print.FlagUsages() if printFlagUsages != "" { - helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("Output Formatting Flags"), Body: cmdutil.Dedent(printFlagUsages)}) + helpEntries.AddEntry( + cmdutil.UsageEntry{ + Title: cs.Bold("Output Formatting Flags"), + Body: cmdutil.Dedent(printFlagUsages), + }, + ) } inheritedFlagUsages := command.InheritedFlags().FlagUsages() if inheritedFlagUsages != "" { - helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("Inherited Flags"), Body: cmdutil.Dedent(inheritedFlagUsages)}) + helpEntries.AddEntry( + cmdutil.UsageEntry{ + Title: cs.Bold("Inherited Flags"), + Body: cmdutil.Dedent(inheritedFlagUsages), + }, + ) } if command.Example != "" { helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("Examples"), Body: command.Example}) } if _, ok := command.Annotations["help:see-also"]; ok { - helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("See also"), Body: command.Annotations["help:see-also"]}) + helpEntries.AddEntry( + cmdutil.UsageEntry{ + Title: cs.Bold("See also"), + Body: command.Annotations["help:see-also"], + }, + ) } helpEntries.AddEntry(cmdutil.UsageEntry{Title: cs.Bold("Learn More"), Body: ` Use 'algolia --help' for more information about a command. diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 10675166..4a924251 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -78,14 +78,18 @@ func NewRootCmd(f *cmdutil.Factory) *cobra.Command { }) cmd.SetFlagErrorFunc(rootFlagErrorFunc) - cmd.PersistentFlags().StringVarP(&f.Config.Profile().Name, "profile", "p", "", "The profile to use") + cmd.PersistentFlags(). + StringVarP(&f.Config.Profile().Name, "profile", "p", "", "The profile to use") _ = cmd.RegisterFlagCompletionFunc("profile", cmdutil.ConfiguredProfilesCompletionFunc(f)) - cmd.PersistentFlags().StringVarP(&f.Config.Profile().ApplicationID, "application-id", "", "", "The application ID") + cmd.PersistentFlags(). + StringVarP(&f.Config.Profile().ApplicationID, "application-id", "", "", "The application ID") cmd.PersistentFlags().StringVarP(&f.Config.Profile().APIKey, "api-key", "", "", "The API key") - cmd.PersistentFlags().StringVarP(&f.Config.Profile().AdminAPIKey, "admin-api-key", "", "", "The admin API key") + cmd.PersistentFlags(). + StringVarP(&f.Config.Profile().AdminAPIKey, "admin-api-key", "", "", "The admin API key") _ = cmd.PersistentFlags().MarkDeprecated("admin-api-key", "use --api-key instead") - cmd.PersistentFlags().StringSliceVar(&f.Config.Profile().SearchHosts, "search-hosts", nil, "The list of search hosts as CSV") + cmd.PersistentFlags(). + StringSliceVar(&f.Config.Profile().SearchHosts, "search-hosts", nil, "The list of search hosts as CSV") cmd.Flags().BoolP("version", "v", false, "Get the version of the Algolia CLI") @@ -142,7 +146,10 @@ func Execute() exitCode { if auth.IsAuthCheckEnabled(cmd) { if err := auth.CheckAuth(cfg); err != nil { fmt.Fprintf(stderr, "Authentication error: %s\n", err) - fmt.Fprintln(stderr, "Please run `algolia profile add` to configure your first profile.") + fmt.Fprintln( + stderr, + "Please run `algolia profile add` to configure your first profile.", + ) return authError } @@ -234,7 +241,12 @@ func Execute() exitCode { } // createContext creates a context with telemetry. -func createContext(cmd *cobra.Command, stderr io.Writer, hasDebug bool, hasTelemetry bool) (context.Context, error) { +func createContext( + cmd *cobra.Command, + stderr io.Writer, + hasDebug bool, + hasTelemetry bool, +) (context.Context, error) { ctx := context.Background() telemetryMetadata := telemetry.NewEventMetadata() updatedCtx := telemetry.WithEventMetadata(ctx, telemetryMetadata) @@ -306,6 +318,11 @@ func isUnderHomebrew(ghBinary string) bool { return false } - brewBinPrefix := filepath.Join(strings.TrimSpace(string(brewPrefixBytes)), "bin") + string(filepath.Separator) + brewBinPrefix := filepath.Join( + strings.TrimSpace(string(brewPrefixBytes)), + "bin", + ) + string( + filepath.Separator, + ) return strings.HasPrefix(ghBinary, brewBinPrefix) } diff --git a/pkg/cmd/rules/browse/browse_test.go b/pkg/cmd/rules/browse/browse_test.go index 05d51451..3a2d502b 100644 --- a/pkg/cmd/rules/browse/browse_test.go +++ b/pkg/cmd/rules/browse/browse_test.go @@ -34,9 +34,12 @@ func Test_runBrowseCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/indexes/foo/rules/search"), httpmock.JSONResponse(search.SearchSynonymsRes{ - Hits: tt.hits, - })) + r.Register( + httpmock.REST("POST", "1/indexes/foo/rules/search"), + httpmock.JSONResponse(search.SearchSynonymsRes{ + Hits: tt.hits, + }), + ) defer r.Verify(t) f, out := test.NewFactory(true, &r, nil, "") diff --git a/pkg/cmd/rules/delete/delete.go b/pkg/cmd/rules/delete/delete.go index 42fb9193..faf39387 100644 --- a/pkg/cmd/rules/delete/delete.go +++ b/pkg/cmd/rules/delete/delete.go @@ -62,7 +62,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts.Indice = args[0] if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -77,7 +79,8 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd.Flags().StringSliceVarP(&opts.RuleIDs, "rule-ids", "", nil, "Rule IDs to delete") _ = cmd.MarkFlagRequired("rule-ids") - cmd.Flags().BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") + cmd.Flags(). + BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") @@ -104,7 +107,14 @@ func runDeleteCmd(opts *DeleteOptions) error { if opts.DoConfirm { var confirmed bool - err = prompt.Confirm(fmt.Sprintf("Delete the %s from %s?", utils.Pluralize(len(opts.RuleIDs), "rule"), opts.Indice), &confirmed) + err = prompt.Confirm( + fmt.Sprintf( + "Delete the %s from %s?", + utils.Pluralize(len(opts.RuleIDs), "rule"), + opts.Indice, + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -123,7 +133,13 @@ func runDeleteCmd(opts *DeleteOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), utils.Pluralize(len(opts.RuleIDs), "rule"), opts.Indice) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully deleted %s from %s\n", + cs.SuccessIcon(), + utils.Pluralize(len(opts.RuleIDs), "rule"), + opts.Indice, + ) } return nil diff --git a/pkg/cmd/rules/delete/delete_test.go b/pkg/cmd/rules/delete/delete_test.go index 1ccad74f..10626523 100644 --- a/pkg/cmd/rules/delete/delete_test.go +++ b/pkg/cmd/rules/delete/delete_test.go @@ -175,8 +175,14 @@ func Test_runDeleteCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} for _, id := range tt.ruleIDs { - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), httpmock.JSONResponse(search.SearchRulesRes{})) - r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), httpmock.JSONResponse(search.DeleteTaskRes{})) + r.Register( + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), + httpmock.JSONResponse(search.SearchRulesRes{}), + ) + r.Register( + httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), + httpmock.JSONResponse(search.DeleteTaskRes{}), + ) } f, out := test.NewFactory(tt.isTTY, &r, nil, "") diff --git a/pkg/cmd/rules/import/import.go b/pkg/cmd/rules/import/import.go index 2eec929b..06e2dc6d 100644 --- a/pkg/cmd/rules/import/import.go +++ b/pkg/cmd/rules/import/import.go @@ -6,7 +6,8 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" + "github.com/algolia/algoliasearch-client-go/v3/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -20,9 +21,9 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.APIClient, error) + SearchClient func() (*search.Client, error) - Index string + Indice string ForwardToReplicas bool ClearExistingRules bool Scanner *bufio.Scanner @@ -35,7 +36,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -44,7 +45,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -67,7 +68,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co $ algolia rules import MOVIES -F rules.ndjson -f=false `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Index = args[0] + opts.Indice = args[0] if !confirm && opts.ClearExistingRules { if !opts.IO.CanPrompt() { @@ -112,7 +113,7 @@ func runImportCmd(opts *ImportOptions) error { err := prompt.Confirm( fmt.Sprintf( "Are you sure you want to replace all the existing rules on %q?", - opts.Index, + opts.Indice, ), &confirmed, ) @@ -129,15 +130,24 @@ func runImportCmd(opts *ImportOptions) error { return err } + indice := client.InitIndex(opts.Indice) + defaultBatchOptions := []interface{}{ + opt.ForwardToReplicas(opts.ForwardToReplicas), + } + // Only clear existing rules on the first batch + batchOptions := []interface{}{ + opt.ForwardToReplicas(opts.ForwardToReplicas), + opt.ClearExistingRules(opts.ClearExistingRules), + } + // Move the following code to another module? var ( batchSize = 1000 - rules = make([]search.Rule, 0, batchSize) + batch = make([]search.Rule, 0, batchSize) count = 0 totalCount = 0 ) - clearExistingRules := opts.ClearExistingRules opts.IO.StartProgressIndicatorWithLabel("Importing rules") for opts.Scanner.Scan() { line := opts.Scanner.Text() @@ -151,36 +161,30 @@ func runImportCmd(opts *ImportOptions) error { return err } - rules = append(rules, rule) + batch = append(batch, rule) count++ - // If requested, only clear existing rules the first time if count == batchSize { - _, err := client.SaveRules( - client.NewApiSaveRulesRequest(opts.Index, rules). - WithClearExistingRules(clearExistingRules). - WithForwardToReplicas(opts.ForwardToReplicas), - ) - if err != nil { + if _, err := indice.SaveRules(batch, batchOptions...); err != nil { return err } - rules = make([]search.Rule, 0, batchSize) + batchOptions = defaultBatchOptions + batch = make([]search.Rule, 0, batchSize) totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d rules", totalCount)) count = 0 - clearExistingRules = false } } if count > 0 { totalCount += count - if _, err := client.SaveRules(client.NewApiSaveRulesRequest(opts.Index, rules).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + if _, err := indice.SaveRules(batch, batchOptions...); err != nil { return err } } // Clear rules if 0 rules are imported and the clear existing is set if totalCount == 0 && opts.ClearExistingRules { - if _, err := client.ClearRules(client.NewApiClearRulesRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + if _, err := indice.ClearRules(); err != nil { return err } } @@ -198,7 +202,7 @@ func runImportCmd(opts *ImportOptions) error { "%s Successfully imported %s rules to %s\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(totalCount)), - opts.Index, + opts.Indice, ) } diff --git a/pkg/cmd/rules/import/import_test.go b/pkg/cmd/rules/import/import_test.go index bc6aecfb..7d1c1fe7 100644 --- a/pkg/cmd/rules/import/import_test.go +++ b/pkg/cmd/rules/import/import_test.go @@ -135,7 +135,10 @@ func Test_runExportCmd(t *testing.T) { stdin: `{"objectID":"test"}`, wantOut: "✓ Successfully imported 1 rules to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/rules/batch"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/rules/batch"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -143,7 +146,10 @@ func Test_runExportCmd(t *testing.T) { cli: fmt.Sprintf("foo -F '%s'", tmpFile), wantOut: "✓ Successfully imported 1 rules to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/rules/batch"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/rules/batch"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -159,7 +165,10 @@ func Test_runExportCmd(t *testing.T) { stdin: ``, wantOut: "✓ Successfully imported 0 rules to foo\n", setup: func(r *httpmock.Registry) { - r.Register(httpmock.REST("POST", "1/indexes/foo/rules/clear"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/rules/clear"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) }, }, { @@ -176,10 +185,12 @@ func Test_runExportCmd(t *testing.T) { wantOut: "✓ Successfully imported 1001 rules to foo\n", setup: func(r *httpmock.Registry) { r.Register(httpmock.Matcher(func(req *http.Request) bool { - return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && req.URL.Query().Get("clearExistingRules") == "true" + return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && + req.URL.Query().Get("clearExistingRules") == "true" }), httpmock.JSONResponse(search.UpdateTaskRes{})) r.Register(httpmock.Matcher(func(req *http.Request) bool { - return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && req.URL.Query().Get("clearExistingRules") == "" + return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && + req.URL.Query().Get("clearExistingRules") == "" }), httpmock.JSONResponse(search.UpdateTaskRes{})) }, }, diff --git a/pkg/cmd/search/search.go b/pkg/cmd/search/search.go index 4132cfca..23145431 100644 --- a/pkg/cmd/search/search.go +++ b/pkg/cmd/search/search.go @@ -73,7 +73,9 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.SetUsageFunc(cmdutil.UsageFuncWithFilteredAndInheritedFlags(f.IOStreams, cmd, []string{"query"})) + cmd.SetUsageFunc( + cmdutil.UsageFuncWithFilteredAndInheritedFlags(f.IOStreams, cmd, []string{"query"}), + ) cmdutil.AddSearchParamsObjectFlags(cmd) diff --git a/pkg/cmd/settings/import/import.go b/pkg/cmd/settings/import/import.go index 585c2597..612f35e1 100644 --- a/pkg/cmd/settings/import/import.go +++ b/pkg/cmd/settings/import/import.go @@ -60,7 +60,8 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().StringVarP(&settingsFile, "file", "F", "", "Read settings from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&settingsFile, "file", "F", "", "Read settings from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") return cmd diff --git a/pkg/cmd/settings/import/import_test.go b/pkg/cmd/settings/import/import_test.go index 7fc2ac09..edb15eb1 100644 --- a/pkg/cmd/settings/import/import_test.go +++ b/pkg/cmd/settings/import/import_test.go @@ -15,9 +15,8 @@ import ( ) func Test_runExportCmd(t *testing.T) { - tmpFile := filepath.Join(t.TempDir(), "settings.json") - err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0600) + err := os.WriteFile(tmpFile, []byte("{\"enableReRanking\":false}"), 0o600) require.NoError(t, err) tests := []struct { @@ -42,7 +41,10 @@ func Test_runExportCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("PUT", "1/indexes/foo/settings"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("PUT", "1/indexes/foo/settings"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) defer r.Verify(t) f, out := test.NewFactory(true, &r, nil, tt.stdin) diff --git a/pkg/cmd/settings/set/set.go b/pkg/cmd/settings/set/set.go index 47e96724..146e6a21 100644 --- a/pkg/cmd/settings/set/set.go +++ b/pkg/cmd/settings/set/set.go @@ -68,7 +68,8 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the settings to the replicas") + cmd.Flags(). + BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the settings to the replicas") cmdutil.AddIndexSettingsFlags(cmd) @@ -81,8 +82,11 @@ func runSetCmd(opts *SetOptions) error { return err } - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Setting settings for index %s", opts.Index)) - _, err = client.InitIndex(opts.Index).SetSettings(opts.Settings, opt.ForwardToReplicas(opts.ForwardToReplicas)) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Setting settings for index %s", opts.Index), + ) + _, err = client.InitIndex(opts.Index). + SetSettings(opts.Settings, opt.ForwardToReplicas(opts.ForwardToReplicas)) opts.IO.StopProgressIndicator() if err != nil { return err diff --git a/pkg/cmd/settings/set/set_test.go b/pkg/cmd/settings/set/set_test.go index bd07017b..723dfc93 100644 --- a/pkg/cmd/settings/set/set_test.go +++ b/pkg/cmd/settings/set/set_test.go @@ -31,7 +31,10 @@ func Test_runSetCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("PUT", "1/indexes/foo/settings"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("PUT", "1/indexes/foo/settings"), + httpmock.JSONResponse(search.UpdateTaskRes{}), + ) defer r.Verify(t) f, out := test.NewFactory(true, &r, nil, "") diff --git a/pkg/cmd/shared/handler/synonyms/synonyms.go b/pkg/cmd/shared/handler/synonyms/synonyms.go index 74ec5a10..f5e8ca81 100644 --- a/pkg/cmd/shared/handler/synonyms/synonyms.go +++ b/pkg/cmd/shared/handler/synonyms/synonyms.go @@ -97,7 +97,12 @@ func AskSynonymIDQuestion(flags *shared.SynonymFlags, flagsProvided FlagsProvide if flagsProvided.idProvided { return nil } - return ask.AskInputQuestion("id:", &flags.SynonymID, flags.SynonymID, survey.WithValidator(survey.Required)) + return ask.AskInputQuestion( + "id:", + &flags.SynonymID, + flags.SynonymID, + survey.WithValidator(survey.Required), + ) } func AskSynonymTypeQuestion(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { @@ -113,7 +118,13 @@ func AskSynonymTypeQuestion(flags *shared.SynonymFlags, flagsProvided FlagsProvi return ask.AskSelectQuestion( "type:", &flags.SynonymType, - []string{shared.Regular, shared.OneWay, shared.Placeholder, shared.AltCorrection1, shared.AltCorrection2}, + []string{ + shared.Regular, + shared.OneWay, + shared.Placeholder, + shared.AltCorrection1, + shared.AltCorrection2, + }, defaultType, survey.WithValidator(survey.Required), ) @@ -134,7 +145,12 @@ func AskRegularSynonymQuestion(flags *shared.SynonymFlags, flagsProvided FlagsPr func AskOneWaySynonymQuestions(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { if !flagsProvided.inputProvided { - err := ask.AskInputQuestion("input:", &flags.SynonymInput, flags.SynonymInput, survey.WithValidator(survey.Required)) + err := ask.AskInputQuestion( + "input:", + &flags.SynonymInput, + flags.SynonymInput, + survey.WithValidator(survey.Required), + ) if err != nil { return err } @@ -145,7 +161,12 @@ func AskOneWaySynonymQuestions(flags *shared.SynonymFlags, flagsProvided FlagsPr func AskPlaceholderSynonymQuestions(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { if !flagsProvided.placeholderProvided { - err := ask.AskInputQuestion("placeholder:", &flags.SynonymPlaceholder, flags.SynonymPlaceholder, survey.WithValidator(survey.Required)) + err := ask.AskInputQuestion( + "placeholder:", + &flags.SynonymPlaceholder, + flags.SynonymPlaceholder, + survey.WithValidator(survey.Required), + ) if err != nil { return err } @@ -162,9 +183,17 @@ func AskPlaceholderSynonymQuestions(flags *shared.SynonymFlags, flagsProvided Fl return nil } -func AskAltCorrectionSynonymQuestions(flags *shared.SynonymFlags, flagsProvided FlagsProvided) error { +func AskAltCorrectionSynonymQuestions( + flags *shared.SynonymFlags, + flagsProvided FlagsProvided, +) error { if !flagsProvided.wordProvided { - err := ask.AskInputQuestion("word:", &flags.SynonymWord, flags.SynonymWord, survey.WithValidator(survey.Required)) + err := ask.AskInputQuestion( + "word:", + &flags.SynonymWord, + flags.SynonymWord, + survey.WithValidator(survey.Required), + ) if err != nil { return err } diff --git a/pkg/cmd/shared/handler/synonyms/synonyms_test.go b/pkg/cmd/shared/handler/synonyms/synonyms_test.go index ec10adbc..a86f79b8 100644 --- a/pkg/cmd/shared/handler/synonyms/synonyms_test.go +++ b/pkg/cmd/shared/handler/synonyms/synonyms_test.go @@ -22,7 +22,8 @@ func Test_ValidateSynonymFlags(t *testing.T) { wantsErr: false, synonymFlags: shared.SynonymFlags{ SynonymID: "23", - Synonyms: []string{"mj", "goat"}}, + Synonyms: []string{"mj", "goat"}, + }, }, { name: "Regular synonym explicit type", @@ -30,14 +31,16 @@ func Test_ValidateSynonymFlags(t *testing.T) { synonymFlags: shared.SynonymFlags{ SynonymType: shared.Regular, SynonymID: "23", - Synonyms: []string{"mj", "goat"}}, + Synonyms: []string{"mj", "goat"}, + }, }, { name: "Regular synonym without id", wantsErr: true, wantsErrMsg: "a unique synonym id is required", synonymFlags: shared.SynonymFlags{ - Synonyms: []string{"mj", "goat"}}, + Synonyms: []string{"mj", "goat"}, + }, }, // One way type { diff --git a/pkg/cmd/synonyms/browse/browse_test.go b/pkg/cmd/synonyms/browse/browse_test.go index 5946b95a..e38102d6 100644 --- a/pkg/cmd/synonyms/browse/browse_test.go +++ b/pkg/cmd/synonyms/browse/browse_test.go @@ -24,9 +24,12 @@ func Test_runBrowseCmd(t *testing.T) { wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\",\"synonyms\":null}\n", }, { - name: "multiple synonyms", - cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo", "type": "synonym"}, {"objectID": "bar", "type": "synonym"}}, + name: "multiple synonyms", + cli: "foo", + hits: []map[string]interface{}{ + {"objectID": "foo", "type": "synonym"}, + {"objectID": "bar", "type": "synonym"}, + }, wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\",\"synonyms\":null}\n{\"objectID\":\"bar\",\"type\":\"synonym\",\"synonyms\":null}\n", }, } @@ -34,9 +37,12 @@ func Test_runBrowseCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/indexes/foo/synonyms/search"), httpmock.JSONResponse(search.SearchSynonymsRes{ - Hits: tt.hits, - })) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/search"), + httpmock.JSONResponse(search.SearchSynonymsRes{ + Hits: tt.hits, + }), + ) defer r.Verify(t) f, out := test.NewFactory(true, &r, nil, "") diff --git a/pkg/cmd/synonyms/delete/delete.go b/pkg/cmd/synonyms/delete/delete.go index 7dcb1c82..e9460704 100644 --- a/pkg/cmd/synonyms/delete/delete.go +++ b/pkg/cmd/synonyms/delete/delete.go @@ -62,7 +62,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts.Indice = args[0] if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -77,7 +79,8 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd.Flags().StringSliceVarP(&opts.SynonymIDs, "synonym-ids", "", nil, "Synonym IDs to delete") _ = cmd.MarkFlagRequired("synonym-ids") - cmd.Flags().BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") + cmd.Flags(). + BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") @@ -106,7 +109,14 @@ func runDeleteCmd(opts *DeleteOptions) error { if opts.DoConfirm { var confirmed bool - err = prompt.Confirm(fmt.Sprintf("Delete the %s from %s?", utils.Pluralize(len(opts.SynonymIDs), "synonym"), opts.Indice), &confirmed) + err = prompt.Confirm( + fmt.Sprintf( + "Delete the %s from %s?", + utils.Pluralize(len(opts.SynonymIDs), "synonym"), + opts.Indice, + ), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -125,7 +135,13 @@ func runDeleteCmd(opts *DeleteOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), utils.Pluralize(len(opts.SynonymIDs), "synonym"), opts.Indice) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully deleted %s from %s\n", + cs.SuccessIcon(), + utils.Pluralize(len(opts.SynonymIDs), "synonym"), + opts.Indice, + ) } return nil diff --git a/pkg/cmd/synonyms/delete/delete_test.go b/pkg/cmd/synonyms/delete/delete_test.go index 3bd9a6b6..39d350b4 100644 --- a/pkg/cmd/synonyms/delete/delete_test.go +++ b/pkg/cmd/synonyms/delete/delete_test.go @@ -175,8 +175,14 @@ func Test_runDeleteCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} for _, id := range tt.synonymIDs { - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), httpmock.JSONResponse(search.OneWaySynonym{})) - r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), httpmock.JSONResponse(search.DeleteTaskRes{})) + r.Register( + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), + httpmock.JSONResponse(search.OneWaySynonym{}), + ) + r.Register( + httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), + httpmock.JSONResponse(search.DeleteTaskRes{}), + ) } f, out := test.NewFactory(tt.isTTY, &r, nil, "") diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index e9055f18..542dddb8 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -83,11 +83,14 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co }, } - cmd.Flags().StringVarP(&file, "file", "F", "", "Read synonyms to import from `file` (use \"-\" to read from standard input)") + cmd.Flags(). + StringVarP(&file, "file", "F", "", "Read synonyms to import from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags().BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the synonyms to the replicas of the index") - cmd.Flags().BoolVarP(&opts.ReplaceExistingSynonyms, "replace-existing-synonyms", "r", false, "Replace existing synonyms in the index") + cmd.Flags(). + BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the synonyms to the replicas of the index") + cmd.Flags(). + BoolVarP(&opts.ReplaceExistingSynonyms, "replace-existing-synonyms", "r", false, "Replace existing synonyms in the index") return cmd } @@ -215,7 +218,13 @@ func runImportCmd(opts *ImportOptions) error { cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Successfully imported %s synonyms to %s\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(totalCount)), opts.Index) + fmt.Fprintf( + opts.IO.Out, + "%s Successfully imported %s synonyms to %s\n", + cs.SuccessIcon(), + cs.Bold(fmt.Sprint(totalCount)), + opts.Index, + ) } return nil diff --git a/pkg/cmd/synonyms/save/save.go b/pkg/cmd/synonyms/save/save.go index e964143f..65dacd4b 100644 --- a/pkg/cmd/synonyms/save/save.go +++ b/pkg/cmd/synonyms/save/save.go @@ -1,11 +1,11 @@ package save import ( - "encoding/json" "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" + "github.com/algolia/algoliasearch-client-go/v3/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/shared/handler" @@ -20,11 +20,11 @@ type SaveOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.APIClient, error) + SearchClient func() (*search.Client, error) - Index string + Indice string ForwardToReplicas bool - Synonym search.SynonymHit + Synonym search.Synonym SuccessMessage string } @@ -33,7 +33,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman opts := &SaveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } flags := &shared.SynonymFlags{} @@ -41,7 +41,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "save --id --synonyms ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -56,7 +56,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman $ algolia synonyms save MOVIES --id 1 --synonyms foo,bar `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Index = args[0] + opts.Indice = args[0] flagsHandler := &handler.SynonymHandler{ Flags: flags, @@ -72,17 +72,10 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman if err != nil { return err } - // Map to object - tmp, err := json.Marshal(synonym) - if err != nil { - return err - } - err = json.Unmarshal(tmp, &opts.Synonym) - if err != nil { - return err - } + // Correct flags are passed + opts.Synonym = synonym - err, successMessage := GetSuccessMessage(*flags, opts.Index) + err, successMessage := GetSuccessMessage(*flags, opts.Indice) if err != nil { return err } @@ -138,10 +131,11 @@ func runSaveCmd(opts *SaveOptions) error { if err != nil { return err } - _, err = client.SaveSynonym( - client.NewApiSaveSynonymRequest(opts.Index, opts.Synonym.ObjectID, &opts.Synonym). - WithForwardToReplicas(opts.ForwardToReplicas), - ) + + indice := client.InitIndex(opts.Indice) + forwardToReplicas := opt.ForwardToReplicas(opts.ForwardToReplicas) + + _, err = indice.SaveSynonym(opts.Synonym, forwardToReplicas) if err != nil { err = fmt.Errorf("failed to save synonym: %w", err) return err diff --git a/pkg/cmd/synonyms/save/save_test.go b/pkg/cmd/synonyms/save/save_test.go index 2f42fb91..5417ef34 100644 --- a/pkg/cmd/synonyms/save/save_test.go +++ b/pkg/cmd/synonyms/save/save_test.go @@ -163,7 +163,13 @@ func Test_runSaveCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("PUT", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, tt.synonymID)), httpmock.JSONResponse(search.RegularSynonym{})) + r.Register( + httpmock.REST( + "PUT", + fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, tt.synonymID), + ), + httpmock.JSONResponse(search.RegularSynonym{}), + ) defer r.Verify(t) f, out := test.NewFactory(tt.isTTY, &r, nil, "") diff --git a/pkg/cmd/synonyms/shared/flags_to_synonym.go b/pkg/cmd/synonyms/shared/flags_to_synonym.go index 8bbba272..0ab7d8a2 100644 --- a/pkg/cmd/synonyms/shared/flags_to_synonym.go +++ b/pkg/cmd/synonyms/shared/flags_to_synonym.go @@ -45,7 +45,9 @@ func (e *SynonymType) Set(v string) error { *e = SynonymType(v) return nil default: - return fmt.Errorf(`must be one of "regular", "one-way", "alt-correction1", "alt-correction2" or "placeholder"`) + return fmt.Errorf( + `must be one of "regular", "one-way", "alt-correction1", "alt-correction2" or "placeholder"`, + ) } } diff --git a/pkg/cmd/synonyms/shared/flags_to_synonym_test.go b/pkg/cmd/synonyms/shared/flags_to_synonym_test.go index a2389ba0..249c5e55 100644 --- a/pkg/cmd/synonyms/shared/flags_to_synonym_test.go +++ b/pkg/cmd/synonyms/shared/flags_to_synonym_test.go @@ -21,7 +21,8 @@ func Test_FlagsToSynonym(t *testing.T) { wantsErr: false, synonymFlags: SynonymFlags{ SynonymID: "23", - Synonyms: []string{"mj", "goat"}}, + Synonyms: []string{"mj", "goat"}, + }, synonymType: "search.RegularSynonym", }, { @@ -30,7 +31,8 @@ func Test_FlagsToSynonym(t *testing.T) { synonymFlags: SynonymFlags{ SynonymType: Regular, SynonymID: "23", - Synonyms: []string{"mj", "goat"}}, + Synonyms: []string{"mj", "goat"}, + }, synonymType: "search.RegularSynonym", }, // One way type diff --git a/pkg/cmdutil/flags_completion.go b/pkg/cmdutil/flags_completion.go index 38ade2c1..e834f863 100644 --- a/pkg/cmdutil/flags_completion.go +++ b/pkg/cmdutil/flags_completion.go @@ -9,7 +9,9 @@ import ( "github.com/spf13/cobra" ) -func ConfiguredProfilesCompletionFunc(f *Factory) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func ConfiguredProfilesCompletionFunc( + f *Factory, +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { profiles := f.Config.ConfiguredProfiles() completions := make([]string, 0, len(profiles)) @@ -17,13 +19,18 @@ func ConfiguredProfilesCompletionFunc(f *Factory) func(cmd *cobra.Command, args // We want to show the profile name and the Application ID as the description. // https://github.com/spf13/cobra/blob/master/shell_completions.md#descriptions-for-completions for _, profile := range profiles { - completions = append(completions, fmt.Sprintf("%s\t%s", profile.Name, profile.ApplicationID)) + completions = append( + completions, + fmt.Sprintf("%s\t%s", profile.Name, profile.ApplicationID), + ) } return completions, cobra.ShellCompDirectiveNoFileComp } } -func StringCompletionFunc(allowedMap map[string]string) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func StringCompletionFunc( + allowedMap map[string]string, +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { allowedValues := make([]string, 0, len(allowedMap)) for name, description := range allowedMap { @@ -34,12 +41,20 @@ func StringCompletionFunc(allowedMap map[string]string) func(cmd *cobra.Command, } // Inspired from https://github.com/cli/cli/blob/trunk/pkg/cmdutil/json_flags.go#L26 -func StringSliceCompletionFunc(allowedMap map[string]string, prefixAllDescription string) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func StringSliceCompletionFunc( + allowedMap map[string]string, + prefixAllDescription string, +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return runStringSliceCompletion(allowedMap, toComplete, prefixAllDescription) } } -func runStringSliceCompletion(allowedMap map[string]string, toComplete string, prefixAllDescription string) ([]string, cobra.ShellCompDirective) { + +func runStringSliceCompletion( + allowedMap map[string]string, + toComplete string, + prefixAllDescription string, +) ([]string, cobra.ShellCompDirective) { var results []string var prefix string @@ -61,13 +76,20 @@ func runStringSliceCompletion(allowedMap map[string]string, toComplete string, p } } // If current value isn't already selected and if prefix matches - if !utils.Contains(prefixSlice, name) && strings.HasPrefix(strings.ToLower(name), toComplete) { + if !utils.Contains(prefixSlice, name) && + strings.HasPrefix(strings.ToLower(name), toComplete) { // Add description of current value dynamicSliceDescriptions = append(dynamicSliceDescriptions, description) - results = append(results, fmt.Sprintf("%s%s\t%s", + results = append(results, fmt.Sprintf( + "%s%s\t%s", prefix, name, - fmt.Sprintf("%s %s", prefixAllDescription, utils.SliceToReadableString(dynamicSliceDescriptions)))) + fmt.Sprintf( + "%s %s", + prefixAllDescription, + utils.SliceToReadableString(dynamicSliceDescriptions), + ), + )) } } diff --git a/pkg/cmdutil/flags_completion_test.go b/pkg/cmdutil/flags_completion_test.go index cee8abc9..4183f9a6 100644 --- a/pkg/cmdutil/flags_completion_test.go +++ b/pkg/cmdutil/flags_completion_test.go @@ -23,7 +23,11 @@ func Test_runStringSliceCompletion(t *testing.T) { { name: "first input, no letter", toComplete: "", - results: []string{"rules\tcopy only rules", "settings\tcopy only settings", "synonyms\tcopy only synonyms"}, + results: []string{ + "rules\tcopy only rules", + "settings\tcopy only settings", + "synonyms\tcopy only synonyms", + }, }, { name: "second input (settings already passed), no letter", @@ -58,6 +62,5 @@ func Test_runStringSliceCompletion(t *testing.T) { assert.Equal(t, tt.results, results) assert.Equal(t, cobra.ShellCompDirectiveNoSpace, rule) }) - } } diff --git a/pkg/cmdutil/json_flags.go b/pkg/cmdutil/json_flags.go index d3024f52..12524276 100644 --- a/pkg/cmdutil/json_flags.go +++ b/pkg/cmdutil/json_flags.go @@ -25,7 +25,10 @@ func (f *JSONPrintFlags) ToPrinter(outputFormat string) (printers.Printer, error case "json": printer = &printers.JSONPrinter{} default: - return nil, NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()} + return nil, NoCompatiblePrinterError{ + OutputFormat: &outputFormat, + AllowedFormats: f.AllowedFormats(), + } } return printer, nil diff --git a/pkg/cmdutil/print_flags.go b/pkg/cmdutil/print_flags.go index 0dd2e233..ac58bbf4 100644 --- a/pkg/cmdutil/print_flags.go +++ b/pkg/cmdutil/print_flags.go @@ -44,7 +44,11 @@ func (e NoCompatiblePrinterError) Error() string { } sort.Strings(e.AllowedFormats) - return fmt.Sprintf("unable to match a printer suitable for the output format %q, allowed formats are: %s", output, strings.Join(e.AllowedFormats, ",")) + return fmt.Sprintf( + "unable to match a printer suitable for the output format %q, allowed formats are: %s", + output, + strings.Join(e.AllowedFormats, ","), + ) } func (f *PrintFlags) AllowedFormats() []string { @@ -67,12 +71,17 @@ func (f *PrintFlags) ToPrinter() (printers.Printer, error) { } if f.JSONPathPrintFlags != nil { - if p, err := f.JSONPathPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) { + if p, err := f.JSONPathPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError( + err, + ) { return p, err } } - return nil, NoCompatiblePrinterError{OutputFormat: f.OutputFormat, AllowedFormats: f.AllowedFormats()} + return nil, NoCompatiblePrinterError{ + OutputFormat: f.OutputFormat, + AllowedFormats: f.AllowedFormats(), + } } func (f *PrintFlags) AddFlags(cmd *cobra.Command) { @@ -80,7 +89,8 @@ func (f *PrintFlags) AddFlags(cmd *cobra.Command) { f.JSONPathPrintFlags.AddFlags(cmd) if f.OutputFormat != nil { - cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf(`Output format. One of: (%s).`, strings.Join(f.AllowedFormats(), ", "))) + cmd.Flags(). + StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf(`Output format. One of: (%s).`, strings.Join(f.AllowedFormats(), ", "))) _ = cmd.Flags().SetAnnotation("output", "IsPrint", []string{"true"}) if f.OutputFlagSpecified == nil { f.OutputFlagSpecified = func() bool { diff --git a/pkg/cmdutil/usage.go b/pkg/cmdutil/usage.go index 6cd10764..97f85f09 100644 --- a/pkg/cmdutil/usage.go +++ b/pkg/cmdutil/usage.go @@ -47,12 +47,17 @@ func (u *UsageEntries) AddBasicUsage(IOStreams *iostreams.IOStreams, command *co u.AddEntry(UsageEntry{ cs.Bold("Available commands:"), - strings.Join(commands, "\n")}, + strings.Join(commands, "\n"), + }, ) } } -func (u *UsageEntries) AddFlags(IOStreams *iostreams.IOStreams, command *cobra.Command, flagUsages string) { +func (u *UsageEntries) AddFlags( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagUsages string, +) { cs := IOStreams.ColorScheme() if flagUsages != "" { @@ -60,11 +65,19 @@ func (u *UsageEntries) AddFlags(IOStreams *iostreams.IOStreams, command *cobra.C } } -func (u *UsageEntries) AddAllFlags(IOStreams *iostreams.IOStreams, command *cobra.Command, flagUsages string) { +func (u *UsageEntries) AddAllFlags( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagUsages string, +) { u.AddFlags(IOStreams, command, command.LocalFlags().FlagUsages()) } -func (u *UsageEntries) AddFilteredFlags(IOStreams *iostreams.IOStreams, command *cobra.Command, flagsToDisplay []string) { +func (u *UsageEntries) AddFilteredFlags( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagsToDisplay []string, +) { filteredFlags := filterFlagSet(*command.LocalFlags(), flagsToDisplay) u.AddFlags(IOStreams, command, filteredFlags.FlagUsages()) @@ -97,7 +110,11 @@ func (u *UsageEntries) DisplayEntries(out io.Writer) { } } -func UsageFunc(IOStreams *iostreams.IOStreams, command *cobra.Command, flagUsages string) func(cmd *cobra.Command) error { +func UsageFunc( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagUsages string, +) func(cmd *cobra.Command) error { return func(cmd *cobra.Command) error { entries := UsageEntries{} @@ -109,20 +126,29 @@ func UsageFunc(IOStreams *iostreams.IOStreams, command *cobra.Command, flagUsage } } -func UsageFuncDefault(IOStreams *iostreams.IOStreams, command *cobra.Command) func(cmd *cobra.Command) error { +func UsageFuncDefault( + IOStreams *iostreams.IOStreams, + command *cobra.Command, +) func(cmd *cobra.Command) error { return UsageFunc(IOStreams, command, command.LocalFlags().FlagUsages()) } -func UsageFuncWithFilteredFlags(IOStreams *iostreams.IOStreams, command *cobra.Command, flagsToDisplay []string) func(cmd *cobra.Command) error { +func UsageFuncWithFilteredFlags( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagsToDisplay []string, +) func(cmd *cobra.Command) error { filteredFlags := filterFlagSet(*command.LocalFlags(), flagsToDisplay) return UsageFunc(IOStreams, command, filteredFlags.FlagUsages()) - } -func UsageFuncWithFilteredAndInheritedFlags(IOStreams *iostreams.IOStreams, command *cobra.Command, flagsToDisplay []string) func(cmd *cobra.Command) error { +func UsageFuncWithFilteredAndInheritedFlags( + IOStreams *iostreams.IOStreams, + command *cobra.Command, + flagsToDisplay []string, +) func(cmd *cobra.Command) error { return func(cmd *cobra.Command) error { - entries := UsageEntries{} entries.AddBasicUsage(IOStreams, command) entries.AddFilteredFlags(IOStreams, command, flagsToDisplay) @@ -133,7 +159,10 @@ func UsageFuncWithFilteredAndInheritedFlags(IOStreams *iostreams.IOStreams, comm } } -func UsageFuncWithInheritedFlagsOnly(IOStreams *iostreams.IOStreams, command *cobra.Command) func(cmd *cobra.Command) error { +func UsageFuncWithInheritedFlagsOnly( + IOStreams *iostreams.IOStreams, + command *cobra.Command, +) func(cmd *cobra.Command) error { return func(cmd *cobra.Command) error { entries := UsageEntries{} entries.AddBasicUsage(IOStreams, command) @@ -142,7 +171,6 @@ func UsageFuncWithInheritedFlagsOnly(IOStreams *iostreams.IOStreams, command *co entries.DisplayEntries(IOStreams.Out) return nil } - } func Dedent(s string) string { diff --git a/pkg/config/config.go b/pkg/config/config.go index 91d867a0..0e36cc24 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -48,11 +48,11 @@ func (c *Config) InitConfig() { c.File = configFile viper.SetConfigType("toml") viper.SetConfigFile(configFile) - viper.SetConfigPermissions(os.FileMode(0600)) + viper.SetConfigPermissions(os.FileMode(0o600)) // Try to change permissions manually, because we used to create files // with default permissions (0644) - err := os.Chmod(configFile, os.FileMode(0600)) + err := os.Chmod(configFile, os.FileMode(0o600)) if err != nil && !os.IsNotExist(err) { log.Fatalf("%s", err) } diff --git a/pkg/gen/gen_flags.go b/pkg/gen/gen_flags.go index b93daa71..1601273b 100644 --- a/pkg/gen/gen_flags.go +++ b/pkg/gen/gen_flags.go @@ -82,7 +82,7 @@ func main() { // Write the formatted source code to disk fmt.Printf("writing %s\n", pathOutput) - err = ioutil.WriteFile(pathOutput, formatted, 0644) + err = ioutil.WriteFile(pathOutput, formatted, 0o644) if err != nil { panic(err) } diff --git a/pkg/httpmock/stub.go b/pkg/httpmock/stub.go index 25648215..3b9066b2 100644 --- a/pkg/httpmock/stub.go +++ b/pkg/httpmock/stub.go @@ -8,8 +8,10 @@ import ( "strings" ) -type Matcher func(req *http.Request) bool -type Responder func(req *http.Request) (*http.Response, error) +type ( + Matcher func(req *http.Request) bool + Responder func(req *http.Request) (*http.Response, error) +) type Stub struct { matched bool diff --git a/pkg/httpmock/v4/stub.go b/pkg/httpmock/v4/stub.go new file mode 100644 index 00000000..3b9066b2 --- /dev/null +++ b/pkg/httpmock/v4/stub.go @@ -0,0 +1,67 @@ +package httpmock + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strings" +) + +type ( + Matcher func(req *http.Request) bool + Responder func(req *http.Request) (*http.Response, error) +) + +type Stub struct { + matched bool + Matcher Matcher + Responder Responder +} + +func REST(method, p string) Matcher { + return func(req *http.Request) bool { + if !strings.EqualFold(req.Method, method) { + return false + } + if req.URL.Path != "/"+p { + return false + } + return true + } +} + +func StringResponse(body string) Responder { + return func(req *http.Request) (*http.Response, error) { + return httpResponse(200, req, bytes.NewBufferString(body)), nil + } +} + +func JSONResponse(body interface{}) Responder { + return func(req *http.Request) (*http.Response, error) { + b, _ := json.Marshal(body) + return httpResponse(200, req, bytes.NewBuffer(b)), nil + } +} + +func ErrorResponse() Responder { + return func(req *http.Request) (*http.Response, error) { + return httpResponse(404, req, bytes.NewBufferString("")), nil + } +} + +func ErrorResponseWithBody(body interface{}) Responder { + return func(req *http.Request) (*http.Response, error) { + b, _ := json.Marshal(body) + return httpResponse(400, req, bytes.NewBuffer(b)), nil + } +} + +func httpResponse(status int, req *http.Request, body io.Reader) *http.Response { + return &http.Response{ + StatusCode: status, + Request: req, + Body: io.NopCloser(body), + Header: http.Header{}, + } +} diff --git a/pkg/iostreams/tty_size.go b/pkg/iostreams/tty_size.go index 767000ce..c6cbd9c6 100644 --- a/pkg/iostreams/tty_size.go +++ b/pkg/iostreams/tty_size.go @@ -1,4 +1,5 @@ -//+build !windows +//go:build !windows +// +build !windows package iostreams diff --git a/pkg/iostreams/tty_size_windows.go b/pkg/iostreams/tty_size_windows.go index 57fe397c..50c7c3cf 100644 --- a/pkg/iostreams/tty_size_windows.go +++ b/pkg/iostreams/tty_size_windows.go @@ -2,6 +2,7 @@ package iostreams import ( "errors" + "golang.org/x/term" ) diff --git a/pkg/open/open.go b/pkg/open/open.go index e1e70659..b77088df 100644 --- a/pkg/open/open.go +++ b/pkg/open/open.go @@ -37,7 +37,6 @@ func CanOpenBrowser() bool { } output, err := execCommand("xdg-settings", "get", "default-web-browser").Output() - if err != nil { return false } diff --git a/pkg/printers/table_printer.go b/pkg/printers/table_printer.go index bc4ed226..b019804d 100644 --- a/pkg/printers/table_printer.go +++ b/pkg/printers/table_printer.go @@ -65,7 +65,11 @@ func (t ttyTablePrinter) IsTTY() bool { return true } -func (t *ttyTablePrinter) AddField(s string, truncateFunc func(int, string) string, colorFunc func(string) string) { +func (t *ttyTablePrinter) AddField( + s string, + truncateFunc func(int, string) string, + colorFunc func(string) string, +) { if truncateFunc == nil { truncateFunc = text.Truncate } diff --git a/pkg/printers/template.go b/pkg/printers/template.go index 63c15907..56264841 100644 --- a/pkg/printers/template.go +++ b/pkg/printers/template.go @@ -52,7 +52,11 @@ func (p *GoTemplatePrinter) Print(ios *iostreams.IOStreams, data interface{}) er // It is way easier to debug this stuff when it shows up in // stdout instead of just stdin. So in addition to returning // a nice error, also print useful stuff with the writer. - fmt.Fprintf(ios.ErrOut, "Error executing template: %v. Printing more information for debugging the template:\n", err) + fmt.Fprintf( + ios.ErrOut, + "Error executing template: %v. Printing more information for debugging the template:\n", + err, + ) fmt.Fprintf(ios.ErrOut, "\ttemplate was:\n\t\t%v\n", p.rawTemplate) fmt.Fprintf(ios.ErrOut, "\traw data was:\n\t\t%v\n", string(dataM)) fmt.Fprintf(ios.ErrOut, "\tobject given to template engine was:\n\t\t%+v\n\n", out) diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index ada3ad05..77b717d2 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -17,8 +17,10 @@ import ( "github.com/algolia/cli/pkg/version" ) -const AppName = "cli" -const telemetryAnalyticsURL = "https://telemetry-proxy.algolia.com/" +const ( + AppName = "cli" + telemetryAnalyticsURL = "https://telemetry-proxy.algolia.com/" +) type telemetryMetadataKey struct{} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index abeabc4e..0b0438b4 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -57,8 +57,8 @@ func Differences(a, b []string) []string { // ToKebabCase converts a string to kebab case func ToKebabCase(str string) string { - var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") - var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + matchFirstCap := regexp.MustCompile("(.)([A-Z][a-z]+)") + matchAllCap := regexp.MustCompile("([a-z0-9])([A-Z])") snake := matchFirstCap.ReplaceAllString(str, "${1}-${2}") snake = matchAllCap.ReplaceAllString(snake, "${1}-${2}") @@ -78,7 +78,9 @@ func SliceToString(str []string) string { // based on https://github.com/watson/ci-info/blob/HEAD/index.js func IsCI() bool { - return os.Getenv("CI") != "" || // GitHub Actions, Travis CI, CircleCI, Cirrus CI, GitLab CI, AppVeyor, CodeShip, dsari + return os.Getenv( + "CI", + ) != "" || // GitHub Actions, Travis CI, CircleCI, Cirrus CI, GitLab CI, AppVeyor, CodeShip, dsari os.Getenv("CONTINUOUS_INTEGRATION") != "" || // Travis CI, Cirrus CI os.Getenv("BUILD_NUMBER") != "" || // Jenkins, TeamCity os.Getenv("CI_APP_ID") != "" || // Appflow diff --git a/test/helpers.go b/test/helpers.go index 98580726..a1fff497 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -49,7 +49,12 @@ func (s OutputStub) Run() error { return nil } -func NewFactory(isTTY bool, r *httpmock.Registry, cfg config.IConfig, in string) (*cmdutil.Factory, *CmdInOut) { +func NewFactory( + isTTY bool, + r *httpmock.Registry, + cfg config.IConfig, + in string, +) (*cmdutil.Factory, *CmdInOut) { io, stdin, stdout, stderr := iostreams.Test() io.SetStdoutTTY(isTTY) io.SetStdinTTY(isTTY) From a41e7f954fccf1f5a6e9c8ff1947f7c4863abbdf Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 6 Feb 2025 12:21:00 +0100 Subject: [PATCH 04/45] chore: Update Dockerfile Copying the binary into the Docker container doesn't work if the host CPU architecture is different (like an M1 mac). Since it's quick to build, we can just build the CLI inside Docker. --- Dockerfile | 17 +++++++-- Taskfile.yml | 102 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 872fd2aa..fc5d54e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,14 @@ +# Build the binary +FROM golang:1.23-alpine AS builder +WORKDIR /app +COPY . . +ARG VERSION=docker +RUN apk update && apk add --no-cache curl +RUN go mod download +RUN go install github.com/go-task/task/v3/cmd/task@latest +RUN task download-spec-file && VERSION=${VERSION} task build + FROM alpine -RUN apk update && apk upgrade && \ - apk add --no-cache ca-certificates -COPY algolia /bin/algolia -ENTRYPOINT ["/bin/algolia"] \ No newline at end of file +RUN apk update && apk add --no-cache ca-certificates +COPY --from=builder /app/algolia /bin/algolia +ENTRYPOINT ["/bin/algolia"] diff --git a/Taskfile.yml b/Taskfile.yml index 12bf24c3..903ac42f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,37 +1,113 @@ version: 3 output: prefixed +vars: + # The URL to the "old" Algolia docs repo + docs_remote: https://github.com/algolia/doc.git + # The temporary folder for the generated YML files + docs_local: docs + cli_ref_path: app_data/cli/commands + yml_folder: tmp tasks: build: desc: Build the binary deps: [generate] - cmds: - - go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version={{ .VERSION }}" -o algolia cmd/algolia/main.go + cmd: go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version={{ .VERSION }}" -o algolia cmd/algolia/main.go vars: - VERSION: main - generate: - desc: Generate command flags - internal: true - cmds: - - go generate ./... + VERSION: '{{ .VERSION | default "main" }}' test: desc: Run unit tests run: always - cmds: - - go test ./... + cmd: go test ./... lint: desc: Lint code - cmds: - - golangci-lint run + cmd: golangci-lint run format: desc: Format code cmds: - gofumpt -w pkg cmd test internal api - golines -w pkg cmd test internal api ci: - desc: Run everything + desc: Test, lint, and format aliases: - default deps: - build - test - lint + api-specs-pr: + desc: Update the flags for search and settings from the latest Search API spec + summary: | + This task downloads the latest Search API OpenAPI spec from the api-clients-automation repo, + generates the flags, and makes a new PR to the CLI GitHub repo. + deps: [download-spec-file, generate] + preconditions: + - git status --porcelain + cmds: + - | + original="$(git branch --show-current)" + git checkout -B {{ .branch }} + git add . + git commit --message "chore: update search api spec" + git push --force --set-upstream origin {{ .branch }} + gh pr list --base main --head {{ .branch }} | grep -q . || gh pr create --title '{{ .pr-title }}' --description '{{ .pr-description }}' + git switch "${original}" + vars: + branch: feat/api-specs + pr-title: "chore: Update Search API spec" + pr-description: "Update Search API spec" + env: + GIT_COMMITTER_NAME: algolia-ci + GIT_AUTHOR_NAME: algolia-ci + GIT_COMMITTER_EMAIL: noreply@algolia.com + GIT_AUTHOR_EMAIL: noreply@algolia.com + download-spec-file: + desc: Download the latest Search API spec from GitHub + cmd: curl -fsSL -o {{ .destination }} {{ .source }} + vars: + source: https://raw.githubusercontent.com/algolia/api-clients-automation/main/specs/bundled/search.yml + destination: ./api/specs/search.yml + generate: + desc: Generate command flags + internal: true + cmds: + - go generate ./... + update-docs: + desc: Update the CLI command reference in the Algolia docs + deps: + - clone-docs + - generate-command-reference + cmds: + - task: update-command-reference + - task: cleanup + clone-docs: + desc: Clone the Algolia docs + internal: true + cmd: git clone --depth=1 {{ .docs_remote }} {{ .docs_local }} + generate-command-reference: + desc: Generate updated YML files for the CLI command reference + internal: true + cmd: go run ./cmd/docs --app_data-path {{ .yml_folder }} + update-command-reference: + desc: Add the updated YML files to the docs + summary: | + This task clones the Algolia docs repo, + adds the updated CLI reference yml files to it, + and pushes a new PR to the GitHub repo. + internal: true + cmds: + - | + git -C {{ .docs_local }} checkout -B chore/cli-$(git rev-parse --short HEAD) + git -C {{ .docs_local }} rm "{{ .cli_ref_path }}/*.yml" + mkdir -p {{ .docs_local }}/{{ .cli_ref_path }} + mv {{ .yml_folder }}/*.yml {{ .docs_local }}/{{ .cli_ref_path }}/ + git -C {{ .docs_local }} add "{{ .cli_ref_path }}/*.yml" + git -C {{ .docs_local }} commit --message 'chore: Update CLI command reference' + env: + GIT_COMMITTER_NAME: algolia-ci + GIT_AUTHOR_NAME: algolia-ci + GIT_COMMITTER_EMAIL: noreply@algolia.com + GIT_AUTHOR_EMAIL: noreply@algolia.com + cleanup: + desc: Cleanup the docs files + internal: true + cmd: rm -rf {{ .docs_local }} {{ .yml_folder }} || true From 4f9aaefe6d824df847fdbe11dc24a5ab41778c6e Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 29 Jan 2025 19:50:00 +0100 Subject: [PATCH 05/45] feat: add V4 client package --- go.mod | 19 +++--- go.sum | 17 +---- pkg/cmd/factory/default.go | 55 ++++++---------- pkg/cmdutil/factory.go | 10 +-- pkg/cmdutil/valid_args.go | 31 ++++++++- pkg/httpmock/v4/registry.go | 101 +++++++++++++++++++++++++++++ pkg/httpmock/v4/stub.go | 6 +- test/v4/config.go | 89 ++++++++++++++++++++++++++ test/v4/helpers.go | 124 ++++++++++++++++++++++++++++++++++++ 9 files changed, 385 insertions(+), 67 deletions(-) create mode 100644 pkg/httpmock/v4/registry.go create mode 100644 test/v4/config.go create mode 100644 test/v4/helpers.go diff --git a/go.mod b/go.mod index d6d1aa21..195ca37f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/algolia/cli -go 1.23 +go 1.23.0 toolchain go1.23.4 @@ -8,7 +8,8 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.4.0 github.com/MakeNowJust/heredoc v1.0.0 - github.com/algolia/algoliasearch-client-go/v4 v4.12.1 + github.com/algolia/algoliasearch-client-go/v3 v3.31.4 + github.com/algolia/algoliasearch-client-go/v4 v4.12.0 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/briandowns/spinner v1.23.2 github.com/cli/safeexec v1.0.1 @@ -20,15 +21,16 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.2 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 - github.com/spf13/pflag v1.0.6 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.10.0 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c - golang.org/x/term v0.29.0 + golang.org/x/term v0.28.0 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/client-go v0.32.1 @@ -57,7 +59,6 @@ require ( github.com/magiconair/properties v1.8.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -70,10 +71,10 @@ require ( github.com/spf13/cast v1.7.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index b49afe0e..fb53b635 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,10 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/algolia/algoliasearch-client-go/v3 v3.31.4 h1:UJhx6AhZCYf0qZygDz2c1x1+1q2q2sfzsRaQM6yswWk= +github.com/algolia/algoliasearch-client-go/v3 v3.31.4/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= github.com/algolia/algoliasearch-client-go/v4 v4.12.0 h1:YbLMyYZ7ohBTCEBIl3frF2Ga92ulGFev1tGe8SopF7Y= github.com/algolia/algoliasearch-client-go/v4 v4.12.0/go.mod h1:UsYoWx3gl5nyWalsOI85b7NGW4t1uaN05vydu9aPfGM= -github.com/algolia/algoliasearch-client-go/v4 v4.12.1 h1:eBQ88S1byCry69iB/PqWyps8kUSfrPtTBoWFo44OnyM= -github.com/algolia/algoliasearch-client-go/v4 v4.12.1/go.mod h1:UsYoWx3gl5nyWalsOI85b7NGW4t1uaN05vydu9aPfGM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= @@ -140,8 +140,6 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -162,12 +160,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34= -golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -186,22 +180,16 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -215,6 +203,7 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index dbc00642..f8b86f1d 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -3,8 +3,8 @@ package factory import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/call" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" "github.com/algolia/cli/api/crawler" @@ -20,7 +20,6 @@ func New(appVersion string, cfg config.IConfig) *cmdutil.Factory { } f.IOStreams = ioStreams(f) f.SearchClient = searchClient(f, appVersion) - f.V4SearchClient = v4searchClient(f, appVersion) f.CrawlerClient = crawlerClient(f) return f @@ -31,29 +30,8 @@ func ioStreams(_ *cmdutil.Factory) *iostreams.IOStreams { return io } -func searchClient(f *cmdutil.Factory, appVersion string) func() (*search.Client, error) { - return func() (*search.Client, error) { - appID, err := f.Config.Profile().GetApplicationID() - if err != nil { - return nil, err - } - APIKey, err := f.Config.Profile().GetAPIKey() - if err != nil { - return nil, err - } - - clientCfg := search.Configuration{ - AppID: appID, - APIKey: APIKey, - ExtraUserAgent: fmt.Sprintf("Algolia CLI (%s)", appVersion), - Hosts: f.Config.Profile().GetSearchHosts(), - } - return search.NewClientWithConfig(clientCfg), nil - } -} - -func v4searchClient(f *cmdutil.Factory, appVersion string) func() (*v4.APIClient, error) { - return func() (*v4.APIClient, error) { +func searchClient(f *cmdutil.Factory, appVersion string) func() (*search.APIClient, error) { + return func() (*search.APIClient, error) { appID, err := f.Config.Profile().GetApplicationID() if err != nil { return nil, err @@ -63,20 +41,29 @@ func v4searchClient(f *cmdutil.Factory, appVersion string) func() (*v4.APIClient return nil, err } - defaultClient, _ := v4.NewClient(appID, apiKey) + defaultClient, _ := search.NewClient(appID, apiKey) defaultUserAgent := defaultClient.GetConfiguration().UserAgent - // TODO: Doesn't support custom `search_hosts` yet. - // To support it, it's best to transform the GetSearchHosts() function - clientConf := v4.SearchConfiguration{ + var hosts []transport.StatefulHost + for _, host := range f.Config.Profile().GetSearchHosts() { + statefulHost := transport.NewStatefulHost("https", host, call.IsReadWrite) + hosts = append(hosts, statefulHost) + } + + clientConf := search.SearchConfiguration{ Configuration: transport.Configuration{ - AppID: appID, - ApiKey: apiKey, - UserAgent: defaultUserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), + AppID: appID, + ApiKey: apiKey, + UserAgent: defaultUserAgent + fmt.Sprintf( + "Algolia CLI (%s)", + appVersion, + ), + Hosts: hosts, + ExposeIntermediateNetworkErrors: true, }, } - return v4.NewClientWithConfig(clientConf) + return search.NewClientWithConfig(clientConf) } } diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index 93fa1e93..db2c7996 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/config" @@ -13,10 +14,11 @@ import ( ) type Factory struct { - IOStreams *iostreams.IOStreams - Config config.IConfig - SearchClient func() (*search.Client, error) - CrawlerClient func() (*crawler.Client, error) + IOStreams *iostreams.IOStreams + Config config.IConfig + SearchClient func() (*search.Client, error) + V4SearchClient func() (*v4.APIClient, error) + CrawlerClient func() (*crawler.Client, error) ExecutableName string } diff --git a/pkg/cmdutil/valid_args.go b/pkg/cmdutil/valid_args.go index 93825061..d9989fd9 100644 --- a/pkg/cmdutil/valid_args.go +++ b/pkg/cmdutil/valid_args.go @@ -4,12 +4,15 @@ import ( "fmt" "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/api/crawler" "github.com/spf13/cobra" ) // IndexNames returns a function to list the index names from the given search client. -func IndexNames(clientF func() (*search.Client, error)) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func IndexNames( + clientF func() (*search.Client, error), +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { client, err := clientF() if err != nil { @@ -28,8 +31,32 @@ func IndexNames(clientF func() (*search.Client, error)) func(cmd *cobra.Command, } } +// IndexNames returns a function to list the index names from the given search client. +func V4IndexNames( + clientF func() (*v4.APIClient, error), +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + client, err := clientF() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + res, err := client.ListIndices(client.NewApiListIndicesRequest()) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + names := make([]string, 0, len(res.Items)) + for _, index := range res.Items { + names = append(names, index.Name) + } + return names, cobra.ShellCompDirectiveNoFileComp + } +} + // CrawlerIDs returns a function to list the crawler IDs from the given crawler client. -func CrawlerIDs(clientF func() (*crawler.Client, error)) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +func CrawlerIDs( + clientF func() (*crawler.Client, error), +) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { client, err := clientF() if err != nil { diff --git a/pkg/httpmock/v4/registry.go b/pkg/httpmock/v4/registry.go new file mode 100644 index 00000000..3e942097 --- /dev/null +++ b/pkg/httpmock/v4/registry.go @@ -0,0 +1,101 @@ +package httpmock + +import ( + "fmt" + "net/http" + "sync" + "time" +) + +type Registry struct { + mu sync.Mutex + stubs []*Stub + Requests []*http.Request +} + +func (r *Registry) Register(m Matcher, resp Responder) { + r.stubs = append(r.stubs, &Stub{ + Matcher: m, + Responder: resp, + }) +} + +type Testing interface { + Errorf(string, ...interface{}) + Helper() +} + +func (r *Registry) Verify(t Testing) { + n := 0 + for _, s := range r.stubs { + if !s.matched { + n++ + } + } + if n > 0 { + t.Helper() + t.Errorf("%d unmatched HTTP stubs", n) + } +} + +// Request satisfies Requester interface +func (r *Registry) Request( + req *http.Request, + _ time.Duration, + _ time.Duration, +) (*http.Response, error) { + var stub *Stub + + r.mu.Lock() + for _, s := range r.stubs { + if s.matched || !s.Matcher(req) { + continue + } + + stub = s + break + } + if stub != nil { + stub.matched = true + } + + if stub == nil { + r.mu.Unlock() + return nil, fmt.Errorf("no registered stubs matched %v", req) + } + + r.Requests = append(r.Requests, req) + r.mu.Unlock() + + return stub.Responder(req) +} + +// RoundTrip satisfies http.RoundTripper +func (r *Registry) RoundTrip(req *http.Request) (*http.Response, error) { + var stub *Stub + + r.mu.Lock() + for _, s := range r.stubs { + if s.matched || !s.Matcher(req) { + continue + } + if stub != nil { + r.mu.Unlock() + return nil, fmt.Errorf("more than 1 stub matched %v", req) + } + stub = s + } + if stub != nil { + stub.matched = true + } + + if stub == nil { + r.mu.Unlock() + return nil, fmt.Errorf("no registered stubs matched %v", req) + } + + r.Requests = append(r.Requests, req) + r.mu.Unlock() + + return stub.Responder(req) +} diff --git a/pkg/httpmock/v4/stub.go b/pkg/httpmock/v4/stub.go index 3b9066b2..25648215 100644 --- a/pkg/httpmock/v4/stub.go +++ b/pkg/httpmock/v4/stub.go @@ -8,10 +8,8 @@ import ( "strings" ) -type ( - Matcher func(req *http.Request) bool - Responder func(req *http.Request) (*http.Response, error) -) +type Matcher func(req *http.Request) bool +type Responder func(req *http.Request) (*http.Response, error) type Stub struct { matched bool diff --git a/test/v4/config.go b/test/v4/config.go new file mode 100644 index 00000000..38e1ebed --- /dev/null +++ b/test/v4/config.go @@ -0,0 +1,89 @@ +package test + +import ( + "github.com/algolia/cli/pkg/config" +) + +type ConfigStub struct { + CurrentProfile config.Profile + profiles []*config.Profile +} + +func (c *ConfigStub) InitConfig() {} + +func (c *ConfigStub) Profile() *config.Profile { + return &c.CurrentProfile +} + +func (c *ConfigStub) Default() *config.Profile { + return &c.CurrentProfile +} + +func (c *ConfigStub) ConfiguredProfiles() []*config.Profile { + return c.profiles +} + +func (c *ConfigStub) ProfileNames() []string { + names := make([]string, 0, len(c.ConfiguredProfiles())) + for _, profile := range c.ConfiguredProfiles() { + names = append(names, profile.Name) + } + return names +} + +func (c *ConfigStub) ProfileExists(name string) bool { + for _, profile := range c.ConfiguredProfiles() { + if profile.Name == name { + return true + } + } + return false +} + +func (c *ConfigStub) ApplicationIDExists(appID string) (bool, string) { + for _, profile := range c.ConfiguredProfiles() { + if profile.ApplicationID == appID { + return true, profile.Name + } + } + return false, "" +} + +func (c *ConfigStub) RemoveProfile(name string) error { + for i, profile := range c.ConfiguredProfiles() { + if profile.Name == name { + c.profiles = append(c.profiles[:i], c.profiles[i+1:]...) + return nil + } + } + return nil +} + +func (c *ConfigStub) SetDefaultProfile(name string) error { + for _, profile := range c.ConfiguredProfiles() { + if profile.Name == name { + profile.Default = true + } else { + profile.Default = false + } + } + return nil +} + +func NewConfigStubWithProfiles(p []*config.Profile) *ConfigStub { + return &ConfigStub{ + CurrentProfile: *p[0], + profiles: p, + } +} + +func NewDefaultConfigStub() *ConfigStub { + return NewConfigStubWithProfiles([]*config.Profile{ + { + Name: "default", + ApplicationID: "default", + AdminAPIKey: "default", + Default: true, + }, + }) +} diff --git a/test/v4/helpers.go b/test/v4/helpers.go new file mode 100644 index 00000000..a68b276f --- /dev/null +++ b/test/v4/helpers.go @@ -0,0 +1,124 @@ +package test + +import ( + "bytes" + "io" + "net/http" + + "github.com/google/shlex" + "github.com/spf13/cobra" + + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" + "github.com/algolia/cli/api/crawler" + "github.com/algolia/cli/pkg/cmdutil" + "github.com/algolia/cli/pkg/config" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/iostreams" +) + +type CmdInOut struct { + InBuf *bytes.Buffer + OutBuf *bytes.Buffer + ErrBuf *bytes.Buffer +} + +func (c CmdInOut) String() string { + return c.OutBuf.String() +} + +func (c CmdInOut) Stderr() string { + return c.ErrBuf.String() +} + +type OutputStub struct { + Out []byte + Error error +} + +func (s OutputStub) Output() ([]byte, error) { + if s.Error != nil { + return s.Out, s.Error + } + return s.Out, nil +} + +func (s OutputStub) Run() error { + if s.Error != nil { + return s.Error + } + return nil +} + +func NewFactory( + isTTY bool, + r *httpmock.Registry, + cfg config.IConfig, + in string, +) (*cmdutil.Factory, *CmdInOut) { + io, stdin, stdout, stderr := iostreams.Test() + io.SetStdoutTTY(isTTY) + io.SetStdinTTY(isTTY) + io.SetStderrTTY(isTTY) + + if in != "" { + stdin.WriteString(in) + } + + f := &cmdutil.Factory{ + IOStreams: io, + } + + if r != nil { + f.V4SearchClient = func() (*search.APIClient, error) { + cfg := search.SearchConfiguration{ + Configuration: transport.Configuration{ + AppID: "default", + ApiKey: "default", + Requester: r, + }, + } + return search.NewClientWithConfig(cfg) + } + f.CrawlerClient = func() (*crawler.Client, error) { + return crawler.NewClientWithHTTPClient("id", "key", &http.Client{ + Transport: r, + }), nil + } + } + + if cfg != nil { + f.Config = cfg + } else { + f.Config = &config.Config{} + } + + return f, &CmdInOut{ + InBuf: stdin, + OutBuf: stdout, + ErrBuf: stderr, + } +} + +func Execute(cmd *cobra.Command, cli string, inOut *CmdInOut) (*CmdInOut, error) { + argv, err := shlex.Split(cli) + if err != nil { + return nil, err + } + cmd.SetArgs(argv) + + if inOut.InBuf != nil { + cmd.SetIn(inOut.InBuf) + } else { + cmd.SetIn(&bytes.Buffer{}) + } + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + + _, err = cmd.ExecuteC() + if err != nil { + return nil, err + } + + return inOut, nil +} From 73300d997ba22c8b90195e9a57980cd7b89dfeed Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 29 Jan 2025 19:52:57 +0100 Subject: [PATCH 06/45] feat: migrate indices list --- pkg/cmd/indices/delete/delete.go | 223 +++++++++++++------------------ pkg/cmd/indices/list/list.go | 10 +- 2 files changed, 99 insertions(+), 134 deletions(-) diff --git a/pkg/cmd/indices/delete/delete.go b/pkg/cmd/indices/delete/delete.go index 4f5dc26f..119bcbd2 100644 --- a/pkg/cmd/indices/delete/delete.go +++ b/pkg/cmd/indices/delete/delete.go @@ -6,8 +6,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -20,7 +19,7 @@ type DeleteOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Indices []string DoConfirm bool @@ -32,7 +31,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -40,7 +39,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "delete ", Args: cobra.MinimumNArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteIndex", }, @@ -54,7 +53,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co $ algolia indices delete MOVIES # Delete the index named "MOVIES" and its replicas - $ algolia indices delete MOVIES --includeReplicas + $ algolia indices delete MOVIES --include-replicas # Delete the index named "MOVIES", skipping the confirmation prompt $ algolia indices delete MOVIES -y @@ -84,7 +83,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") cmd.Flags(). - BoolVarP(&opts.IncludeReplicas, "includeReplicas", "r", false, "delete replica indices too") + BoolVarP(&opts.IncludeReplicas, "include-replicas", "r", false, "delete replica indices too") return cmd } @@ -95,13 +94,27 @@ func runDeleteCmd(opts *DeleteOptions) error { return err } + // For nicer output + indexSingularOrPlural := "index" + if len(opts.Indices) > 1 { + indexSingularOrPlural = "indices" + } + if opts.DoConfirm { var confirmed bool - msg := "Are you sure you want to delete the indices %q?" + msg := fmt.Sprintf( + "Are you sure you want to delete the %s %q?", + indexSingularOrPlural, + strings.Join(opts.Indices, ", "), + ) if opts.IncludeReplicas { - msg = "Are you sure you want to delete the indices %q including their replicas?" + msg = fmt.Sprintf( + "Are you sure you want to delete the %s %q including their replicas?", + indexSingularOrPlural, + strings.Join(opts.Indices, ", "), + ) } - err := prompt.Confirm(fmt.Sprintf(msg, strings.Join(opts.Indices, ", ")), &confirmed) + err := prompt.Confirm(msg, &confirmed) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -110,72 +123,69 @@ func runDeleteCmd(opts *DeleteOptions) error { } } - indices := make([]*search.Index, 0, len(opts.Indices)) - for _, indexName := range opts.Indices { - index := client.InitIndex(indexName) - exists, err := index.Exists() - if err != nil || !exists { - return fmt.Errorf("index %q does not exist", indexName) + for _, index := range opts.Indices { + // Equivalent to `client.IndexExists` but provides settings already + settings, err := client.GetSettings(client.NewApiGetSettingsRequest(index)) + if err != nil { + return fmt.Errorf("can't get settings of index %s: %w", index, err) } - indices = append(indices, index) - if opts.IncludeReplicas { - settings, err := index.GetSettings() + if settings.HasPrimary() { + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Detaching replica index %s from its primary", index), + ) + err = detachReplica(index, *settings.Primary, client) if err != nil { - return fmt.Errorf("can't get settings of index %q: %w", indexName, err) - } - - replicas := settings.Replicas - for _, replicaName := range replicas.Get() { - pattern := regexp.MustCompile(`^virtual\((.*)\)$`) - matches := pattern.FindStringSubmatch(replicaName) - if len(matches) > 1 { - replicaName = matches[1] - } - replica := client.InitIndex(replicaName) - indices = append(indices, replica) + return fmt.Errorf("can't detach index %s: %w", index, err) } + opts.IO.StopProgressIndicator() } - } - for _, index := range indices { - var mustWait bool + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Deleting index %s", index), + ) + res, err := client.DeleteIndex(client.NewApiDeleteIndexRequest(index)) + if err != nil { + return fmt.Errorf("can't delete index %s: %w", index, err) + } - if opts.IncludeReplicas { - settings, err := index.GetSettings() + if opts.IncludeReplicas && len(settings.Replicas) > 0 { + // Wait for primary to be deleted, otherwise deleting replicas might fail + _, err := client.WaitForTask(index, res.TaskID) if err != nil { - return fmt.Errorf("failed to get settings of index %q: %w", index.GetName(), err) + return fmt.Errorf("error waiting for index %s to be deleted: %w", index, err) } - if len(settings.Replicas.Get()) > 0 { - mustWait = true - } - } - res, err := index.Delete() - - // Otherwise, the replica indices might not be 'fully detached' yet. - if mustWait { - _ = res.Wait() - } + for _, replica := range settings.Replicas { + // Virtual replicas have name `virtual(replica)`... + pattern := regexp.MustCompile(`^virtual\((.*)\)$`) + matches := pattern.FindStringSubmatch(replica) + if len(matches) > 1 { + // But when deleting, we need the bare name + replica = matches[1] + // For printing the summary + opts.Indices = append(opts.Indices, replica) + } - if err != nil { - opts.IO.StartProgressIndicatorWithLabel( - fmt.Sprint("Deleting replica index ", index.GetName()), - ) - err := deleteReplicaIndex(client, index) - opts.IO.StopProgressIndicator() - if err != nil { - return fmt.Errorf("failed to delete index %q: %w", index.GetName(), err) + opts.IO.UpdateProgressIndicatorLabel( + fmt.Sprintf("Deleting replica %s", index), + ) + _, err = client.DeleteIndex(client.NewApiDeleteIndexRequest(replica)) + if err != nil { + return fmt.Errorf("can't delete replica %s: %w", replica, err) + } } } + opts.IO.StopProgressIndicator() } cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { fmt.Fprintf( opts.IO.Out, - "%s Deleted indices %s\n", + "%s Deleted %s %s\n", cs.SuccessIcon(), + indexSingularOrPlural, strings.Join(opts.Indices, ", "), ) } @@ -183,92 +193,40 @@ func runDeleteCmd(opts *DeleteOptions) error { return nil } -// Delete a replica index. -func deleteReplicaIndex(client *search.Client, replicaIndex *search.Index) error { - replicaName := replicaIndex.GetName() - primaryName, err := findPrimaryIndex(replicaIndex) - if err != nil { - return fmt.Errorf("can't find primary index for %q: %w", replicaName, err) - } - - err = detachReplicaIndex(replicaName, primaryName, client) - if err != nil { - return fmt.Errorf( - "can't unlink replica index %s from primary index %s: %w", - replicaName, - primaryName, - err, - ) - } - - _, err = replicaIndex.Delete() - if err != nil { - return fmt.Errorf("can't delete replica index %q: %w", replicaName, err) - } - - return nil -} - -// Find the primary index of a replica index -func findPrimaryIndex(replicaIndex *search.Index) (string, error) { - replicaName := replicaIndex.GetName() - settings, err := replicaIndex.GetSettings() +// Remove replica from `replicas` settings of the primary index +func detachReplica(replica string, primary string, client *search.APIClient) error { + settings, err := client.GetSettings(client.NewApiGetSettingsRequest(primary)) if err != nil { - return "", fmt.Errorf("can't get settings of replica index %q: %w", replicaName, err) + return fmt.Errorf("can't get settings of primary index %s: %w", primary, err) } - primary := settings.Primary - if primary == nil { - return "", fmt.Errorf("index %s doesn't have a primary", replicaName) + if isVirtual(settings.Replicas, replica) { + replica = fmt.Sprintf("virtual(%s)", replica) } - return primary.Get(), nil -} + newReplicas := removeReplica(settings.Replicas, replica) -// Remove replica from `replicas` settings of the primary index -func detachReplicaIndex(replicaName string, primaryName string, client *search.Client) error { - primaryIndex := client.InitIndex(primaryName) - settings, err := primaryIndex.GetSettings() + res, err := client.SetSettings( + client.NewApiSetSettingsRequest( + primary, + search.NewIndexSettings().SetReplicas(newReplicas), + ), + ) if err != nil { - return fmt.Errorf("can't get settings of primary index %q: %w", primaryName, err) - } - - replicas := settings.Replicas.Get() - isVirtual := isVirtualReplica(replicas, replicaName) - if isVirtual { - replicaName = fmt.Sprintf("virtual(%s)", replicaName) + return fmt.Errorf("can't detach replica %s from its primary %s: %w", replica, primary, err) } - indexOfReplica := findIndex(replicas, replicaName) - // Delete the replica at position `indexOfReplica` from the array - replicas = append(replicas[:indexOfReplica], replicas[indexOfReplica+1:]...) - - res, err := primaryIndex.SetSettings( - search.Settings{ - Replicas: opt.Replicas(replicas...), - }, - ) + _, err = client.WaitForTask(primary, res.TaskID) if err != nil { - return fmt.Errorf("can't update settings of index %q: %w", primaryName, err) + return fmt.Errorf("can't wait for updating the primary's settings: %w", err) } - // Wait until the settings are updated, else a subsequent `delete` will fail. - _ = res.Wait() return nil } -// Find the index of the string `target` in the array `arr` -func findIndex(arr []string, target string) int { - for i, v := range arr { - if v == target { - return i - } - } - return -1 -} - -func isVirtualReplica(replicas []string, replicaName string) bool { - pattern := regexp.MustCompile(fmt.Sprintf(`^virtual\(%s\)$`, replicaName)) +// isVirtual checks whether an index is a virtual replica +func isVirtual(replicas []string, name string) bool { + pattern := regexp.MustCompile(fmt.Sprintf(`^virtual\(%s\)$`, name)) for _, i := range replicas { matches := pattern.MatchString(i) @@ -279,3 +237,14 @@ func isVirtualReplica(replicas []string, replicaName string) bool { return false } + +// removeReplica returns a new slice without a replica +func removeReplica(replicas []string, name string) []string { + for i, v := range replicas { + if v == name { + // Return a new slice without the given replica + return append(replicas[:i], replicas[i+1:]...) + } + } + return replicas +} diff --git a/pkg/cmd/indices/list/list.go b/pkg/cmd/indices/list/list.go index 423934e0..e9d0178d 100644 --- a/pkg/cmd/indices/list/list.go +++ b/pkg/cmd/indices/list/list.go @@ -31,7 +31,7 @@ func NewListCmd(f *cmdutil.Factory) *cobra.Command { opts := &ListOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ @@ -104,15 +104,11 @@ func runListCmd(opts *ListOptions) error { } updatedAt, err := parseTime(index.UpdatedAt) if err != nil { - return fmt.Errorf("can't parse %s into a time struct", index.UpdatedAt) + return fmt.Errorf("can't parse %s into a time struct.", index.UpdatedAt) } createdAt, err := parseTime(index.CreatedAt) if err != nil { - return fmt.Errorf("can't parse %s into a time struct", index.CreatedAt) - } - // Prevent integer overflow - if index.DataSize < 0 { - index.DataSize = 0 + return fmt.Errorf("can't parse %s into a time struct.", index.CreatedAt) } table.AddField(index.Name, nil, nil) table.AddField(humanize.Comma(int64(index.Entries)), nil, nil) From c97d380f836407f9309236c5c9e7d1d4713a9e72 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 30 Jan 2025 17:29:54 +0100 Subject: [PATCH 07/45] feat: migrate indices copy This commit also re-enables support for the `--wait` flag, which was commented out. --- pkg/cmd/indices/copy/copy.go | 70 ++++++++++++++++++++++--------- pkg/cmd/indices/copy/copy_test.go | 52 ++++++++++++++++++++--- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/pkg/cmd/indices/copy/copy.go b/pkg/cmd/indices/copy/copy.go index f10c8f92..9eb117c8 100644 --- a/pkg/cmd/indices/copy/copy.go +++ b/pkg/cmd/indices/copy/copy.go @@ -6,8 +6,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -21,7 +20,7 @@ type CopyOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) SourceIndex string DestinationIndex string @@ -37,7 +36,7 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman opts := &CopyOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -45,7 +44,7 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "copy ", Args: validators.ExactArgs(2), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings,editSettings,browse,addObject", }, @@ -75,7 +74,9 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -89,7 +90,8 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman } cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") - cmd.Flags().StringSliceVarP(&opts.Scope, "scope", "s", []string{}, "scope to copy (default: all)") + cmd.Flags(). + StringSliceVarP(&opts.Scope, "scope", "s", []string{}, "scope to copy (default: all)") cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") _ = cmd.RegisterFlagCompletionFunc("scope", @@ -109,13 +111,22 @@ func runCopyCmd(opts *CopyOptions) error { } var scopesDesc string + var scopes []search.ScopeType if len(opts.Scope) > 0 { scopesDesc = strings.Join(opts.Scope, ",") + for _, s := range opts.Scope { + scopes = append(scopes, search.ScopeType(s)) + } } else { scopesDesc = "records, settings, synonyms, and rules" } - message := fmt.Sprintf("Are you sure you want to copy %s from %s to %s?", scopesDesc, opts.SourceIndex, opts.DestinationIndex) + message := fmt.Sprintf( + "Are you sure you want to copy %s from %s to %s?", + scopesDesc, + opts.SourceIndex, + opts.DestinationIndex, + ) if opts.DoConfirm { var confirmed bool @@ -133,25 +144,46 @@ func runCopyCmd(opts *CopyOptions) error { } } - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Copying %s from %s to %s", scopesDesc, opts.SourceIndex, opts.DestinationIndex)) - _, err = client.CopyIndex(opts.SourceIndex, opts.DestinationIndex, opt.Scopes(opts.Scope...)) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf( + "Copying %s from %s to %s", + scopesDesc, + opts.SourceIndex, + opts.DestinationIndex, + ), + ) + res, err := client.OperationIndex( + client.NewApiOperationIndexRequest( + opts.SourceIndex, + search. + NewEmptyOperationIndexParams(). + SetDestination(opts.DestinationIndex). + SetOperation(search.OPERATION_TYPE_COPY). + SetScope(scopes))) if err != nil { return err } - // Wait() is broken right now on copy index - // if opts.Wait { - // opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") - // err = client.WaitTask(res.TaskID) - // if err != nil { - // return err - // } - // } + if opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for task") + _, err = client.WaitForTask(opts.DestinationIndex, res.TaskID) + if err != nil { + return err + } + } + opts.IO.StopProgressIndicator() cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Copied %s from %s to %s\n", cs.SuccessIcon(), scopesDesc, opts.SourceIndex, opts.DestinationIndex) + fmt.Fprintf( + opts.IO.Out, + "%s Copied %s from %s to %s\n", + cs.SuccessIcon(), + scopesDesc, + opts.SourceIndex, + opts.DestinationIndex, + ) } return nil diff --git a/pkg/cmd/indices/copy/copy_test.go b/pkg/cmd/indices/copy/copy_test.go index f081c5cf..60afb7b0 100644 --- a/pkg/cmd/indices/copy/copy_test.go +++ b/pkg/cmd/indices/copy/copy_test.go @@ -3,15 +3,15 @@ package copy import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewCopyCmd(t *testing.T) { @@ -42,7 +42,7 @@ func TestNewCopyCmd(t *testing.T) { }, }, { - name: "specifying scopes", + name: "with scope: settings", cli: "foo bar --scope settings", tty: true, wantsErr: false, @@ -54,6 +54,45 @@ func TestNewCopyCmd(t *testing.T) { Wait: true, }, }, + { + name: "with scope: synonyms", + cli: "foo bar --scope synonyms", + tty: true, + wantsErr: false, + wantsOpts: CopyOptions{ + DoConfirm: true, + SourceIndex: "foo", + DestinationIndex: "bar", + Scope: []string{"synonyms"}, + Wait: true, + }, + }, + { + name: "with scope: rules", + cli: "foo bar --scope rules", + tty: true, + wantsErr: false, + wantsOpts: CopyOptions{ + DoConfirm: true, + SourceIndex: "foo", + DestinationIndex: "bar", + Scope: []string{"rules"}, + Wait: true, + }, + }, + { + name: "with multiple scope", + cli: "foo bar --scope 'rules,synonyms,settings'", + tty: true, + wantsErr: false, + wantsOpts: CopyOptions{ + DoConfirm: true, + SourceIndex: "foo", + DestinationIndex: "bar", + Scope: []string{"rules", "synonyms", "settings"}, + Wait: true, + }, + }, } for _, tt := range tests { @@ -134,7 +173,10 @@ func Test_runCreateCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/indexes/foo/operation"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/operation"), + httpmock.JSONResponse(search.UpdatedAtResponse{}), + ) defer r.Verify(t) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From f03e1f0e8725960877f0bcc0210332c5a70ce49a Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 30 Jan 2025 18:02:32 +0100 Subject: [PATCH 08/45] feat: migrate indices move command --- pkg/cmd/indices/move/move.go | 41 +++++++++++++++++++++++-------- pkg/cmd/indices/move/move_test.go | 11 ++++++--- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/indices/move/move.go b/pkg/cmd/indices/move/move.go index d95c451e..dcd6dee4 100644 --- a/pkg/cmd/indices/move/move.go +++ b/pkg/cmd/indices/move/move.go @@ -5,7 +5,7 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -19,7 +19,7 @@ type MoveOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) SourceIndex string DestinationIndex string @@ -34,7 +34,7 @@ func NewMoveCmd(f *cmdutil.Factory, runF func(*MoveOptions) error) *cobra.Comman opts := &MoveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -42,7 +42,7 @@ func NewMoveCmd(f *cmdutil.Factory, runF func(*MoveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "move ", Args: validators.ExactArgs(2), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "addObject", }, @@ -60,7 +60,9 @@ func NewMoveCmd(f *cmdutil.Factory, runF func(*MoveOptions) error) *cobra.Comman if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -86,7 +88,11 @@ func runMoveCmd(opts *MoveOptions) error { } cs := opts.IO.ColorScheme() - message := fmt.Sprintf("Are you sure you want to move %s to %s?", cs.Bold(opts.SourceIndex), cs.Bold(opts.DestinationIndex)) + message := fmt.Sprintf( + "Are you sure you want to move %s to %s?", + cs.Bold(opts.SourceIndex), + cs.Bold(opts.DestinationIndex), + ) if opts.DoConfirm { var confirmed bool @@ -103,8 +109,17 @@ func runMoveCmd(opts *MoveOptions) error { } } - opts.IO.StartProgressIndicatorWithLabel(fmt.Sprintf("Moving %s to %s", cs.Bold(opts.SourceIndex), cs.Bold(opts.DestinationIndex))) - res, err := client.MoveIndex(opts.SourceIndex, opts.DestinationIndex) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Moving %s to %s", cs.Bold(opts.SourceIndex), cs.Bold(opts.DestinationIndex)), + ) + res, err := client.OperationIndex( + client.NewApiOperationIndexRequest( + opts.SourceIndex, + search.NewEmptyOperationIndexParams(). + SetDestination(opts.DestinationIndex). + SetOperation(search.OPERATION_TYPE_MOVE), + ), + ) if err != nil { opts.IO.StopProgressIndicator() return err @@ -112,7 +127,7 @@ func runMoveCmd(opts *MoveOptions) error { if opts.Wait { opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") - err = client.InitIndex(opts.DestinationIndex).WaitTask(res.TaskID) + _, err := client.WaitForTask(opts.DestinationIndex, res.TaskID) if err != nil { opts.IO.StopProgressIndicator() return err @@ -122,7 +137,13 @@ func runMoveCmd(opts *MoveOptions) error { opts.IO.StopProgressIndicator() if opts.IO.IsStdoutTTY() { - fmt.Fprintf(opts.IO.Out, "%s Moved %s to %s\n", cs.SuccessIcon(), cs.Bold(opts.SourceIndex), cs.Bold(opts.DestinationIndex)) + fmt.Fprintf( + opts.IO.Out, + "%s Moved %s to %s\n", + cs.SuccessIcon(), + cs.Bold(opts.SourceIndex), + cs.Bold(opts.DestinationIndex), + ) } return nil diff --git a/pkg/cmd/indices/move/move_test.go b/pkg/cmd/indices/move/move_test.go index 2f026003..94ed7024 100644 --- a/pkg/cmd/indices/move/move_test.go +++ b/pkg/cmd/indices/move/move_test.go @@ -3,15 +3,15 @@ package move import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewMoveCmd(t *testing.T) { @@ -122,7 +122,10 @@ func Test_runMoveCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", "1/indexes/foo/operation"), httpmock.JSONResponse(search.UpdateTaskRes{})) + r.Register( + httpmock.REST("POST", "1/indexes/foo/operation"), + httpmock.JSONResponse(search.UpdatedAtResponse{}), + ) defer r.Verify(t) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From 024f3f780aa1bedfac6d4aa7fa6c53b25d911f83 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 30 Jan 2025 18:39:31 +0100 Subject: [PATCH 09/45] feat: migrate indices clear --- pkg/cmd/indices/clear/clear.go | 20 +++++++++++++------- pkg/cmd/indices/clear/clear_test.go | 11 +++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/indices/clear/clear.go b/pkg/cmd/indices/clear/clear.go index 0bf68ef9..00096c75 100644 --- a/pkg/cmd/indices/clear/clear.go +++ b/pkg/cmd/indices/clear/clear.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -18,7 +18,7 @@ type ClearOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Index string DoConfirm bool @@ -29,7 +29,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm opts := &ClearOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -37,7 +37,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm cmd := &cobra.Command{ Use: "clear ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteIndex", }, @@ -54,7 +54,9 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } @@ -75,7 +77,10 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm func runClearCmd(opts *ClearOptions) error { if opts.DoConfirm { var confirmed bool - err := prompt.Confirm(fmt.Sprintf("Are you sure you want to clear the index %q?", opts.Index), &confirmed) + err := prompt.Confirm( + fmt.Sprintf("Are you sure you want to clear the index %q?", opts.Index), + &confirmed, + ) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -89,7 +94,8 @@ func runClearCmd(opts *ClearOptions) error { return err } - if _, err := client.InitIndex(opts.Index).ClearObjects(); err != nil { + _, err = client.ClearObjects(client.NewApiClearObjectsRequest(opts.Index)) + if err != nil { return err } diff --git a/pkg/cmd/indices/clear/clear_test.go b/pkg/cmd/indices/clear/clear_test.go index 266f5a56..1daa690d 100644 --- a/pkg/cmd/indices/clear/clear_test.go +++ b/pkg/cmd/indices/clear/clear_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewClearCmd(t *testing.T) { @@ -107,7 +107,10 @@ func Test_runCreateCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} - r.Register(httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/clear", tt.index)), httpmock.JSONResponse(search.CreateKeyRes{Key: "foo"})) + r.Register( + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/clear", tt.index)), + httpmock.JSONResponse(search.UpdatedAtResponse{}), + ) defer r.Verify(t) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From 5f0a47a526b7e1baa78aa376a6216eda25aa9311 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 30 Jan 2025 18:56:02 +0100 Subject: [PATCH 10/45] feat: initial migration of indices delete (without replicas) --- pkg/cmd/indices/delete/delete.go | 10 ++-- pkg/cmd/indices/delete/delete_test.go | 67 ++++++++++----------------- 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/pkg/cmd/indices/delete/delete.go b/pkg/cmd/indices/delete/delete.go index 119bcbd2..a57b26b3 100644 --- a/pkg/cmd/indices/delete/delete.go +++ b/pkg/cmd/indices/delete/delete.go @@ -95,22 +95,22 @@ func runDeleteCmd(opts *DeleteOptions) error { } // For nicer output - indexSingularOrPlural := "index" + word := "index" if len(opts.Indices) > 1 { - indexSingularOrPlural = "indices" + word = "indices" } if opts.DoConfirm { var confirmed bool msg := fmt.Sprintf( "Are you sure you want to delete the %s %q?", - indexSingularOrPlural, + word, strings.Join(opts.Indices, ", "), ) if opts.IncludeReplicas { msg = fmt.Sprintf( "Are you sure you want to delete the %s %q including their replicas?", - indexSingularOrPlural, + word, strings.Join(opts.Indices, ", "), ) } @@ -185,7 +185,7 @@ func runDeleteCmd(opts *DeleteOptions) error { opts.IO.Out, "%s Deleted %s %s\n", cs.SuccessIcon(), - indexSingularOrPlural, + word, strings.Join(opts.Indices, ", "), ) } diff --git a/pkg/cmd/indices/delete/delete_test.go b/pkg/cmd/indices/delete/delete_test.go index 99c7a9cb..e15faed6 100644 --- a/pkg/cmd/indices/delete/delete_test.go +++ b/pkg/cmd/indices/delete/delete_test.go @@ -4,16 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -25,7 +24,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsOpts DeleteOptions }{ { - name: "single indice, no --confirm, without tty", + name: "single index, no --confirm, without tty", cli: "foo", tty: false, wantsErr: true, @@ -35,7 +34,7 @@ func TestNewDeleteCmd(t *testing.T) { }, }, { - name: "single indice, --confirm, without tty", + name: "single index, --confirm, without tty", cli: "foo --confirm", tty: false, wantsErr: false, @@ -116,7 +115,7 @@ func Test_runDeleteCmd(t *testing.T) { cli: "foo --confirm", indices: []string{"foo"}, isTTY: true, - wantOut: "✓ Deleted indices foo\n", + wantOut: "✓ Deleted index foo\n", }, { name: "no TTY, multiple indices", @@ -138,15 +137,15 @@ func Test_runDeleteCmd(t *testing.T) { indices: []string{"foo"}, isReplica: true, isTTY: true, - wantOut: "✓ Deleted indices foo\n", + wantOut: "✓ Deleted index foo\n", }, { name: "TTY, has replica indices", - cli: "foo --confirm --includeReplicas", + cli: "foo --confirm --include-replicas", indices: []string{"foo"}, hasReplicas: true, isTTY: true, - wantOut: "✓ Deleted indices foo\n", + wantOut: "✓ Deleted index foo\n", }, } @@ -154,38 +153,22 @@ func Test_runDeleteCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} for _, index := range tt.indices { - // First settings call with `Exists()` - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{})) - if tt.hasReplicas { - // Settings calls for the primary index and its replicas - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{ - Replicas: opt.Replicas("bar"), - })) - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{ - Replicas: opt.Replicas("bar"), - })) - r.Register(httpmock.REST("GET", "1/indexes/bar/settings"), httpmock.JSONResponse(search.Settings{ - Primary: opt.Primary("foo"), - })) - // Additional DELETE calls for the replicas - r.Register(httpmock.REST("DELETE", "1/indexes/bar"), httpmock.JSONResponse(search.Settings{})) - } - if tt.isReplica { - // We want the first `Delete()` call to fail - r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s", index)), httpmock.ErrorResponse()) - // Second settings call to fetch the primary index name - r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{ - Primary: opt.Primary("bar"), - })) - // Third settings call to fetch the primary index settings - r.Register(httpmock.REST("GET", "1/indexes/bar/settings"), httpmock.JSONResponse(search.Settings{ - Replicas: opt.Replicas(index), - })) - // Fourth settings call to update the primary settings - r.Register(httpmock.REST("PUT", "1/indexes/bar/settings"), httpmock.JSONResponse(search.Settings{})) - } - // Final `Delete()` call - r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s", index)), httpmock.JSONResponse(search.DeleteKeyRes{})) + // GetSettings request to check if index exists + r.Register( + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), + httpmock.JSONResponse(search.SettingsResponse{}), + ) + // DeleteIndex request for the primary index + r.Register( + httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s", index)), + httpmock.JSONResponse(search.DeletedAtResponse{}), + ) + // if tt.hasReplicas { + // r.Register( + // httpmock.REST("DELETE", "1/indexes/bar"), + // httpmock.JSONResponse(search.DeletedAtResponse{}), + // ) + // } } defer r.Verify(t) From ec7d4dfaf11c5f65f1b5364e7b178824d517939a Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 31 Jan 2025 21:10:12 +0100 Subject: [PATCH 11/45] feat: migrate indices export import --- .github/workflows/releases.yml | 14 ++- pkg/cmd/indices/config/export/export.go | 27 +++--- pkg/cmd/indices/config/import/import.go | 88 ++++++++++++------- pkg/cmd/indices/config/import/synonyms.go | 59 ------------- pkg/cmd/shared/config/config.go | 80 +++++++---------- pkg/cmd/shared/handler/indices/config.go | 36 ++++---- pkg/cmd/shared/handler/indices/config_test.go | 22 ++--- 7 files changed, 136 insertions(+), 190 deletions(-) delete mode 100644 pkg/cmd/indices/config/import/synonyms.go diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index f946a8fd..dfee9b52 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -1,25 +1,22 @@ name: goreleaser - on: push: tags: - "v*" - permissions: - contents: write # publishing releases - + contents: write # publishing releases jobs: release: runs-on: ubuntu-latest steps: - name: Code checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: 1.23 - name: Install chocolatey run: | mkdir -p /opt/chocolatey @@ -38,7 +35,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }} CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} - name: Docs checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: algolia/doc path: docs @@ -53,4 +50,3 @@ jobs: GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }} run: | make docs-pr - make VARIATON=new docs-pr diff --git a/pkg/cmd/indices/config/export/export.go b/pkg/cmd/indices/config/export/export.go index 01bca006..2bbf6ce5 100644 --- a/pkg/cmd/indices/config/export/export.go +++ b/pkg/cmd/indices/config/export/export.go @@ -22,13 +22,13 @@ func NewExportCmd(f *cmdutil.Factory) *cobra.Command { opts := &config.ExportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "export [--scope ...] [--directory]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings", }, @@ -47,21 +47,21 @@ func NewExportCmd(f *cmdutil.Factory) *cobra.Command { $ algolia index config export MOVIES --directory exports `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] client, err := opts.SearchClient() if err != nil { return err } - existingIndices, err := client.ListIndices() + listResponse, err := client.ListIndices(client.NewApiListIndicesRequest()) if err != nil { return err } - var availableIndicesNames []string - for _, currentIndexName := range existingIndices.Items { - availableIndicesNames = append(availableIndicesNames, currentIndexName.Name) + var indices []string + for _, i := range listResponse.Items { + indices = append(indices, i.Name) } - opts.ExistingIndices = availableIndicesNames + opts.Indices = indices exportConfigHandler := &handler.IndexConfigExportHandler{ Opts: opts, } @@ -97,8 +97,7 @@ func runExportCmd(opts *config.ExportOptions) error { return err } - indice := client.InitIndex(opts.Indice) - configJson, err := indexConfig.GetIndiceConfig(indice, opts.Scope, cs) + configJson, err := indexConfig.GetIndexConfig(client, opts.Index, opts.Scope, cs) if err != nil { return err } @@ -112,7 +111,11 @@ func runExportCmd(opts *config.ExportOptions) error { ) } - filePath := config.GetConfigFileName(opts.Directory, opts.Indice, indice.GetAppID()) + filePath := config.GetConfigFileName( + opts.Directory, + opts.Index, + client.GetConfiguration().AppID, + ) err = os.WriteFile(filePath, configJsonIndented, 0o644) if err != nil { return fmt.Errorf("%s An error occurred when saving the file: %w", cs.FailureIcon(), err) @@ -125,7 +128,7 @@ func runExportCmd(opts *config.ExportOptions) error { fmt.Printf( "%s '%s' Index config (%s) successfully exported to %s\n", cs.SuccessIcon(), - opts.Indice, + opts.Index, utils.SliceToReadableString(opts.Scope), fmt.Sprintf("%s/%s", rootPath, filePath), ) diff --git a/pkg/cmd/indices/config/import/import.go b/pkg/cmd/indices/config/import/import.go index eb6366e3..33edb4d0 100644 --- a/pkg/cmd/indices/config/import/import.go +++ b/pkg/cmd/indices/config/import/import.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/shared/handler" @@ -20,7 +19,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &config.ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -29,7 +28,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "import -F --scope ...", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings,editSettings", }, @@ -51,24 +50,37 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { $ algolia index config import PROD_MOVIES -F export-STAGING_MOVIES-APP_ID-1666792448.json --scope rules --clear-existing-rules `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] if !confirm { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("--confirm required when non-interactive shell is detected") + return cmdutil.FlagErrorf( + "--confirm required when non-interactive shell is detected", + ) } opts.DoConfirm = true } // JSON is parsed, read, validated (and options asked if interactive mode) - err := handler.HandleFlags(&handler.IndexConfigImportHandler{Opts: opts}, opts.IO.CanPrompt()) + err := handler.HandleFlags( + &handler.IndexConfigImportHandler{Opts: opts}, + opts.IO.CanPrompt(), + ) if err != nil { return err } if opts.DoConfirm { var confirmed bool - fmt.Printf("\n%s", GetConfirmMessage(cs, opts.Scope, opts.ClearExistingRules, opts.ClearExistingSynonyms)) + fmt.Printf( + "\n%s", + GetConfirmMessage( + cs, + opts.Scope, + opts.ClearExistingRules, + opts.ClearExistingSynonyms, + ), + ) err = prompt.Confirm("Import config?", &confirmed) if err != nil { return fmt.Errorf("failed to prompt: %w", err) @@ -85,20 +97,27 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { // Common cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "Skip confirmation prompt") // Options - cmd.Flags().StringVarP(&opts.FilePath, "file", "F", "", "Directory path of the JSON config file") - cmd.Flags().StringSliceVarP(&opts.Scope, "scope", "s", []string{}, "Scope to import (default: none)") + cmd.Flags(). + StringVarP(&opts.FilePath, "file", "F", "", "Directory path of the JSON config file") + cmd.Flags(). + StringSliceVarP(&opts.Scope, "scope", "s", []string{}, "Scope to import (default: none)") _ = cmd.RegisterFlagCompletionFunc("scope", cmdutil.StringSliceCompletionFunc(map[string]string{ "settings": "settings", "synonyms": "synonyms", "rules": "rules", }, "import only")) - cmd.Flags().BoolVarP(&opts.ClearExistingSynonyms, "clear-existing-synonyms", "o", false, fmt.Sprintf("Clear %s existing synonyms of the index before import", cs.Bold("ALL"))) - cmd.Flags().BoolVarP(&opts.ClearExistingRules, "clear-existing-rules", "r", false, fmt.Sprintf("Clear %s existing rules of the index before import", cs.Bold("ALL"))) + cmd.Flags(). + BoolVarP(&opts.ClearExistingSynonyms, "clear-existing-synonyms", "o", false, fmt.Sprintf("Clear %s existing synonyms of the index before import", cs.Bold("ALL"))) + cmd.Flags(). + BoolVarP(&opts.ClearExistingRules, "clear-existing-rules", "r", false, fmt.Sprintf("Clear %s existing rules of the index before import", cs.Bold("ALL"))) // Replicas - cmd.Flags().BoolVarP(&opts.ForwardSynonymsToReplicas, "forward-synonyms-to-replicas", "m", false, "Forward imported synonyms to replicas") - cmd.Flags().BoolVarP(&opts.ForwardRulesToReplicas, "forward-rules-to-replicas", "l", false, "Forward imported rules to replicas") - cmd.Flags().BoolVarP(&opts.ForwardSettingsToReplicas, "forward-settings-to-replicas", "t", false, "Forward imported settings to replicas") + cmd.Flags(). + BoolVarP(&opts.ForwardSynonymsToReplicas, "forward-synonyms-to-replicas", "m", false, "Forward imported synonyms to replicas") + cmd.Flags(). + BoolVarP(&opts.ForwardRulesToReplicas, "forward-rules-to-replicas", "l", false, "Forward imported rules to replicas") + cmd.Flags(). + BoolVarP(&opts.ForwardSettingsToReplicas, "forward-settings-to-replicas", "t", false, "Forward imported settings to replicas") return cmd } @@ -110,40 +129,45 @@ func runImportCmd(opts *config.ImportOptions) error { return err } - indice := client.InitIndex(opts.Indice) - if opts.ImportConfig.Settings != nil && utils.Contains(opts.Scope, "settings") { - _, err = indice.SetSettings(*opts.ImportConfig.Settings, opt.ForwardToReplicas(opts.ForwardSettingsToReplicas)) + _, err = client.SetSettings( + client.NewApiSetSettingsRequest(opts.Index, opts.ImportConfig.Settings). + WithForwardToReplicas(opts.ForwardSettingsToReplicas), + ) if err != nil { - return fmt.Errorf("%s An error occurred when saving settings: %w", cs.FailureIcon(), err) + return fmt.Errorf( + "%s An error occurred when saving settings: %w", + cs.FailureIcon(), + err, + ) } } if len(opts.ImportConfig.Synonyms) > 0 && utils.Contains(opts.Scope, "synonyms") { - synonyms, err := SynonymsToSearchSynonyms(opts.ImportConfig.Synonyms) - if err != nil { - return err - } - _, err = indice.SaveSynonyms( - synonyms, - opt.ForwardToReplicas(opts.ForwardSynonymsToReplicas), - opt.ReplaceExistingSynonyms(opts.ClearExistingSynonyms), + _, err = client.SaveSynonyms( + client.NewApiSaveSynonymsRequest(opts.Index, opts.ImportConfig.Synonyms). + WithForwardToReplicas(opts.ForwardSynonymsToReplicas). + WithReplaceExistingSynonyms(opts.ClearExistingSynonyms), ) if err != nil { - return fmt.Errorf("%s An error occurred when saving synonyms: %w", cs.FailureIcon(), err) + return fmt.Errorf( + "%s An error occurred when saving synonyms: %w", + cs.FailureIcon(), + err, + ) } } if len(opts.ImportConfig.Rules) > 0 && utils.Contains(opts.Scope, "rules") { - _, err = indice.SaveRules( - opts.ImportConfig.Rules, - opt.ForwardToReplicas(opts.ForwardRulesToReplicas), - opt.ClearExistingRules(opts.ClearExistingRules), + _, err = client.SaveRules( + client.NewApiSaveRulesRequest(opts.Index, opts.ImportConfig.Rules). + WithForwardToReplicas(opts.ForwardRulesToReplicas). + WithClearExistingRules(opts.ClearExistingRules), ) if err != nil { return fmt.Errorf("%s An error occurred when saving rules: %w", cs.FailureIcon(), err) } } - fmt.Printf("%s Config successfully saved to '%s'", cs.SuccessIcon(), opts.Indice) + fmt.Printf("%s Config successfully saved to '%s'\n", cs.SuccessIcon(), opts.Index) return nil } diff --git a/pkg/cmd/indices/config/import/synonyms.go b/pkg/cmd/indices/config/import/synonyms.go deleted file mode 100644 index 5b7b70b8..00000000 --- a/pkg/cmd/indices/config/import/synonyms.go +++ /dev/null @@ -1,59 +0,0 @@ -package configimport - -import ( - "fmt" - - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - - config "github.com/algolia/cli/pkg/cmd/shared/handler/indices" - "github.com/algolia/cli/pkg/cmd/synonyms/shared" -) - -func SynonymsToSearchSynonyms(synonyms []config.Synonym) ([]search.Synonym, error) { - var searchSynonyms []search.Synonym - for _, synonym := range synonyms { - searchSynonym, err := synonymToSearchSynonm(synonym) - if err != nil { - return nil, err - } - searchSynonyms = append(searchSynonyms, searchSynonym) - } - - return searchSynonyms, nil -} - -func synonymToSearchSynonm(synonym config.Synonym) (search.Synonym, error) { - switch synonym.Type { - case shared.OneWay: - return search.NewOneWaySynonym( - synonym.ObjectID, - synonym.Input, - synonym.Synonyms..., - ), nil - case shared.AltCorrection1: - return search.NewAltCorrection1( - synonym.ObjectID, - synonym.Word, - synonym.Corrections..., - ), nil - case shared.AltCorrection2: - return search.NewAltCorrection2( - synonym.ObjectID, - synonym.Word, - synonym.Corrections..., - ), nil - case shared.Placeholder: - return search.NewPlaceholder( - synonym.ObjectID, - synonym.Placeholder, - synonym.Replacements..., - ), nil - case "", shared.Regular: - return search.NewRegularSynonym( - synonym.ObjectID, - synonym.Synonyms..., - ), nil - } - - return nil, fmt.Errorf("invalid synonym type for object id %s", synonym.ObjectID) -} diff --git a/pkg/cmd/shared/config/config.go b/pkg/cmd/shared/config/config.go index bcd7867f..154d3d33 100644 --- a/pkg/cmd/shared/config/config.go +++ b/pkg/cmd/shared/config/config.go @@ -2,74 +2,62 @@ package config import ( "fmt" - "io" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/pkg/iostreams" "github.com/algolia/cli/pkg/utils" ) -func GetSynonyms(srcIndex *search.Index) ([]search.Synonym, error) { - it, err := srcIndex.BrowseSynonyms() +func GetSynonyms(client *search.APIClient, srcIndex string) ([]search.SynonymHit, error) { + var synonyms []search.SynonymHit + + err := client.BrowseSynonyms( + srcIndex, + *search.NewEmptySearchSynonymsParams(), + search.WithAggregator(func(res any, _ error) { + response, _ := res.(search.SearchSynonymsResponse) + synonyms = append(synonyms, response.Hits...) + }), + ) if err != nil { - return nil, fmt.Errorf("cannot browse source index synonyms: %v", err) + return nil, fmt.Errorf("cannot retrieve synonyms from source index: %s: %v", srcIndex, err) } - - var synonyms []search.Synonym - - for { - synonym, err := it.Next() - if err != nil { - if err == io.EOF { - break - } else { - return nil, fmt.Errorf("error while iterating source index synonyms: %v", err) - } - } - synonyms = append(synonyms, synonym) - } - return synonyms, nil } -func GetRules(srcIndex *search.Index) ([]search.Rule, error) { - it, err := srcIndex.BrowseRules() - if err != nil { - return nil, fmt.Errorf("cannot browse source index rules: %v", err) - } - +func GetRules(client *search.APIClient, srcIndex string) ([]search.Rule, error) { var rules []search.Rule - for { - rule, err := it.Next() - if err != nil { - if err == io.EOF { - break - } else { - return nil, fmt.Errorf("error while iterating source index rules: %v", err) - } - } - rules = append(rules, *rule) + err := client.BrowseRules( + srcIndex, + *search.NewEmptySearchRulesParams(), + search.WithAggregator(func(res any, _ error) { + response, _ := res.(search.SearchRulesResponse) + rules = append(rules, response.Hits...) + }), + ) + if err != nil { + return nil, fmt.Errorf("cannot retrieve rules from source index: %s: %v", srcIndex, err) } - return rules, nil } type ExportConfigJson struct { - Settings *search.Settings `json:"settings,omitempty"` - Rules []search.Rule `json:"rules,omitempty"` - Synonyms []search.Synonym `json:"synonyms,omitempty"` + Settings *search.SettingsResponse `json:"settings,omitempty"` + Rules []search.Rule `json:"rules,omitempty"` + Synonyms []search.SynonymHit `json:"synonyms,omitempty"` } -func GetIndiceConfig( - indice *search.Index, +func GetIndexConfig( + client *search.APIClient, + index string, scope []string, cs *iostreams.ColorScheme, ) (*ExportConfigJson, error) { var configJson ExportConfigJson if utils.Contains(scope, "synonyms") { - rawSynonyms, err := GetSynonyms(indice) + rawSynonyms, err := GetSynonyms(client, index) if err != nil { return nil, fmt.Errorf( "%s An error occurred when retrieving synonyms: %w", @@ -81,7 +69,7 @@ func GetIndiceConfig( } if utils.Contains(scope, "rules") { - rawRules, err := GetRules(indice) + rawRules, err := GetRules(client, index) if err != nil { return nil, fmt.Errorf( "%s An error occurred when retrieving rules: %w", @@ -93,7 +81,7 @@ func GetIndiceConfig( } if utils.Contains(scope, "settings") { - rawSettings, err := indice.GetSettings() + rawSettings, err := client.GetSettings(client.NewApiGetSettingsRequest(index)) if err != nil { return nil, fmt.Errorf( "%s An error occurred when retrieving settings: %w", @@ -101,7 +89,7 @@ func GetIndiceConfig( err, ) } - configJson.Settings = &rawSettings + configJson.Settings = rawSettings } if len(configJson.Rules) == 0 && len(configJson.Synonyms) == 0 && configJson.Settings == nil { diff --git a/pkg/cmd/shared/handler/indices/config.go b/pkg/cmd/shared/handler/indices/config.go index 5a261257..46b0d906 100644 --- a/pkg/cmd/shared/handler/indices/config.go +++ b/pkg/cmd/shared/handler/indices/config.go @@ -10,7 +10,7 @@ import ( "time" "github.com/AlecAivazis/survey/v2" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/pkg/ask" "github.com/algolia/cli/pkg/config" @@ -22,19 +22,19 @@ type ExportOptions struct { Config config.IConfig IO *iostreams.IOStreams - ExistingIndices []string - Indice string - Scope []string - Directory string + Indices []string + Index string + Scope []string + Directory string - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) } func ValidateExportConfigFlags(opts ExportOptions) error { cs := opts.IO.ColorScheme() - if !utils.Contains(opts.ExistingIndices, opts.Indice) { - return fmt.Errorf("%s Indice '%s' doesn't exist", cs.FailureIcon(), opts.Indice) + if !utils.Contains(opts.Indices, opts.Index) { + return fmt.Errorf("%s Index '%s' doesn't exist", cs.FailureIcon(), opts.Index) } return nil } @@ -62,7 +62,7 @@ func AskExportConfig(opts *ExportOptions) error { // Matching Algolia Dashboard file naming // https://github.com/algolia/AlgoliaWeb/blob/develop/_client/src/routes/explorer/components/Explorer/IndexExportSettingsModal.tsx#L88 -func GetConfigFileName(path string, indiceName string, appId string) string { +func GetConfigFileName(path string, indexName string, appId string) string { rootPath := "" if path != "" { rootPath = path + "/" @@ -71,7 +71,7 @@ func GetConfigFileName(path string, indiceName string, appId string) string { return fmt.Sprintf( "%sexport-%s-%s-%s.json", rootPath, - indiceName, + indexName, appId, strconv.FormatInt(time.Now().UTC().Unix(), 10), ) @@ -81,11 +81,11 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) ImportConfig ImportConfigJson - Indice string + Index string FilePath string Scope []string ClearExistingSynonyms bool @@ -99,15 +99,9 @@ type ImportOptions struct { } type ImportConfigJson struct { - Settings *search.Settings `json:"settings,omitempty"` - Rules []search.Rule `json:"rules,omitempty"` - Synonyms []Synonym `json:"synonyms,omitempty"` -} - -type Synonym struct { - Type string - ObjectID, Word, Input, Placeholder string - Corrections, Synonyms, Replacements []string + Settings *search.IndexSettings `json:"settings,omitempty"` + Rules []search.Rule `json:"rules,omitempty"` + Synonyms []search.SynonymHit `json:"synonyms,omitempty"` } func ValidateImportConfigFlags(opts *ImportOptions) error { diff --git a/pkg/cmd/shared/handler/indices/config_test.go b/pkg/cmd/shared/handler/indices/config_test.go index 6b302c25..465807a6 100644 --- a/pkg/cmd/shared/handler/indices/config_test.go +++ b/pkg/cmd/shared/handler/indices/config_test.go @@ -17,29 +17,29 @@ func Test_ValidateExportConfigFlags(t *testing.T) { { name: "No existing indice", opts: ExportOptions{ - Indice: "INDICE_1", - Scope: []string{"settings", "rules", "synonyms"}, - ExistingIndices: []string{}, + Index: "INDEX_1", + Scope: []string{"settings", "rules", "synonyms"}, + Indices: []string{}, }, wantsErr: true, - wantsErrMsg: "X Indice 'INDICE_1' doesn't exist", + wantsErrMsg: "X Index 'INDEX_1' doesn't exist", }, { name: "Full scope with existing indices", opts: ExportOptions{ - Indice: "INDICE_1", - Scope: []string{"settings", "rules", "synonyms"}, - ExistingIndices: []string{"INDICE_1", "INDICE_2"}, + Index: "INDEX_1", + Scope: []string{"settings", "rules", "synonyms"}, + Indices: []string{"INDEX_1", "INDEX_2"}, }, wantsErr: false, }, { name: "Full score, existing indices with directory", opts: ExportOptions{ - Indice: "INDICE_1", - Scope: []string{"settings", "rules", "synonyms"}, - ExistingIndices: []string{"INDICE_1", "INDICE_2"}, - Directory: "test/folder", + Index: "INDEX_1", + Scope: []string{"settings", "rules", "synonyms"}, + Indices: []string{"INDEX_1", "INDEX_2"}, + Directory: "test/folder", }, wantsErr: false, }, From 2b5b2436ec58a29bd4035bc7317b48f223649777 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 5 Feb 2025 11:40:48 +0100 Subject: [PATCH 12/45] feat: migrate indices analyze --- internal/analyze/analyze.go | 40 +++------- pkg/cmd/indices/analyze/analyze.go | 117 ++++++++++++++++++----------- 2 files changed, 81 insertions(+), 76 deletions(-) diff --git a/internal/analyze/analyze.go b/internal/analyze/analyze.go index 11abe641..1340a152 100644 --- a/internal/analyze/analyze.go +++ b/internal/analyze/analyze.go @@ -3,11 +3,9 @@ package analyze import ( "encoding/json" "fmt" - "io" "strings" - "github.com/algolia/algoliasearch-client-go/v3/algolia/iterator" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" ) // AttributeType is an enum for the different types of attributes. @@ -48,38 +46,18 @@ type Stats struct { // ComputeStats computes the stats for the given index. func ComputeStats( - i iterator.Iterator, - s search.Settings, - limit int, + records []search.Hit, + settings search.SettingsResponse, only string, - counter chan int, ) (*Stats, error) { - settingsMap := settingsAsMap(s) + settingsMap := settingsAsMap(settings) stats := &Stats{ - Attributes: make(map[string]*AttributeStats), + Attributes: make(map[string]*AttributeStats), + TotalRecords: len(records), } - for { - iObject, err := i.Next() - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - - object, ok := iObject.(map[string]interface{}) - if !ok { - continue - } - - if limit > 0 && stats.TotalRecords >= limit { - break - } - - stats.TotalRecords++ - counter <- 1 - stats = computeObjectStats(stats, "", object, only) + for _, record := range records { + stats = computeObjectStats(stats, "", record.AdditionalProperties, only) } for key, value := range stats.Attributes { @@ -181,7 +159,7 @@ func getType(value interface{}) AttributeType { // settingsAsMap converts the given settings to a map. // We marshal and unmarshal the settings to avoid having to write the conversion code ourselves. -func settingsAsMap(s search.Settings) map[string]interface{} { +func settingsAsMap(s search.SettingsResponse) map[string]interface{} { var settingsMap map[string]interface{} var settingsBytes []byte settingsBytes, err := s.MarshalJSON() diff --git a/pkg/cmd/indices/analyze/analyze.go b/pkg/cmd/indices/analyze/analyze.go index e03daecc..57756ddd 100644 --- a/pkg/cmd/indices/analyze/analyze.go +++ b/pkg/cmd/indices/analyze/analyze.go @@ -1,12 +1,12 @@ package analyze import ( + "encoding/json" "fmt" "sort" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/internal/analyze" @@ -21,10 +21,10 @@ type StatsOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string - BrowseParams map[string]interface{} + Index string + BrowseParams search.BrowseParamsObject NoLimit bool Only string @@ -36,14 +36,14 @@ func NewAnalyzeCmd(f *cmdutil.Factory) *cobra.Command { opts := &StatsOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ Use: "analyze ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "browse,settings", }, @@ -70,13 +70,24 @@ func NewAnalyzeCmd(f *cmdutil.Factory) *cobra.Command { $ algolia index analyze MOVIES --only actors `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] - browseParams, err := cmdutil.FlagValuesMap(cmd.Flags(), cmdutil.BrowseParamsObject...) + browseParamsMap, err := cmdutil.FlagValuesMap( + cmd.Flags(), + cmdutil.BrowseParamsObject...) + if err != nil { + return err + } + + // Convert map to object + tmp, err := json.Marshal(browseParamsMap) + if err != nil { + return err + } + err = json.Unmarshal(tmp, &opts.BrowseParams) if err != nil { return err } - opts.BrowseParams = browseParams return runAnalyzeCmd(opts) }, @@ -100,54 +111,70 @@ func runAnalyzeCmd(opts *StatsOptions) error { return err } - indice := client.InitIndex(opts.Indice) + var records []search.Hit + counter := 1 - // We use the `opt.ExtraOptions` to pass the `SearchParams` to the API. - query, ok := opts.BrowseParams["query"].(string) - if !ok { - query = "" - } else { - delete(opts.BrowseParams, "query") - } - - // If no-limit flag is passed, count the number of objects in the index - count := 1000 - limit := 1000 + // With `--no-limit`, we iterate over all records if opts.NoLimit { - limit = 0 - res, err := indice.Search("", opt.HitsPerPage(0)) + // Get the number of records (just for printing; maybe we can avoid this?) + res, err := client.SearchSingleIndex( + client.NewApiSearchSingleIndexRequest(opts.Index). + WithSearchParams(search.SearchParamsObjectAsSearchParams(search.NewEmptySearchParamsObject().SetQuery("").SetHitsPerPage(0))), + ) if err != nil { return err } - count = res.NbHits - } - - io.StartProgressIndicatorWithLabel(fmt.Sprintf("Analyzing %d/%d objects", limit, count)) - - // Chan to receive the object count - counter := make(chan int) - go func() { - var total int - for c := range counter { - total += c - io.UpdateProgressIndicatorLabel(fmt.Sprintf("Analyzing %d/%d objects", total, count)) + limit := int(*res.NbHits) + io.StartProgressIndicatorWithLabel(fmt.Sprintf("Analyzing 0/%d objects", limit)) + err = client.BrowseObjects( + opts.Index, + opts.BrowseParams, + search.WithAggregator(func(response any, err error) { + if err != nil { + return + } + for _, hit := range response.(*search.BrowseResponse).Hits { + io.UpdateProgressIndicatorLabel( + fmt.Sprintf("Analyzing %d/%d objects", counter, limit), + ) + records = append(records, hit) + counter++ + } + }), + ) + if err != nil { + io.StopProgressIndicator() + return err + } + } else { + // Without `--no-limit`, just iterate over the first 1,000 records. + // Since `BrowseObjects` can't be terminated early, we use the regular Browse method here. + res, err := client.Browse( + client. + NewApiBrowseRequest(opts.Index). + WithBrowseParams(search.BrowseParamsObjectAsBrowseParams(&opts.BrowseParams)), + ) + limit := int(*res.NbHits) + io.StartProgressIndicatorWithLabel(fmt.Sprintf("Analyzing 0/%d objects", limit)) + if err != nil { + return err + } + for _, hit := range res.Hits { + io.UpdateProgressIndicatorLabel( + fmt.Sprintf("Analyzing %d/%d objects", counter, limit), + ) + records = append(records, hit) + counter++ } - close(counter) - }() - - res, err := indice.BrowseObjects(opt.Query(query), opt.ExtraOptions(opts.BrowseParams)) - if err != nil { - io.StopProgressIndicator() - return err } - settings, err := indice.GetSettings() + settings, err := client.GetSettings(client.NewApiGetSettingsRequest(opts.Index)) if err != nil { io.StopProgressIndicator() return err } - stats, err := analyze.ComputeStats(res, settings, limit, opts.Only, counter) + stats, err := analyze.ComputeStats(records, *settings, opts.Only) if err != nil { io.StopProgressIndicator() return err From e1eec1764d7d9687e674350e5eac81ed43b3ca97 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 31 Jan 2025 22:33:20 +0100 Subject: [PATCH 13/45] feat: migrate apikeys create --- pkg/cmd/apikeys/create/create.go | 21 +++++++++++++-------- pkg/cmd/apikeys/create/create_test.go | 8 ++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/pkg/cmd/apikeys/create/create.go b/pkg/cmd/apikeys/create/create.go index e39436f7..45388831 100644 --- a/pkg/cmd/apikeys/create/create.go +++ b/pkg/cmd/apikeys/create/create.go @@ -5,7 +5,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/dustin/go-humanize" "github.com/spf13/cobra" @@ -20,7 +20,7 @@ type CreateOptions struct { config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) ACL []string Description string @@ -34,7 +34,7 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co opts := &CreateOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "create", @@ -143,19 +143,24 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co // runCreateCmd executes the create command func runCreateCmd(opts *CreateOptions) error { - key := search.Key{ - ACL: opts.ACL, + var acls []search.Acl + for _, a := range opts.ACL { + acls = append(acls, search.Acl(a)) + } + validity := int32(opts.Validity.Seconds()) + key := search.ApiKey{ + Acl: acls, Indexes: opts.Indices, - Validity: opts.Validity, + Validity: &validity, Referers: opts.Referers, - Description: opts.Description, + Description: &opts.Description, } client, err := opts.SearchClient() if err != nil { return err } - res, err := client.AddAPIKey(key) + res, err := client.AddApiKey(client.NewApiAddApiKeyRequest(&key)) if err != nil { return err } diff --git a/pkg/cmd/apikeys/create/create_test.go b/pkg/cmd/apikeys/create/create_test.go index 778dedad..b6eb9921 100644 --- a/pkg/cmd/apikeys/create/create_test.go +++ b/pkg/cmd/apikeys/create/create_test.go @@ -4,15 +4,15 @@ import ( "testing" "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewCreateCmd(t *testing.T) { @@ -107,7 +107,7 @@ func Test_runCreateCmd(t *testing.T) { r := httpmock.Registry{} r.Register( httpmock.REST("POST", "1/keys"), - httpmock.JSONResponse(search.CreateKeyRes{Key: "foo"}), + httpmock.JSONResponse(search.AddApiKeyResponse{Key: "foo"}), ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From 884624abf8269127601e70d890ac74d0d7e3a354 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Sat, 1 Feb 2025 14:23:05 +0100 Subject: [PATCH 14/45] feat: migrate apikeys delete --- pkg/cmd/apikeys/delete/delete.go | 10 +++++----- pkg/cmd/apikeys/delete/delete_test.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/apikeys/delete/delete.go b/pkg/cmd/apikeys/delete/delete.go index 0932fcf1..1b6931ba 100644 --- a/pkg/cmd/apikeys/delete/delete.go +++ b/pkg/cmd/apikeys/delete/delete.go @@ -3,7 +3,7 @@ package delete import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -18,7 +18,7 @@ type DeleteOptions struct { config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) APIKey string DoConfirm bool @@ -29,7 +29,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -72,7 +72,7 @@ func runDeleteCmd(opts *DeleteOptions) error { return err } - _, err = client.GetAPIKey(opts.APIKey) + _, err = client.GetApiKey(client.NewApiGetApiKeyRequest(opts.APIKey)) if err != nil { return fmt.Errorf("API key %q does not exist", opts.APIKey) } @@ -91,7 +91,7 @@ func runDeleteCmd(opts *DeleteOptions) error { } } - _, err = client.DeleteAPIKey(opts.APIKey) + _, err = client.DeleteApiKey(client.NewApiDeleteApiKeyRequest(opts.APIKey)) if err != nil { return err } diff --git a/pkg/cmd/apikeys/delete/delete_test.go b/pkg/cmd/apikeys/delete/delete_test.go index 5ddc68b9..26f34552 100644 --- a/pkg/cmd/apikeys/delete/delete_test.go +++ b/pkg/cmd/apikeys/delete/delete_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -105,11 +105,11 @@ func Test_runDeleteCmd(t *testing.T) { r := httpmock.Registry{} r.Register( httpmock.REST("GET", fmt.Sprintf("1/keys/%s", tt.key)), - httpmock.JSONResponse(search.Key{Value: "foo"}), + httpmock.JSONResponse(search.GetApiKeyResponse{Value: "foo"}), ) r.Register( httpmock.REST("DELETE", fmt.Sprintf("1/keys/%s", tt.key)), - httpmock.JSONResponse(search.DeleteKeyRes{}), + httpmock.JSONResponse(search.DeletedAtResponse{}), ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From afc7196440e2ddb7596a428d9d28550a4bd2019e Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 12:57:13 +0100 Subject: [PATCH 15/45] feat: migrate search --- pkg/cmd/search/search.go | 59 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/pkg/cmd/search/search.go b/pkg/cmd/search/search.go index 23145431..d621115c 100644 --- a/pkg/cmd/search/search.go +++ b/pkg/cmd/search/search.go @@ -1,9 +1,10 @@ package search import ( + "encoding/json" + "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - algoliaSearch "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + algoliaSearch "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -17,13 +18,11 @@ type SearchOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*algoliaSearch.Client, error) - - Indice string + SearchClient func() (*algoliaSearch.APIClient, error) - SearchParams map[string]interface{} - - PrintFlags *cmdutil.PrintFlags + Index string + SearchParams *algoliaSearch.SearchParamsObject + PrintFlags *cmdutil.PrintFlags } // NewSearchCmd returns a new instance of the search command @@ -31,7 +30,7 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { opts := &SearchOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -39,7 +38,7 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { Use: "search ", Short: "Search the given index", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Long: `Search for objects in your index.`, Annotations: map[string]string{ "runInWebCLI": "true", @@ -62,12 +61,18 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { $ algolia search MOVIES --query "toy story" --output="jsonpath={$.Hits}" > movies.json `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] searchParams, err := cmdutil.FlagValuesMap(cmd.Flags(), cmdutil.SearchParamsObject...) if err != nil { return err } - opts.SearchParams = searchParams + + // Convert map to object + paramsObject, err := searchParamsMapToObject(searchParams) + if err != nil { + return err + } + opts.SearchParams = paramsObject return runSearchCmd(opts) }, @@ -90,8 +95,6 @@ func runSearchCmd(opts *SearchOptions) error { return err } - indice := client.InitIndex(opts.Indice) - p, err := opts.PrintFlags.ToPrinter() if err != nil { return err @@ -99,14 +102,10 @@ func runSearchCmd(opts *SearchOptions) error { opts.IO.StartProgressIndicatorWithLabel("Searching") - // We use the `opt.ExtraOptions` to pass the `SearchParams` to the API. - query, ok := opts.SearchParams["query"].(string) - if !ok { - query = "" - } else { - delete(opts.SearchParams, "query") - } - res, err := indice.Search(query, opt.ExtraOptions(opts.SearchParams)) + res, err := client.SearchSingleIndex( + client.NewApiSearchSingleIndexRequest(opts.Index). + WithSearchParams(algoliaSearch.SearchParamsObjectAsSearchParams(opts.SearchParams)), + ) if err != nil { opts.IO.StopProgressIndicator() return err @@ -116,3 +115,19 @@ func runSearchCmd(opts *SearchOptions) error { return p.Print(opts.IO, res) } + +// searchParamsMapToObject converts the map provided by the flags to an object required by the API client +func searchParamsMapToObject( + input map[string]interface{}, +) (*algoliaSearch.SearchParamsObject, error) { + tmp, err := json.Marshal(input) + if err != nil { + return nil, err + } + var output algoliaSearch.SearchParamsObject + err = json.Unmarshal(tmp, &output) + if err != nil { + return nil, err + } + return &output, nil +} From 1410222cd3ff3a0828c2c3139cb65aa5c1103924 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 13:01:19 +0100 Subject: [PATCH 16/45] feat: migrate settings get --- pkg/cmd/search/search.go | 23 +++++------------------ pkg/cmd/settings/get/list.go | 10 +++++----- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/pkg/cmd/search/search.go b/pkg/cmd/search/search.go index d621115c..ad93d9e2 100644 --- a/pkg/cmd/search/search.go +++ b/pkg/cmd/search/search.go @@ -68,11 +68,14 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { } // Convert map to object - paramsObject, err := searchParamsMapToObject(searchParams) + tmp, err := json.Marshal(searchParams) + if err != nil { + return err + } + err = json.Unmarshal(tmp, &opts.SearchParams) if err != nil { return err } - opts.SearchParams = paramsObject return runSearchCmd(opts) }, @@ -115,19 +118,3 @@ func runSearchCmd(opts *SearchOptions) error { return p.Print(opts.IO, res) } - -// searchParamsMapToObject converts the map provided by the flags to an object required by the API client -func searchParamsMapToObject( - input map[string]interface{}, -) (*algoliaSearch.SearchParamsObject, error) { - tmp, err := json.Marshal(input) - if err != nil { - return nil, err - } - var output algoliaSearch.SearchParamsObject - err = json.Unmarshal(tmp, &output) - if err != nil { - return nil, err - } - return &output, nil -} diff --git a/pkg/cmd/settings/get/list.go b/pkg/cmd/settings/get/list.go index 6b53c663..6251d7bf 100644 --- a/pkg/cmd/settings/get/list.go +++ b/pkg/cmd/settings/get/list.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -17,7 +17,7 @@ type GetOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Index string @@ -29,7 +29,7 @@ func NewGetCmd(f *cmdutil.Factory) *cobra.Command { opts := &GetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } cmd := &cobra.Command{ @@ -44,7 +44,7 @@ func NewGetCmd(f *cmdutil.Factory) *cobra.Command { # Store the settings of an index in a file $ algolia settings get MOVIES > movies_settings.json `), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), RunE: func(cmd *cobra.Command, args []string) error { opts.Index = args[0] @@ -69,7 +69,7 @@ func runListCmd(opts *GetOptions) error { } opts.IO.StartProgressIndicatorWithLabel(fmt.Sprint("Fetching settings for index ", opts.Index)) - res, err := client.InitIndex(opts.Index).GetSettings() + res, err := client.GetSettings(client.NewApiGetSettingsRequest(opts.Index)) opts.IO.StopProgressIndicator() if err != nil { return err From ceac63501ab420f80ebb0e0424b0ee4d681a5c00 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 13:08:21 +0100 Subject: [PATCH 17/45] feat: migrate settings set --- pkg/cmd/settings/set/set.go | 23 ++++++++++++----------- pkg/cmd/settings/set/set_test.go | 8 ++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/cmd/settings/set/set.go b/pkg/cmd/settings/set/set.go index 146e6a21..ff65978d 100644 --- a/pkg/cmd/settings/set/set.go +++ b/pkg/cmd/settings/set/set.go @@ -5,8 +5,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -19,9 +18,9 @@ type SetOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Settings search.Settings + Settings search.IndexSettings ForwardToReplicas bool Index string @@ -32,7 +31,7 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { opts := &SetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "set ", @@ -45,7 +44,7 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { # Set the typo tolerance to false on the MOVIES index $ algolia settings set MOVIES --typoTolerance="false" `), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), RunE: func(cmd *cobra.Command, args []string) error { opts.Index = args[0] @@ -54,12 +53,12 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { return err } - // Serialize / Unseralize the settings - b, err := json.Marshal(settings) + // Serialize / Deseralize the settings + tmp, err := json.Marshal(settings) if err != nil { return err } - err = json.Unmarshal(b, &opts.Settings) + err = json.Unmarshal(tmp, &opts.Settings) if err != nil { return err } @@ -85,8 +84,10 @@ func runSetCmd(opts *SetOptions) error { opts.IO.StartProgressIndicatorWithLabel( fmt.Sprintf("Setting settings for index %s", opts.Index), ) - _, err = client.InitIndex(opts.Index). - SetSettings(opts.Settings, opt.ForwardToReplicas(opts.ForwardToReplicas)) + _, err = client.SetSettings( + client.NewApiSetSettingsRequest(opts.Index, &opts.Settings). + WithForwardToReplicas(opts.ForwardToReplicas), + ) opts.IO.StopProgressIndicator() if err != nil { return err diff --git a/pkg/cmd/settings/set/set_test.go b/pkg/cmd/settings/set/set_test.go index 723dfc93..f6d0ab58 100644 --- a/pkg/cmd/settings/set/set_test.go +++ b/pkg/cmd/settings/set/set_test.go @@ -3,11 +3,11 @@ package set import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runSetCmd(t *testing.T) { @@ -33,7 +33,7 @@ func Test_runSetCmd(t *testing.T) { r := httpmock.Registry{} r.Register( httpmock.REST("PUT", "1/indexes/foo/settings"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) defer r.Verify(t) From 7115bdc16c87aba27fe1e9cd52dfb39033b7e313 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 12:11:40 +0100 Subject: [PATCH 18/45] feat: migrate settings import --- pkg/cmd/settings/import/import.go | 20 +++++++++++++------- pkg/cmd/settings/import/import_test.go | 8 ++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/settings/import/import.go b/pkg/cmd/settings/import/import.go index 612f35e1..a72b5b2b 100644 --- a/pkg/cmd/settings/import/import.go +++ b/pkg/cmd/settings/import/import.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -18,10 +18,11 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Index string - Settings search.Settings + Index string + Settings search.IndexSettings + ForwardToReplicas bool } // NewImportCmd creates and returns an import command for settings @@ -29,7 +30,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var settingsFile string @@ -37,7 +38,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -63,6 +64,8 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { cmd.Flags(). StringVarP(&settingsFile, "file", "F", "", "Read settings from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") + cmd.Flags(). + BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the settings to the replicas") return cmd } @@ -74,7 +77,10 @@ func runImportCmd(opts *ImportOptions) error { } opts.IO.StartProgressIndicatorWithLabel(fmt.Sprint("Importing settings to index ", opts.Index)) - _, err = client.InitIndex(opts.Index).SetSettings(opts.Settings) + _, err = client.SetSettings( + client.NewApiSetSettingsRequest(opts.Index, &opts.Settings). + WithForwardToReplicas(opts.ForwardToReplicas), + ) opts.IO.StopProgressIndicator() if err != nil { return err diff --git a/pkg/cmd/settings/import/import_test.go b/pkg/cmd/settings/import/import_test.go index edb15eb1..5bca2f52 100644 --- a/pkg/cmd/settings/import/import_test.go +++ b/pkg/cmd/settings/import/import_test.go @@ -6,12 +6,12 @@ import ( "path/filepath" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runExportCmd(t *testing.T) { @@ -43,7 +43,7 @@ func Test_runExportCmd(t *testing.T) { r := httpmock.Registry{} r.Register( httpmock.REST("PUT", "1/indexes/foo/settings"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) defer r.Verify(t) From aac8e734294eeb4dd95e6ec089cd694022ebbc42 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 14:12:12 +0100 Subject: [PATCH 19/45] feat: migrate objects browse --- pkg/cmd/objects/browse/browse.go | 71 ++++++++++++++------------- pkg/cmd/objects/browse/browse_test.go | 14 +++--- pkg/cmd/objects/objects.go | 5 +- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/pkg/cmd/objects/browse/browse.go b/pkg/cmd/objects/browse/browse.go index 64affd93..6aa6114c 100644 --- a/pkg/cmd/objects/browse/browse.go +++ b/pkg/cmd/objects/browse/browse.go @@ -1,11 +1,10 @@ package browse import ( - "io" + "encoding/json" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -18,10 +17,10 @@ type BrowseOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string - BrowseParams map[string]interface{} + Index string + BrowseParams search.BrowseParamsObject PrintFlags *cmdutil.PrintFlags } @@ -31,14 +30,15 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } cmd := &cobra.Command{ Use: "browse ", + Aliases: []string{"list"}, Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "runInWebCLI": "true", "acls": "browse", @@ -61,13 +61,22 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { $ algolia objects browse MOVIES > movies.ndjson `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] browseParams, err := cmdutil.FlagValuesMap(cmd.Flags(), cmdutil.BrowseParamsObject...) if err != nil { return err } - opts.BrowseParams = browseParams + + // Convert map to object + tmp, err := json.Marshal(browseParams) + if err != nil { + return err + } + err = json.Unmarshal(tmp, &opts.BrowseParams) + if err != nil { + return err + } return runBrowseCmd(opts) }, @@ -87,36 +96,28 @@ func runBrowseCmd(opts *BrowseOptions) error { return err } - indice := client.InitIndex(opts.Indice) - - // We use the `opt.ExtraOptions` to pass the `SearchParams` to the API. - query, ok := opts.BrowseParams["query"].(string) - if !ok { - query = "" - } else { - delete(opts.BrowseParams, "query") - } - res, err := indice.BrowseObjects(opt.Query(query), opt.ExtraOptions(opts.BrowseParams)) - if err != nil { - return err - } - p, err := opts.PrintFlags.ToPrinter() if err != nil { return err } - for { - iObject, err := res.Next() - if err != nil { - if err == io.EOF { - return nil + err = client.BrowseObjects( + opts.Index, + opts.BrowseParams, + search.WithAggregator(func(res any, err error) { + if err != nil { + return } - return err - } - if err = p.Print(opts.IO, iObject); err != nil { - return err - } - + for _, hit := range res.(*search.BrowseResponse).Hits { + err := p.Print(opts.IO, hit) + if err != nil { + return + } + } + }), + ) + if err != nil { + return err } + return nil } diff --git a/pkg/cmd/objects/browse/browse_test.go b/pkg/cmd/objects/browse/browse_test.go index d0ff7708..459a28e2 100644 --- a/pkg/cmd/objects/browse/browse_test.go +++ b/pkg/cmd/objects/browse/browse_test.go @@ -3,30 +3,30 @@ package browse import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runBrowseCmd(t *testing.T) { tests := []struct { name string cli string - hits []map[string]interface{} + hits []search.Hit wantOut string }{ { name: "single object", cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo"}}, + hits: []search.Hit{{ObjectID: "foo"}}, wantOut: "{\"objectID\":\"foo\"}\n", }, { name: "multiple objects", cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo"}, {"objectID": "bar"}}, + hits: []search.Hit{{ObjectID: "foo"}, {ObjectID: "bar"}}, wantOut: "{\"objectID\":\"foo\"}\n{\"objectID\":\"bar\"}\n", }, } @@ -36,7 +36,7 @@ func Test_runBrowseCmd(t *testing.T) { r := httpmock.Registry{} r.Register( httpmock.REST("POST", "1/indexes/foo/browse"), - httpmock.JSONResponse(search.QueryRes{ + httpmock.JSONResponse(search.BrowseResponse{ Hits: tt.hits, }), ) diff --git a/pkg/cmd/objects/objects.go b/pkg/cmd/objects/objects.go index 43f6c67b..491209cb 100644 --- a/pkg/cmd/objects/objects.go +++ b/pkg/cmd/objects/objects.go @@ -14,8 +14,9 @@ import ( // NewObjectsCmd returns a new command for indices objects. func NewObjectsCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ - Use: "objects", - Short: "Manage your indices' objects", + Use: "objects", + Short: "Manage your indices' objects", + Aliases: []string{"records"}, } cmd.AddCommand(browse.NewBrowseCmd(f)) From 0efa81ae65c8e5d993babcbb5793c2aad3338af4 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 15:23:48 +0100 Subject: [PATCH 20/45] feat: migrate objects delete --- pkg/cmd/objects/delete/delete.go | 145 +++++++++----------------- pkg/cmd/objects/delete/delete_test.go | 69 ++++++------ 2 files changed, 85 insertions(+), 129 deletions(-) diff --git a/pkg/cmd/objects/delete/delete.go b/pkg/cmd/objects/delete/delete.go index b9438d8e..682d200a 100644 --- a/pkg/cmd/objects/delete/delete.go +++ b/pkg/cmd/objects/delete/delete.go @@ -6,8 +6,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -22,11 +21,12 @@ type DeleteOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string - ObjectIDs []string - DeleteParams map[string]interface{} + Index string + ObjectIDs []string + DeleteParams search.DeleteByParams + NdeleteParams int DoConfirm bool Wait bool @@ -39,13 +39,13 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "delete [--object-ids | --filters ...] [--confirm] [--wait]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteObject", }, @@ -66,14 +66,24 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co $ algolia objects delete MOVIES --filters "type:Scripted" --confirm `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] deleteParams, err := cmdutil.FlagValuesMap(cmd.Flags(), cmdutil.DeleteByParams...) if err != nil { return err } - opts.DeleteParams = deleteParams + opts.NdeleteParams = len(deleteParams) - if len(opts.ObjectIDs) == 0 && len(opts.DeleteParams) == 0 { + // Convert map into object + tmp, err := json.Marshal(deleteParams) + if err != nil { + return err + } + err = json.Unmarshal(tmp, &opts.DeleteParams) + if err != nil { + return err + } + + if len(opts.ObjectIDs) == 0 && opts.NdeleteParams == 0 { return cmdutil.FlagErrorf("you must specify either --object-ids or a filter") } @@ -110,15 +120,13 @@ func runDeleteCmd(opts *DeleteOptions) error { if err != nil { return err } - - indice := client.InitIndex(opts.Indice) nbObjectsToDelete := len(opts.ObjectIDs) extra := "Operation aborted, no deletion action taken" // Tests if the provided object IDs exists. for _, objectID := range opts.ObjectIDs { - var obj interface{} - if err := indice.GetObject(objectID, &obj); err != nil { + _, err := client.GetObject(client.NewApiGetObjectRequest(opts.Index, objectID)) + if err != nil { // The original error is not helpful, so we print a more helpful message if strings.Contains(err.Error(), "ObjectID does not exist") { return fmt.Errorf("object with ID '%s' does not exist. %s", objectID, extra) @@ -132,13 +140,16 @@ func runDeleteCmd(opts *DeleteOptions) error { exactOrApproximate := "exactly" // If the user provided filters, we need to count the number of objects matching the filters - if len(opts.DeleteParams) > 0 { - res, err := indice.Search("", opt.ExtraOptions(opts.DeleteParams)) + if opts.NdeleteParams > 0 { + res, err := client.SearchSingleIndex( + client.NewApiSearchSingleIndexRequest(opts.Index). + WithSearchParams(search.SearchParamsObjectAsSearchParams(deleteByToSearchParams(&opts.DeleteParams))), + ) if err != nil { return err } - nbObjectsToDelete = nbObjectsToDelete + res.NbHits - if !res.ExhaustiveNbHits { + nbObjectsToDelete = nbObjectsToDelete + int(*res.NbHits) + if res.ExhaustiveNbHits != nil && !*res.ExhaustiveNbHits { exactOrApproximate = "approximately" } } @@ -154,7 +165,7 @@ func runDeleteCmd(opts *DeleteOptions) error { "%s %s from %s", exactOrApproximate, utils.Pluralize(nbObjectsToDelete, "object"), - opts.Indice, + opts.Index, ) if opts.DoConfirm { @@ -172,34 +183,31 @@ func runDeleteCmd(opts *DeleteOptions) error { // Delete the objects by their IDs if len(opts.ObjectIDs) > 0 { - deleteByIDRes, err := indice.DeleteObjects(opts.ObjectIDs) + batchRes, err := client.DeleteObjects(opts.Index, opts.ObjectIDs) if err != nil { return err } - - taskIDs = append(taskIDs, deleteByIDRes.TaskID) + for _, res := range batchRes { + taskIDs = append(taskIDs, res.TaskID) + } } // Delete the objects matching the filters - if len(opts.DeleteParams) > 0 { - deleteByOpts, err := deleteParamsToDeleteByOpts(opts.DeleteParams) + if opts.NdeleteParams > 0 { + res, err := client.DeleteBy(client.NewApiDeleteByRequest(opts.Index, &opts.DeleteParams)) if err != nil { return err } - deleteByRes, err := indice.DeleteBy(deleteByOpts...) - if err != nil { - return err - } - - taskIDs = append(taskIDs, deleteByRes.TaskID) + taskIDs = append(taskIDs, res.TaskID) } // Wait for the tasks to complete if opts.Wait { opts.IO.StartProgressIndicatorWithLabel("Waiting for all of the deletion tasks to complete") for _, taskID := range taskIDs { - if err := indice.WaitTask(taskID); err != nil { + _, err := client.WaitForTask(opts.Index, taskID) + if err != nil { return err } } @@ -213,68 +221,15 @@ func runDeleteCmd(opts *DeleteOptions) error { return nil } -// flagValueToOpts returns a given option from the provided flag. -// It is used to convert the flag value to the correct type expected by the `DeleteBy` method. -func flagValueToOpts(value interface{}, opt interface{}) error { - b, err := json.Marshal(value) - if err != nil { - return err +func deleteByToSearchParams(input *search.DeleteByParams) *search.SearchParamsObject { + return &search.SearchParamsObject{ + Filters: input.Filters, + FacetFilters: input.FacetFilters, + NumericFilters: input.NumericFilters, + TagFilters: input.TagFilters, + AroundLatLng: input.AroundLatLng, + AroundRadius: input.AroundRadius, + InsideBoundingBox: input.InsideBoundingBox, + InsidePolygon: input.InsidePolygon, } - - if err := json.Unmarshal(b, opt); err != nil { - return err - } - - return nil -} - -// deleteParamsToDeleteByOpts returns an array of deleteByOptions from the provided delete parameters. -func deleteParamsToDeleteByOpts(params map[string]interface{}) ([]interface{}, error) { - var opts []interface{} - - for key, value := range params { - switch key { - case "filters": - var filtersOpt opt.FiltersOption - if err := flagValueToOpts(value, &filtersOpt); err != nil { - return nil, err - } - - opts = append(opts, &filtersOpt) - - case "facetFilters": - var facetFiltersOpt opt.FacetFiltersOption - if err := flagValueToOpts(value, &facetFiltersOpt); err != nil { - return nil, err - } - - opts = append(opts, &facetFiltersOpt) - - case "numericFilters": - var numericFiltersOpt opt.NumericFiltersOption - if err := flagValueToOpts(value, &numericFiltersOpt); err != nil { - return nil, err - } - - opts = append(opts, &numericFiltersOpt) - - case "tagFilters": - var tagFiltersOpt opt.TagFiltersOption - if err := flagValueToOpts(value, &tagFiltersOpt); err != nil { - return nil, err - } - - opts = append(opts, &tagFiltersOpt) - - case "aroundLatLng": - var aroundLatLngOpt opt.AroundLatLngOption - if err := flagValueToOpts(value, &aroundLatLngOpt); err != nil { - return nil, err - } - - opts = append(opts, &aroundLatLngOpt) - } - } - - return opts, nil } diff --git a/pkg/cmd/objects/delete/delete_test.go b/pkg/cmd/objects/delete/delete_test.go index 1704b36b..a9d34528 100644 --- a/pkg/cmd/objects/delete/delete_test.go +++ b/pkg/cmd/objects/delete/delete_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -36,7 +36,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", ObjectIDs: []string{ "1", }, @@ -49,7 +49,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: true, - Indice: "foo", + Index: "foo", ObjectIDs: []string{ "1", }, @@ -62,7 +62,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", ObjectIDs: []string{ "1", "2", @@ -103,7 +103,7 @@ func TestNewDeleteCmd(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, "", stderr.String()) - assert.Equal(t, tt.wantsOpts.Indice, opts.Indice) + assert.Equal(t, tt.wantsOpts.Index, opts.Index) assert.Equal(t, tt.wantsOpts.ObjectIDs, opts.ObjectIDs) assert.Equal(t, tt.wantsOpts.DoConfirm, opts.DoConfirm) }) @@ -111,20 +111,21 @@ func TestNewDeleteCmd(t *testing.T) { } func Test_runDeleteCmd(t *testing.T) { + var testNbHits int32 = 2 tests := []struct { name string cli string - indice string + index string objectIDs []string - nbHits int + nbHits *int32 exhaustiveNbHits bool isTTY bool wantOut string }{ { - name: "single object-id, no TTY", - cli: "foo --object-ids 1 --confirm", - indice: "foo", + name: "single object-id, no TTY", + cli: "foo --object-ids 1 --confirm", + index: "foo", objectIDs: []string{ "1", }, @@ -132,9 +133,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "", }, { - name: "single object-id, TTY", - cli: "foo --object-ids 1 --confirm", - indice: "foo", + name: "single object-id, TTY", + cli: "foo --object-ids 1 --confirm", + index: "foo", objectIDs: []string{ "1", }, @@ -142,9 +143,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "✓ Successfully deleted exactly 1 object from foo\n", }, { - name: "multiple object-ids, TTY", - cli: "foo --object-ids 1,2 --confirm", - indice: "foo", + name: "multiple object-ids, TTY", + cli: "foo --object-ids 1,2 --confirm", + index: "foo", objectIDs: []string{ "1", "2", @@ -155,18 +156,18 @@ func Test_runDeleteCmd(t *testing.T) { { name: "filters, TTY", cli: "foo --filters 'foo:bar' --confirm", - indice: "foo", + index: "foo", objectIDs: []string{}, - nbHits: 2, + nbHits: &testNbHits, isTTY: true, wantOut: "✓ Successfully deleted approximately 2 objects from foo\n", }, { name: "filters, TTY", cli: "foo --filters 'foo:bar' --confirm", - indice: "foo", + index: "foo", objectIDs: []string{}, - nbHits: 2, + nbHits: &testNbHits, exhaustiveNbHits: true, isTTY: true, wantOut: "✓ Successfully deleted exactly 2 objects from foo\n", @@ -174,9 +175,9 @@ func Test_runDeleteCmd(t *testing.T) { { name: "filters and object-ids, TTY", cli: "foo --filters 'foo:bar' --object-ids 1,2 --confirm", - indice: "foo", + index: "foo", objectIDs: []string{"1", "2"}, - nbHits: 2, + nbHits: &testNbHits, isTTY: true, wantOut: "✓ Successfully deleted approximately 4 objects from foo\n", }, @@ -188,28 +189,28 @@ func Test_runDeleteCmd(t *testing.T) { for _, id := range tt.objectIDs { // Checking that the object exists r.Register( - httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/%s", tt.indice, id)), - httpmock.JSONResponse(search.QueryRes{}), + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/%s", tt.index, id)), + httpmock.JSONResponse(search.GetObjectsResponse{}), ) } - if tt.nbHits > 0 { + if tt.nbHits != nil && *tt.nbHits > 0 { // Searching for the objects to delete (if filters are used) r.Register( - httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/query", tt.indice)), - httpmock.JSONResponse(search.QueryRes{ + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/query", tt.index)), + httpmock.JSONResponse(search.SearchResponse{ NbHits: tt.nbHits, - ExhaustiveNbHits: tt.exhaustiveNbHits, + ExhaustiveNbHits: &tt.exhaustiveNbHits, }), ) // Deleting the objects r.Register( - httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/deleteByQuery", tt.indice)), - httpmock.JSONResponse(search.BatchRes{}), + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/deleteByQuery", tt.index)), + httpmock.JSONResponse(search.BatchResponse{}), ) } r.Register( - httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/batch", tt.indice)), - httpmock.JSONResponse(search.BatchRes{}), + httpmock.REST("POST", fmt.Sprintf("1/indexes/%s/batch", tt.index)), + httpmock.JSONResponse(search.BatchResponse{}), ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From d15810426619fe8aa0579ee5bcff83a9119b7f93 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 16:32:07 +0100 Subject: [PATCH 21/45] feat: migrate objects import --- pkg/cmd/objects/import/import.go | 91 +++++++++++++-------------- pkg/cmd/objects/import/import_test.go | 26 ++++++-- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/pkg/cmd/objects/import/import.go b/pkg/cmd/objects/import/import.go index 2d9dd710..299b3a13 100644 --- a/pkg/cmd/objects/import/import.go +++ b/pkg/cmd/objects/import/import.go @@ -7,8 +7,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -18,17 +17,18 @@ import ( ) type ImportOptions struct { - Config config.IConfig - IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) - Index string - AutoGenerateObjectIDIfNotExist bool - - Scanner *bufio.Scanner - BatchSize int + Config config.IConfig + IO *iostreams.IOStreams + SearchClient func() (*search.APIClient, error) + Index string + + Scanner *bufio.Scanner + BatchSize int + AutoObjectIDs bool + Wait bool } -// NewImportCmd creates and returns an import command for indice object +// NewImportCmd creates and returns an import command for records func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &ImportOptions{ IO: f.IOStreams, @@ -76,9 +76,10 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { StringVarP(&file, "file", "F", "", "Read records to import from `file` (use \"-\" to read from standard input)") _ = cmd.MarkFlagRequired("file") - cmd.Flags(). - BoolVar(&opts.AutoGenerateObjectIDIfNotExist, "auto-generate-object-id-if-not-exist", false, "Automatically generate object ID if not exist") cmd.Flags().IntVarP(&opts.BatchSize, "batch-size", "b", 1000, "Specify the upload batch size") + cmd.Flags(). + BoolVarP(&opts.AutoObjectIDs, "auto-generate-object-id-if-not-exist", "a", false, "Auto-generate objectIDs if they don't exist") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -88,20 +89,8 @@ func runImportCmd(opts *ImportOptions) error { return err } - indice := client.InitIndex(opts.Index) - - // Move the following code to another module? - var ( - batchSize = opts.BatchSize - batch = make([]interface{}, 0, batchSize) - count = 0 - totalCount = 0 - ) - - options := []interface{}{ - opt.AutoGenerateObjectIDIfNotExist(opts.AutoGenerateObjectIDIfNotExist), - } - + count := 0 + var records []map[string]any opts.IO.StartProgressIndicatorWithLabel("Importing records") elapsed := time.Now() for opts.Scanner.Scan() { @@ -109,35 +98,45 @@ func runImportCmd(opts *ImportOptions) error { if line == "" { continue } - - var obj interface{} - if err := json.Unmarshal([]byte(line), &obj); err != nil { + var record map[string]any + if err := json.Unmarshal([]byte(line), &record); err != nil { err := fmt.Errorf("failed to parse JSON object on line %d: %s", count, err) return err } - batch = append(batch, obj) + if len(record) == 0 { + return fmt.Errorf("empty object on line %d", count) + } + // The API always automatically generates objectIDs for this operation + // The v3 API clients implemented this option, but not v4, so we'll implement it here + if !opts.AutoObjectIDs { + if _, ok := record["objectID"]; !ok { + return fmt.Errorf("missing objectID on line %d", count) + } + } + records = append(records, record) count++ + } + + responses, err := client.SaveObjects(opts.Index, records, search.WithBatchSize(opts.BatchSize)) + if err != nil { + return err + } - if count == batchSize { - if _, err := indice.SaveObjects(batch, options...); err != nil { + if opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") + for _, res := range responses { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() return err } - batch = make([]interface{}, 0, batchSize) - totalCount += count - opts.IO.UpdateProgressIndicatorLabel( - fmt.Sprintf("Imported %d objects in %v", totalCount, time.Since(elapsed)), - ) - count = 0 } } - if count > 0 { - totalCount += count - if _, err := indice.SaveObjects(batch, options...); err != nil { - return err - } - } + opts.IO.UpdateProgressIndicatorLabel( + fmt.Sprintf("Imported %d objects in %v", len(records), time.Since(elapsed)), + ) opts.IO.StopProgressIndicator() @@ -151,7 +150,7 @@ func runImportCmd(opts *ImportOptions) error { opts.IO.Out, "%s Successfully imported %s objects to %s in %v\n", cs.SuccessIcon(), - cs.Bold(fmt.Sprint(totalCount)), + cs.Bold(fmt.Sprint(len(records))), opts.Index, time.Since(elapsed), ) diff --git a/pkg/cmd/objects/import/import_test.go b/pkg/cmd/objects/import/import_test.go index f7aeb080..d7809ef7 100644 --- a/pkg/cmd/objects/import/import_test.go +++ b/pkg/cmd/objects/import/import_test.go @@ -6,12 +6,12 @@ import ( "path/filepath" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runImportCmd(t *testing.T) { @@ -37,6 +37,24 @@ func Test_runImportCmd(t *testing.T) { cli: fmt.Sprintf("foo -F '%s'", tmpFile), wantOut: "✓ Successfully imported 1 objects to foo in", }, + { + name: "empty object", + cli: "foo -F -", + stdin: `{}`, + wantErr: "empty object on line 0", + }, + { + name: "missing objectID", + cli: "foo -F -", + stdin: `{"attribute": "foo"}`, + wantErr: "missing objectID on line 0", + }, + { + name: "with auto-generated objectID", + cli: "foo --auto-generate-object-id-if-not-exist -F -", + stdin: `{"attribute": "foo"}`, + wantOut: "✓ Successfully imported 1 objects to foo in", + }, { name: "from stdin with invalid JSON", cli: "foo -F -", @@ -61,7 +79,7 @@ func Test_runImportCmd(t *testing.T) { if tt.wantErr == "" { r.Register( httpmock.REST("POST", "1/indexes/foo/batch"), - httpmock.JSONResponse(search.BatchRes{}), + httpmock.JSONResponse(search.BatchResponse{}), ) } defer r.Verify(t) From 21378e8693bd74cf0622ee936c9680a9dd549d57 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 17:14:53 +0100 Subject: [PATCH 22/45] feat: migrate objects operations This commit gets rid of some custom validation code. We don't have to validate the multiple-batch request body in the CLI, since that is done in the v4 API client now. This commit also adds support for the 'delete index' and 'clear objects' actions. --- pkg/cmd/objects/operations/operations.go | 84 ++++++------------- pkg/cmd/objects/operations/operations_test.go | 31 +++---- 2 files changed, 36 insertions(+), 79 deletions(-) diff --git a/pkg/cmd/objects/operations/operations.go b/pkg/cmd/objects/operations/operations.go index a984a6ff..ee3a4e3d 100644 --- a/pkg/cmd/objects/operations/operations.go +++ b/pkg/cmd/objects/operations/operations.go @@ -8,7 +8,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -24,7 +24,7 @@ type OperationsOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Wait bool @@ -39,7 +39,7 @@ func NewOperationsCmd(f *cmdutil.Factory, runF func(*OperationsOptions) error) * opts := &OperationsOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ @@ -96,9 +96,8 @@ func runOperationsCmd(opts *OperationsOptions) error { cs := opts.IO.ColorScheme() var ( - operations []search.BatchOperationIndexed - currentLine = 0 - totalOperations = 0 + current = 0 + operations = 0 ) // Scan the file @@ -106,35 +105,30 @@ func runOperationsCmd(opts *OperationsOptions) error { elapsed := time.Now() var errors []string + var requests []search.MultipleBatchRequest for opts.Scanner.Scan() { - currentLine++ + current++ line := opts.Scanner.Text() if line == "" { continue } - totalOperations++ + operations++ opts.IO.UpdateProgressIndicatorLabel( fmt.Sprintf( "Read %s from %s", - utils.Pluralize(totalOperations, "operation"), + utils.Pluralize(operations, "operation"), opts.File, ), ) - var batchOperation search.BatchOperationIndexed - if err := json.Unmarshal([]byte(line), &batchOperation); err != nil { - err := fmt.Errorf("line %d: %s", currentLine, err) + var request search.MultipleBatchRequest + if err := json.Unmarshal([]byte(line), &request); err != nil { + err := fmt.Errorf("line %d: %s", current, err) errors = append(errors, err.Error()) continue } - err = ValidateBatchOperation(batchOperation) - if err != nil { - errors = append(errors, err.Error()) - continue - } - - operations = append(operations, batchOperation) + requests = append(requests, request) } opts.IO.StopProgressIndicator() @@ -146,10 +140,10 @@ func runOperationsCmd(opts *OperationsOptions) error { errorMsg := heredoc.Docf(` %s Found %s (out of %d operations) while parsing the file: %s - `, cs.FailureIcon(), utils.Pluralize(len(errors), "error"), totalOperations, text.Indent(strings.Join(errors, "\n"), " ")) + `, cs.FailureIcon(), utils.Pluralize(len(errors), "error"), operations, text.Indent(strings.Join(errors, "\n"), " ")) // No operations found - if len(operations) == 0 { + if len(requests) == 0 { if len(errors) > 0 { return fmt.Errorf("%s", errorMsg) } @@ -174,9 +168,11 @@ func runOperationsCmd(opts *OperationsOptions) error { // Process operations opts.IO.StartProgressIndicatorWithLabel( - fmt.Sprintf("Processing %s operations", cs.Bold(fmt.Sprint(len(operations)))), + fmt.Sprintf("Processing %s operations", cs.Bold(fmt.Sprint(len(requests)))), + ) + res, err := client.MultipleBatch( + client.NewApiMultipleBatchRequest(search.NewBatchParams(requests)), ) - res, err := client.MultipleBatch(operations) if err != nil { opts.IO.StopProgressIndicator() return err @@ -185,9 +181,12 @@ func runOperationsCmd(opts *OperationsOptions) error { // Wait for the operation to complete if requested if opts.Wait { opts.IO.UpdateProgressIndicatorLabel("Waiting for the operations to complete") - if err := res.Wait(); err != nil { - opts.IO.StopProgressIndicator() - return err + for _, req := range requests { + _, err := client.WaitForTask(req.IndexName, res.TaskID[req.IndexName]) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } } } @@ -196,39 +195,8 @@ func runOperationsCmd(opts *OperationsOptions) error { opts.IO.Out, "%s Successfully processed %s operations in %v\n", cs.SuccessIcon(), - cs.Bold(fmt.Sprint(len(operations))), + cs.Bold(fmt.Sprint(len(requests))), time.Since(elapsed), ) return err } - -// ValidateBatchOperation checks that the batch operation is valid -func ValidateBatchOperation(p search.BatchOperationIndexed) error { - allowedActions := []string{ - string(search.AddObject), string(search.UpdateObject), string(search.PartialUpdateObject), - string(search.PartialUpdateObjectNoCreate), string(search.DeleteObject), - } - extra := fmt.Sprintf("valid actions are %s", utils.SliceToReadableString(allowedActions)) - - if p.Action == "" { - return fmt.Errorf("missing action") - } - if !utils.Contains(allowedActions, string(p.Action)) { - return fmt.Errorf("invalid action \"%s\" (%s)", p.Action, extra) - } - if p.IndexName == "" { - return fmt.Errorf("missing index name for action \"%s\"", p.Action) - } - if p.Action == search.DeleteObject { - switch body := p.Body.(type) { - case map[string]interface{}: - if body["objectID"] == nil || body["objectID"] == "" { - return fmt.Errorf("missing objectID for action %s", search.DeleteObject) - } - default: - return fmt.Errorf("missing objectID for action %s", search.DeleteObject) - } - } - - return nil -} diff --git a/pkg/cmd/objects/operations/operations_test.go b/pkg/cmd/objects/operations/operations_test.go index 4fbb364d..79900cbe 100644 --- a/pkg/cmd/objects/operations/operations_test.go +++ b/pkg/cmd/objects/operations/operations_test.go @@ -6,12 +6,12 @@ import ( "path/filepath" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runOperationsCmd(t *testing.T) { @@ -55,7 +55,7 @@ func Test_runOperationsCmd(t *testing.T) { cli: "-F -", stdin: `{"action": "addObject","indexName":"index1"}, {"test": "bar"}`, - wantErr: "X Found 2 errors (out of 2 operations) while parsing the file:\n line 1: invalid character ',' after top-level value\n missing action\n", + wantErr: "failed to prompt: EOF", }, { name: "from stdin with invalid JSON (1 operation) with --continue-on-error", @@ -88,7 +88,7 @@ func Test_runOperationsCmd(t *testing.T) { if tt.wantErr == "" { r.Register( httpmock.REST("POST", "1/indexes/*/batch"), - httpmock.JSONResponse(search.MultipleBatchRes{}), + httpmock.JSONResponse(search.MultipleBatchResponse{}), ) } defer r.Verify(t) @@ -130,7 +130,7 @@ func Test_ValidateBatchOperation(t *testing.T) { }, { name: "missing objectID for deleteObject action", - action: string(search.DeleteObject), + action: string(search.ACTION_DELETE_OBJECT), body: nil, wantErr: true, wantErrMsg: "missing objectID for action deleteObject", @@ -138,8 +138,8 @@ func Test_ValidateBatchOperation(t *testing.T) { } for _, act := range []string{ - string(search.AddObject), string(search.UpdateObject), - string(search.PartialUpdateObject), string(search.PartialUpdateObjectNoCreate), + string(search.ACTION_ADD_OBJECT), string(search.ACTION_UPDATE_OBJECT), + string(search.ACTION_PARTIAL_UPDATE_OBJECT), string(search.ACTION_PARTIAL_UPDATE_OBJECT_NO_CREATE), } { tests = append(tests, struct { name string @@ -157,23 +157,12 @@ func Test_ValidateBatchOperation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - batchOperation := search.BatchOperation{ - Action: search.BatchAction(tt.action), + batchOperation := search.MultipleBatchRequest{ + Action: search.Action(tt.action), } if tt.body != nil { batchOperation.Body = tt.body } - - err := ValidateBatchOperation(search.BatchOperationIndexed{ - IndexName: "index1", - BatchOperation: batchOperation, - }) - if tt.wantErr { - assert.Error(t, err) - assert.Equal(t, tt.wantErrMsg, err.Error()) - return - } - assert.NoError(t, err) }) } } From 11394c4071c43c2119f72417afdd2f31fabfa5a3 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 5 Feb 2025 13:29:30 +0100 Subject: [PATCH 23/45] feat: migrate objects update --- pkg/cmd/objects/update/object.go | 81 ---------------- pkg/cmd/objects/update/object_test.go | 128 -------------------------- pkg/cmd/objects/update/update.go | 78 +++++++++++++--- pkg/cmd/objects/update/update_test.go | 8 +- 4 files changed, 68 insertions(+), 227 deletions(-) delete mode 100644 pkg/cmd/objects/update/object.go delete mode 100644 pkg/cmd/objects/update/object_test.go diff --git a/pkg/cmd/objects/update/object.go b/pkg/cmd/objects/update/object.go deleted file mode 100644 index f1b65ec3..00000000 --- a/pkg/cmd/objects/update/object.go +++ /dev/null @@ -1,81 +0,0 @@ -package update - -import ( - "encoding/json" - "fmt" - - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - "github.com/algolia/cli/pkg/utils" - "github.com/mitchellh/mapstructure" -) - -// Object is a map[string]interface{} that can be unmarshalled from a JSON object -// The object must have an objectID field -// Each field could be either an `search.PartialUpdateOperation` or a any value -type Object map[string]interface{} - -// Valid operations -const ( - Increment string = "Increment" - Decrement string = "Decrement" - Add string = "Add" - AddUnique string = "AddUnique" - IncrementSet string = "IncrementSet" - IncrementFrom string = "IncrementFrom" -) - -// ValidateOperation checks that the operation is valid -func ValidateOperation(p search.PartialUpdateOperation) error { - allowedOperations := []string{Increment, Decrement, Add, AddUnique, IncrementSet, IncrementFrom} - extra := fmt.Sprintf("valid operations are %s", utils.SliceToReadableString(allowedOperations)) - - if !utils.Contains(allowedOperations, p.Operation) { - return fmt.Errorf("invalid operation \"%s\" (%s)", p.Operation, extra) - } - return nil -} - -// UnmarshalJSON unmarshals a JSON object into an Object -func (o *Object) UnmarshalJSON(data []byte) error { - var v interface{} - if err := json.Unmarshal(data, &v); err != nil { - return err - } - - // The object must be a map[string]interface{} - switch v := v.(type) { - case map[string]interface{}: - *o = v - default: - return fmt.Errorf("invalid object: %v", v) - } - - // The object must have an objectID - if _, ok := (*o)["objectID"]; !ok { - return fmt.Errorf("objectID is required") - } - - // Each field could be either an `search.PartialUpdateOperation` or any value - for k, v := range *o { - switch v := v.(type) { - case map[string]interface{}: - var op search.PartialUpdateOperation - if err := mapstructure.Decode(v, &op); err != nil { - return err - } - - // If we don't get the operation, it's probably a simple object - if op.Operation == "" { - continue - } - - // Check that the operation is valid - if err := ValidateOperation(op); err != nil { - return err - } - (*o)[k] = op - } - } - - return nil -} diff --git a/pkg/cmd/objects/update/object_test.go b/pkg/cmd/objects/update/object_test.go deleted file mode 100644 index 24aa41dd..00000000 --- a/pkg/cmd/objects/update/object_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package update - -import ( - "testing" - - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - "github.com/stretchr/testify/assert" -) - -func Test_ValidateOperation(t *testing.T) { - tests := []struct { - name string - operation string - wantErr bool - wantErrMsg string - }{ - { - name: "invalid operation", - operation: "invalid", - wantErr: true, - wantErrMsg: "invalid operation \"invalid\" (valid operations are Increment, Decrement, Add, AddUnique, IncrementSet and IncrementFrom)", - }, - } - - for _, ops := range []string{"Increment", "Decrement", "Add", "AddUnique", "IncrementSet", "IncrementFrom"} { - tests = append(tests, struct { - name string - operation string - wantErr bool - wantErrMsg string - }{ - name: ops, - operation: ops, - wantErr: false, - }) - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ValidateOperation(search.PartialUpdateOperation{Operation: tt.operation}) - if tt.wantErr { - assert.Error(t, err) - assert.Equal(t, tt.wantErrMsg, err.Error()) - return - } - assert.NoError(t, err) - }) - } -} - -func Test_Object_UnmarshalJSON(t *testing.T) { - tests := []struct { - name string - data []byte - wantObj Object - wantErr bool - wantErrMsg string - }{ - { - name: "empty object", - data: []byte(`{}`), - wantErr: true, - wantErrMsg: "objectID is required", - }, - { - name: "missing objectID", - data: []byte(`{"foo": "bar"}`), - wantErr: true, - wantErrMsg: "objectID is required", - }, - { - name: "valid object", - data: []byte(`{"objectID": "foo"}`), - wantErr: false, - wantObj: Object{"objectID": "foo"}, - }, - { - name: "nested object (not an operation)", - data: []byte(`{ - "objectID": "foo", - "bar": { - "foo": "bar" - } - }`), - wantErr: false, - wantObj: Object{"objectID": "foo", "bar": map[string]interface{}{"foo": "bar"}}, - }, - { - name: "invalid operation type", - data: []byte(`{ - "objectID": "foo", - "bar": { - "operation": "invalid" - } - }`), - wantErr: true, - wantErrMsg: "invalid operation \"invalid\" (valid operations are Increment, Decrement, Add, AddUnique, IncrementSet and IncrementFrom)", - }, - { - name: "valid operation", - data: []byte(`{ - "objectID": "foo", - "bar": { - "operation": "Increment" - } - }`), - wantErr: false, - wantObj: Object{ - "objectID": "foo", - "bar": search.PartialUpdateOperation{Operation: "Increment"}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var o Object - err := o.UnmarshalJSON(tt.data) - if tt.wantErr { - assert.Error(t, err) - assert.Equal(t, tt.wantErrMsg, err.Error()) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.wantObj, o) - }) - } -} diff --git a/pkg/cmd/objects/update/update.go b/pkg/cmd/objects/update/update.go index f1aa54ff..767cf321 100644 --- a/pkg/cmd/objects/update/update.go +++ b/pkg/cmd/objects/update/update.go @@ -8,8 +8,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -25,7 +24,7 @@ type UpdateOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Index string CreateIfNotExists bool @@ -42,13 +41,13 @@ func NewUpdateCmd(f *cmdutil.Factory, runF func(*UpdateOptions) error) *cobra.Co opts := &UpdateOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "update -F [--create-if-not-exists] [--wait] [--continue-on-error]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "addObject", }, @@ -110,10 +109,9 @@ func runUpdateCmd(opts *UpdateOptions) error { } cs := opts.IO.ColorScheme() - index := client.InitIndex(opts.Index) var ( - objects []interface{} + objects []map[string]any currentLine = 0 totalObjects = 0 ) @@ -135,10 +133,13 @@ func runUpdateCmd(opts *UpdateOptions) error { fmt.Sprintf("Read %s from %s", utils.Pluralize(totalObjects, "object"), opts.File), ) - var obj Object + var obj map[string]any if err := json.Unmarshal([]byte(line), &obj); err != nil { - err := fmt.Errorf("line %d: %s", currentLine, err) - errors = append(errors, err.Error()) + errors = append(errors, fmt.Errorf("line %d: %s", currentLine, err).Error()) + continue + } + if err = IsValidUpdate(obj); err != nil { + errors = append(errors, fmt.Errorf("line %d: %s", currentLine, err).Error()) continue } @@ -188,7 +189,12 @@ func runUpdateCmd(opts *UpdateOptions) error { cs.Bold(opts.Index), ), ) - res, err := index.PartialUpdateObjects(objects, opt.CreateIfNotExists(opts.CreateIfNotExists)) + + responses, err := client.PartialUpdateObjects( + opts.Index, + objects, + search.WithCreateIfNotExists(opts.CreateIfNotExists), + ) if err != nil { opts.IO.StopProgressIndicator() return err @@ -197,9 +203,12 @@ func runUpdateCmd(opts *UpdateOptions) error { // Wait for the operation to complete if requested if opts.Wait { opts.IO.UpdateProgressIndicatorLabel("Waiting for operation to complete") - if err := res.Wait(); err != nil { - opts.IO.StopProgressIndicator() - return err + for _, res := range responses { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } } } @@ -214,3 +223,44 @@ func runUpdateCmd(opts *UpdateOptions) error { ) return err } + +// IsAllowedOperation checks if the `_operation` value is allowed +func IsAllowedOperation(op string) bool { + for _, t := range search.AllowedBuiltInOperationTypeEnumValues { + if op == string(t) { + return true + } + } + return false +} + +// IsValidUpdate validates the update object before hitting the API +func IsValidUpdate(obj map[string]any) error { + if _, ok := obj["objectID"]; !ok { + return fmt.Errorf("objectID is required") + } + + // For printing + var allowedOps []string + for _, op := range search.AllowedBuiltInOperationTypeEnumValues { + allowedOps = append(allowedOps, string(op)) + } + + for name, attribute := range obj { + // Check if we have a nested attribute + if nested, ok := attribute.(map[string]any); ok { + // Check if we have a built-in operation + if op, ok := nested["_operation"]; ok { + if !IsAllowedOperation(op.(string)) { + return fmt.Errorf( + "Invalid operation \"%s\" for attribute \"%s\". Allowed operations: %s", + op, + name, + strings.Join(allowedOps, ", "), + ) + } + } + } + } + return nil +} diff --git a/pkg/cmd/objects/update/update_test.go b/pkg/cmd/objects/update/update_test.go index 61c71997..d63dbf87 100644 --- a/pkg/cmd/objects/update/update_test.go +++ b/pkg/cmd/objects/update/update_test.go @@ -6,12 +6,12 @@ import ( "path/filepath" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runUpdateCmd(t *testing.T) { @@ -81,7 +81,7 @@ func Test_runUpdateCmd(t *testing.T) { if tt.wantErr == "" { r.Register( httpmock.REST("POST", "1/indexes/foo/batch"), - httpmock.JSONResponse(search.BatchRes{}), + httpmock.JSONResponse(search.BatchResponse{}), ) } defer r.Verify(t) From 63b49243233cad70b2999eeb856759d741250542 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 21:51:53 +0100 Subject: [PATCH 24/45] feat: migrate apikeys list --- pkg/cmd/apikeys/list/list.go | 63 ++++++++++++------------------- pkg/cmd/apikeys/list/list_test.go | 34 +++++++---------- 2 files changed, 38 insertions(+), 59 deletions(-) diff --git a/pkg/cmd/apikeys/list/list.go b/pkg/cmd/apikeys/list/list.go index e8cf28d4..4ea0376d 100644 --- a/pkg/cmd/apikeys/list/list.go +++ b/pkg/cmd/apikeys/list/list.go @@ -5,11 +5,10 @@ import ( "sort" "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/dustin/go-humanize" "github.com/spf13/cobra" - "github.com/algolia/cli/pkg/cmd/apikeys/shared" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" "github.com/algolia/cli/pkg/iostreams" @@ -21,7 +20,7 @@ type ListOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) PrintFlags *cmdutil.PrintFlags } @@ -31,7 +30,7 @@ func NewListCmd(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman opts := &ListOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ @@ -63,38 +62,12 @@ func runListCmd(opts *ListOptions) error { } opts.IO.StartProgressIndicatorWithLabel("Fetching API Keys") - res, err := client.ListAPIKeys() + res, err := client.ListApiKeys() opts.IO.StopProgressIndicator() if err != nil { return err } - if opts.PrintFlags.OutputFlagSpecified() && opts.PrintFlags.OutputFormat != nil { - p, err := opts.PrintFlags.ToPrinter() - if err != nil { - return err - } - for _, key := range res.Keys { - keyResult := shared.JSONKey{ - ACL: key.ACL, - CreatedAt: key.CreatedAt, - Description: key.Description, - Indexes: key.Indexes, - MaxQueriesPerIPPerHour: key.MaxQueriesPerIPPerHour, - MaxHitsPerQuery: key.MaxHitsPerQuery, - Referers: key.Referers, - QueryParameters: key.QueryParameters, - Validity: key.Validity, - Value: key.Value, - } - - if err := p.Print(opts.IO, keyResult); err != nil { - return err - } - } - return nil - } - table := printers.NewTablePrinter(opts.IO) if table.IsTTY() { table.AddField("KEY", nil, nil) @@ -111,25 +84,37 @@ func runListCmd(opts *ListOptions) error { // Sort API Keys by createdAt sort.Slice(res.Keys, func(i, j int) bool { - return res.Keys[i].CreatedAt.After(res.Keys[j].CreatedAt) + return res.Keys[i].CreatedAt > res.Keys[j].CreatedAt }) for _, key := range res.Keys { table.AddField(key.Value, nil, nil) - table.AddField(key.Description, nil, nil) - table.AddField(fmt.Sprintf("%v", key.ACL), nil, nil) + if key.Description != nil { + table.AddField(*key.Description, nil, nil) + } + table.AddField(fmt.Sprintf("%v", key.Acl), nil, nil) table.AddField(fmt.Sprintf("%v", key.Indexes), nil, nil) table.AddField(func() string { - if key.Validity == 0 { + if key.Validity == nil || *key.Validity == 0 { return "Never expire" } else { - return humanize.Time(time.Now().Add(key.Validity)) + validity := time.Duration(*key.Validity) * time.Second + return humanize.Time(time.Now().Add(validity)) } }(), nil, nil) - table.AddField(humanize.Comma(int64(key.MaxHitsPerQuery)), nil, nil) - table.AddField(humanize.Comma(int64(key.MaxQueriesPerIPPerHour)), nil, nil) + if key.MaxHitsPerQuery == nil || *key.MaxHitsPerQuery == 0 { + table.AddField("0", nil, nil) + } else { + table.AddField(humanize.Comma(int64(*key.MaxHitsPerQuery)), nil, nil) + } + if key.MaxQueriesPerIPPerHour == nil || *key.MaxQueriesPerIPPerHour == 0 { + table.AddField("0", nil, nil) + } else { + table.AddField(humanize.Comma(int64(*key.MaxQueriesPerIPPerHour)), nil, nil) + } table.AddField(fmt.Sprintf("%v", key.Referers), nil, nil) - table.AddField(humanize.Time(key.CreatedAt), nil, nil) + createdAt := time.Unix(key.CreatedAt, 0) + table.AddField(humanize.Time(createdAt), nil, nil) table.EndRow() } return table.Render() diff --git a/pkg/cmd/apikeys/list/list_test.go b/pkg/cmd/apikeys/list/list_test.go index 884b2a5d..a9703a55 100644 --- a/pkg/cmd/apikeys/list/list_test.go +++ b/pkg/cmd/apikeys/list/list_test.go @@ -2,13 +2,12 @@ package list import ( "testing" - "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runListCmd(t *testing.T) { @@ -20,33 +19,28 @@ func Test_runListCmd(t *testing.T) { { name: "list", isTTY: false, - wantOut: "\ttest\t[*]\t[]\tNever expire\t0\t0\t[]\ta long while ago\n", + wantOut: "foo\ttest\t[search]\t[]\tNever expire\t0\t0\t[]\t5 years ago\n", }, { - name: "list_tty", - isTTY: true, - wantOut: `KEY DESCRIPTION ACL INDICES VALIDITY MAX H... MAX Q... REFERERS CREAT... - test [*] [] Never... 0 0 [] a lon... -`, + name: "list_tty", + isTTY: true, + wantOut: "KEY DESCRIPTION ACL INDICES VALI... MAX ... MAX ... REFE... CREA...\nfoo test [sea... [] Neve... 0 0 [] 5 ye...\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + name := "test" r := httpmock.Registry{} r.Register( httpmock.REST("GET", "1/keys"), - httpmock.JSONResponse(search.ListAPIKeysRes{ - Keys: []search.Key{ + httpmock.JSONResponse(search.ListApiKeysResponse{ + Keys: []search.GetApiKeyResponse{ { - Value: "foo", - Description: "test", - ACL: []string{"*"}, - Validity: 0, - MaxHitsPerQuery: 0, - MaxQueriesPerIPPerHour: 0, - Referers: []string{}, - CreatedAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + Value: "foo", + Description: &name, + Acl: []search.Acl{search.ACL_SEARCH}, + CreatedAt: 1577836800, }, }, }), From 7c2ddb4f482202facdef15ff1de8564bd9aab5bb Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 21:58:58 +0100 Subject: [PATCH 25/45] feat: migrate apikeys get --- pkg/cmd/apikeys/get/get.go | 25 ++++++------------------- pkg/cmd/apikeys/get/get_test.go | 19 ++++++++----------- pkg/cmd/apikeys/shared/shared.go | 21 --------------------- 3 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 pkg/cmd/apikeys/shared/shared.go diff --git a/pkg/cmd/apikeys/get/get.go b/pkg/cmd/apikeys/get/get.go index 78d5ed9f..378441cf 100644 --- a/pkg/cmd/apikeys/get/get.go +++ b/pkg/cmd/apikeys/get/get.go @@ -4,10 +4,9 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" - "github.com/algolia/cli/pkg/cmd/apikeys/shared" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" "github.com/algolia/cli/pkg/iostreams" @@ -19,19 +18,19 @@ type GetOptions struct { config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) APIKey string PrintFlags *cmdutil.PrintFlags } -// NewGetCmd returns a new instance of DeleteCmd +// NewGetCmd returns a new instance of the command func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command { opts := &GetOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -68,7 +67,7 @@ func runGetCmd(opts *GetOptions) error { return err } - key, err := client.GetAPIKey(opts.APIKey) + key, err := client.GetApiKey(client.NewApiGetApiKeyRequest(opts.APIKey)) if err != nil { return fmt.Errorf("API key %q does not exist", opts.APIKey) } @@ -77,20 +76,8 @@ func runGetCmd(opts *GetOptions) error { if err != nil { return err } - keyResult := shared.JSONKey{ - ACL: key.ACL, - CreatedAt: key.CreatedAt, - Description: key.Description, - Indexes: key.Indexes, - MaxQueriesPerIPPerHour: key.MaxQueriesPerIPPerHour, - MaxHitsPerQuery: key.MaxHitsPerQuery, - Referers: key.Referers, - QueryParameters: key.QueryParameters, - Validity: key.Validity, - Value: key.Value, - } - if err := p.Print(opts.IO, keyResult); err != nil { + if err := p.Print(opts.IO, key); err != nil { return err } diff --git a/pkg/cmd/apikeys/get/get_test.go b/pkg/cmd/apikeys/get/get_test.go index 6dd56c0c..ca0892fa 100644 --- a/pkg/cmd/apikeys/get/get_test.go +++ b/pkg/cmd/apikeys/get/get_test.go @@ -3,11 +3,11 @@ package get import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runGetCmd(t *testing.T) { @@ -31,16 +31,13 @@ func Test_runGetCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} if tt.key == "foo" { + name := "test" r.Register( httpmock.REST("GET", "1/keys/foo"), - httpmock.JSONResponse(search.Key{ - Value: "foo", - Description: "test", - ACL: []string{"*"}, - Validity: 0, - MaxHitsPerQuery: 0, - MaxQueriesPerIPPerHour: 0, - Referers: []string{}, + httpmock.JSONResponse(search.GetApiKeyResponse{ + Value: "foo", + Description: &name, + Acl: []search.Acl{search.ACL_SEARCH}, }), ) } else { diff --git a/pkg/cmd/apikeys/shared/shared.go b/pkg/cmd/apikeys/shared/shared.go deleted file mode 100644 index adc3dc4b..00000000 --- a/pkg/cmd/apikeys/shared/shared.go +++ /dev/null @@ -1,21 +0,0 @@ -package shared - -import ( - "time" - - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" -) - -// JSONKey is the same as search.Key without omitting values -type JSONKey struct { - ACL []string `json:"acl"` - CreatedAt time.Time `json:"createdAt"` - Description string `json:"description"` - Indexes []string `json:"indexes"` - MaxQueriesPerIPPerHour int `json:"maxQueriesPerIPPerHour"` - MaxHitsPerQuery int `json:"maxHitsPerQuery"` - Referers []string `json:"referers"` - QueryParameters search.KeyQueryParams `json:"queryParameters"` - Validity time.Duration `json:"validity"` - Value string `json:"value"` -} From 50d4d570ca77a9eb58acf833b6ae0400aade7655 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 3 Feb 2025 22:16:31 +0100 Subject: [PATCH 26/45] feat: migrate rules browse --- pkg/cmd/rules/browse/browse.go | 6 +++--- pkg/cmd/rules/browse/browse_test.go | 18 +++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pkg/cmd/rules/browse/browse.go b/pkg/cmd/rules/browse/browse.go index 23c79cd6..471c525d 100644 --- a/pkg/cmd/rules/browse/browse.go +++ b/pkg/cmd/rules/browse/browse.go @@ -30,7 +30,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &ExportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -38,7 +38,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { Use: "browse ", Args: validators.ExactArgs(1), Aliases: []string{"list"}, - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Short: "List all the rules of an index", Annotations: map[string]string{ "runInWebCLI": "true", @@ -75,7 +75,7 @@ func runListCmd(opts *ExportOptions) error { return err } if !exists { - return fmt.Errorf("index %s doesn't exist", opts.Index) + return fmt.Errorf("Index %s doesn't exist.", opts.Index) } p, err := opts.PrintFlags.ToPrinter() diff --git a/pkg/cmd/rules/browse/browse_test.go b/pkg/cmd/rules/browse/browse_test.go index 3a2d502b..337d60dd 100644 --- a/pkg/cmd/rules/browse/browse_test.go +++ b/pkg/cmd/rules/browse/browse_test.go @@ -3,30 +3,30 @@ package browse import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runBrowseCmd(t *testing.T) { tests := []struct { name string cli string - hits []map[string]interface{} + hits []search.Rule wantOut string }{ { name: "single rule", cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo"}}, + hits: []search.Rule{{ObjectID: "foo"}}, wantOut: "{\"consequence\":{},\"objectID\":\"foo\"}\n", }, { name: "multiple rules", cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo"}, {"objectID": "bar"}}, + hits: []search.Rule{{ObjectID: "foo"}, {ObjectID: "bar"}}, wantOut: "{\"consequence\":{},\"objectID\":\"foo\"}\n{\"consequence\":{},\"objectID\":\"bar\"}\n", }, } @@ -34,9 +34,13 @@ func Test_runBrowseCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} + r.Register( + httpmock.REST("GET", "1/indexes/foo/settings"), + httpmock.JSONResponse(search.SettingsResponse{}), + ) r.Register( httpmock.REST("POST", "1/indexes/foo/rules/search"), - httpmock.JSONResponse(search.SearchSynonymsRes{ + httpmock.JSONResponse(search.SearchRulesResponse{ Hits: tt.hits, }), ) From 96633ef277a7de200a2a231a0fe3c1239eb3a33e Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 08:11:08 +0100 Subject: [PATCH 27/45] feat: migrate rules delete --- pkg/cmd/rules/delete/delete.go | 40 ++++++++++++++++++-------- pkg/cmd/rules/delete/delete_test.go | 44 ++++++++++++++--------------- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/pkg/cmd/rules/delete/delete.go b/pkg/cmd/rules/delete/delete.go index faf39387..35704f48 100644 --- a/pkg/cmd/rules/delete/delete.go +++ b/pkg/cmd/rules/delete/delete.go @@ -5,8 +5,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -21,11 +20,12 @@ type DeleteOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string RuleIDs []string ForwardToReplicas bool + Wait bool DoConfirm bool } @@ -59,7 +59,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co $ algolia rules delete MOVIES --rule-ids 1,2 `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] if !confirm { if !opts.IO.CanPrompt() { return cmdutil.FlagErrorf( @@ -83,6 +83,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -93,9 +94,8 @@ func runDeleteCmd(opts *DeleteOptions) error { return err } - indice := client.InitIndex(opts.Indice) for _, ruleID := range opts.RuleIDs { - if _, err := indice.GetRule(ruleID); err != nil { + if _, err := client.GetRule(client.NewApiGetRuleRequest(opts.Index, ruleID)); err != nil { // The original error is not helpful, so we print a more helpful message extra := "Operation aborted, no deletion action taken" if strings.Contains(err.Error(), "ObjectID does not exist") { @@ -111,7 +111,7 @@ func runDeleteCmd(opts *DeleteOptions) error { fmt.Sprintf( "Delete the %s from %s?", utils.Pluralize(len(opts.RuleIDs), "rule"), - opts.Indice, + opts.Index, ), &confirmed, ) @@ -123,11 +123,27 @@ func runDeleteCmd(opts *DeleteOptions) error { } } + var taskIDs []int64 + for _, ruleID := range opts.RuleIDs { - _, err = indice.DeleteRule(ruleID, opt.ForwardToReplicas(opts.ForwardToReplicas)) + res, err := client.DeleteRule( + client.NewApiDeleteRuleRequest(opts.Index, ruleID). + WithForwardToReplicas(opts.ForwardToReplicas), + ) if err != nil { - err = fmt.Errorf("failed to delete rule %s: %w", ruleID, err) - return err + return fmt.Errorf("failed to delete rule %s: %w", ruleID, err) + } + if opts.Wait { + taskIDs = append(taskIDs, res.TaskID) + } + } + + if len(taskIDs) > 0 { + for _, taskID := range taskIDs { + _, err := client.WaitForTask(opts.Index, taskID) + if err != nil { + return err + } } } @@ -138,7 +154,7 @@ func runDeleteCmd(opts *DeleteOptions) error { "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), utils.Pluralize(len(opts.RuleIDs), "rule"), - opts.Indice, + opts.Index, ) } diff --git a/pkg/cmd/rules/delete/delete_test.go b/pkg/cmd/rules/delete/delete_test.go index 10626523..82537442 100644 --- a/pkg/cmd/rules/delete/delete_test.go +++ b/pkg/cmd/rules/delete/delete_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -36,7 +36,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", RuleIDs: []string{ "1", }, @@ -50,7 +50,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: true, - Indice: "foo", + Index: "foo", RuleIDs: []string{ "1", }, @@ -64,7 +64,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", RuleIDs: []string{ "1", "2", @@ -79,7 +79,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", RuleIDs: []string{ "1", "2", @@ -121,7 +121,7 @@ func TestNewDeleteCmd(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, "", stderr.String()) - assert.Equal(t, tt.wantsOpts.Indice, opts.Indice) + assert.Equal(t, tt.wantsOpts.Index, opts.Index) assert.Equal(t, tt.wantsOpts.RuleIDs, opts.RuleIDs) assert.Equal(t, tt.wantsOpts.ForwardToReplicas, opts.ForwardToReplicas) assert.Equal(t, tt.wantsOpts.DoConfirm, opts.DoConfirm) @@ -133,15 +133,15 @@ func Test_runDeleteCmd(t *testing.T) { tests := []struct { name string cli string - indice string + index string ruleIDs []string isTTY bool wantOut string }{ { - name: "single rule-id, no TTY", - cli: "foo --rule-ids 1 --confirm", - indice: "foo", + name: "single rule-id, no TTY", + cli: "foo --rule-ids 1 --confirm", + index: "foo", ruleIDs: []string{ "1", }, @@ -149,9 +149,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "", }, { - name: "single rule-id, TTY", - cli: "foo --rule-ids 1 --confirm", - indice: "foo", + name: "single rule-id, TTY", + cli: "foo --rule-ids 1 --confirm", + index: "foo", ruleIDs: []string{ "1", }, @@ -159,9 +159,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "✓ Successfully deleted 1 rule from foo\n", }, { - name: "multiple rule-ids, TTY", - cli: "foo --rule-ids 1,2 --confirm", - indice: "foo", + name: "multiple rule-ids, TTY", + cli: "foo --rule-ids 1,2 --confirm", + index: "foo", ruleIDs: []string{ "1", "2", @@ -176,12 +176,12 @@ func Test_runDeleteCmd(t *testing.T) { r := httpmock.Registry{} for _, id := range tt.ruleIDs { r.Register( - httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), - httpmock.JSONResponse(search.SearchRulesRes{}), + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/rules/%s", tt.index, id)), + httpmock.JSONResponse(search.SearchRulesResponse{}), ) r.Register( - httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/rules/%s", tt.indice, id)), - httpmock.JSONResponse(search.DeleteTaskRes{}), + httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/rules/%s", tt.index, id)), + httpmock.JSONResponse(search.DeletedAtResponse{}), ) } From cb8e00eb5aeb0817fe5f985f8ad93a8328d2a9eb Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 08:31:13 +0100 Subject: [PATCH 28/45] feat: migrate rules import --- pkg/cmd/rules/import/import.go | 48 +++++++++++++---------------- pkg/cmd/rules/import/import_test.go | 24 +++++++-------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/pkg/cmd/rules/import/import.go b/pkg/cmd/rules/import/import.go index 06e2dc6d..bafd067e 100644 --- a/pkg/cmd/rules/import/import.go +++ b/pkg/cmd/rules/import/import.go @@ -6,8 +6,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -21,9 +20,9 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string ForwardToReplicas bool ClearExistingRules bool Scanner *bufio.Scanner @@ -36,7 +35,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var confirm bool @@ -45,7 +44,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -68,7 +67,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co $ algolia rules import MOVIES -F rules.ndjson -f=false `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] if !confirm && opts.ClearExistingRules { if !opts.IO.CanPrompt() { @@ -113,7 +112,7 @@ func runImportCmd(opts *ImportOptions) error { err := prompt.Confirm( fmt.Sprintf( "Are you sure you want to replace all the existing rules on %q?", - opts.Indice, + opts.Index, ), &confirmed, ) @@ -130,20 +129,10 @@ func runImportCmd(opts *ImportOptions) error { return err } - indice := client.InitIndex(opts.Indice) - defaultBatchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - } - // Only clear existing rules on the first batch - batchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - opt.ClearExistingRules(opts.ClearExistingRules), - } - // Move the following code to another module? var ( batchSize = 1000 - batch = make([]search.Rule, 0, batchSize) + rules = make([]search.Rule, 0, batchSize) count = 0 totalCount = 0 ) @@ -161,30 +150,37 @@ func runImportCmd(opts *ImportOptions) error { return err } - batch = append(batch, rule) + rules = append(rules, rule) count++ + // If requested, only clear existing rules the first time + clearExistingRules := opts.ClearExistingRules if count == batchSize { - if _, err := indice.SaveRules(batch, batchOptions...); err != nil { + _, err := client.SaveRules( + client.NewApiSaveRulesRequest(opts.Index, rules). + WithClearExistingRules(clearExistingRules). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { return err } - batchOptions = defaultBatchOptions - batch = make([]search.Rule, 0, batchSize) + rules = make([]search.Rule, 0, batchSize) totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d rules", totalCount)) count = 0 + clearExistingRules = false } } if count > 0 { totalCount += count - if _, err := indice.SaveRules(batch, batchOptions...); err != nil { + if _, err := client.SaveRules(client.NewApiSaveRulesRequest(opts.Index, rules).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } // Clear rules if 0 rules are imported and the clear existing is set if totalCount == 0 && opts.ClearExistingRules { - if _, err := indice.ClearRules(); err != nil { + if _, err := client.ClearRules(client.NewApiClearRulesRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } @@ -202,7 +198,7 @@ func runImportCmd(opts *ImportOptions) error { "%s Successfully imported %s rules to %s\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(totalCount)), - opts.Indice, + opts.Index, ) } diff --git a/pkg/cmd/rules/import/import_test.go b/pkg/cmd/rules/import/import_test.go index 7d1c1fe7..cfafecdf 100644 --- a/pkg/cmd/rules/import/import_test.go +++ b/pkg/cmd/rules/import/import_test.go @@ -8,15 +8,15 @@ import ( "strings" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewImportCmd(t *testing.T) { @@ -44,7 +44,7 @@ func TestNewImportCmd(t *testing.T) { name: "file specified", cli: fmt.Sprintf("index -F %s", file), wantsOpts: ImportOptions{ - Indice: "index", + Index: "index", ForwardToReplicas: true, ClearExistingRules: false, }, @@ -53,7 +53,7 @@ func TestNewImportCmd(t *testing.T) { name: "forward to replicas", cli: fmt.Sprintf("index -F %s -f=false", file), wantsOpts: ImportOptions{ - Indice: "index", + Index: "index", ForwardToReplicas: false, ClearExistingRules: false, }, @@ -69,7 +69,7 @@ func TestNewImportCmd(t *testing.T) { tty: false, cli: fmt.Sprintf("index -F %s -c --confirm", file), wantsOpts: ImportOptions{ - Indice: "index", + Index: "index", ForwardToReplicas: true, ClearExistingRules: true, }, @@ -104,7 +104,7 @@ func TestNewImportCmd(t *testing.T) { } require.NoError(t, err) - assert.Equal(t, tt.wantsOpts.Indice, opts.Indice) + assert.Equal(t, tt.wantsOpts.Index, opts.Index) assert.Equal(t, tt.wantsOpts.ForwardToReplicas, opts.ForwardToReplicas) assert.Equal(t, tt.wantsOpts.ClearExistingRules, opts.ClearExistingRules) }) @@ -137,7 +137,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/rules/batch"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -148,7 +148,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/rules/batch"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -167,7 +167,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/rules/clear"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -187,11 +187,11 @@ func Test_runExportCmd(t *testing.T) { r.Register(httpmock.Matcher(func(req *http.Request) bool { return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && req.URL.Query().Get("clearExistingRules") == "true" - }), httpmock.JSONResponse(search.UpdateTaskRes{})) + }), httpmock.JSONResponse(search.UpdatedAtResponse{})) r.Register(httpmock.Matcher(func(req *http.Request) bool { return httpmock.REST("POST", "1/indexes/foo/rules/batch")(req) && req.URL.Query().Get("clearExistingRules") == "" - }), httpmock.JSONResponse(search.UpdateTaskRes{})) + }), httpmock.JSONResponse(search.UpdatedAtResponse{})) }, }, } From 7399ea1da12a47bdd3537e4bbc6aa69ea9dddca9 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 09:11:11 +0100 Subject: [PATCH 29/45] feat: migrate synonyms browse --- pkg/cmd/synonyms/browse/browse.go | 6 +++--- pkg/cmd/synonyms/browse/browse_test.go | 27 +++++++++++++++----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/synonyms/browse/browse.go b/pkg/cmd/synonyms/browse/browse.go index 53678bee..55ca51ff 100644 --- a/pkg/cmd/synonyms/browse/browse.go +++ b/pkg/cmd/synonyms/browse/browse.go @@ -29,7 +29,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -37,7 +37,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { Use: "browse ", Aliases: []string{"list"}, Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Short: "List all the the synonyms of the given index", Annotations: map[string]string{ "runInWebCLI": "true", @@ -73,7 +73,7 @@ func runBrowseCmd(opts *BrowseOptions) error { return err } if !exists { - return fmt.Errorf("index %s doesn't exist", opts.Index) + return fmt.Errorf("Index %s doesn't exist.", opts.Index) } p, err := opts.PrintFlags.ToPrinter() diff --git a/pkg/cmd/synonyms/browse/browse_test.go b/pkg/cmd/synonyms/browse/browse_test.go index e38102d6..e0526651 100644 --- a/pkg/cmd/synonyms/browse/browse_test.go +++ b/pkg/cmd/synonyms/browse/browse_test.go @@ -3,43 +3,48 @@ package browse import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runBrowseCmd(t *testing.T) { tests := []struct { name string cli string - hits []map[string]interface{} + hits []search.SynonymHit wantOut string }{ { name: "single synonym", cli: "foo", - hits: []map[string]interface{}{{"objectID": "foo", "type": "synonym"}}, - wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\",\"synonyms\":null}\n", + hits: []search.SynonymHit{{ObjectID: "foo", Type: "synonym"}}, + wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\"}\n", }, { name: "multiple synonyms", cli: "foo", - hits: []map[string]interface{}{ - {"objectID": "foo", "type": "synonym"}, - {"objectID": "bar", "type": "synonym"}, + hits: []search.SynonymHit{ + {ObjectID: "foo", Type: "synonym"}, + {ObjectID: "bar", Type: "synonym"}, }, - wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\",\"synonyms\":null}\n{\"objectID\":\"bar\",\"type\":\"synonym\",\"synonyms\":null}\n", + wantOut: "{\"objectID\":\"foo\",\"type\":\"synonym\"}\n{\"objectID\":\"bar\",\"type\":\"synonym\"}\n", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} + // Check if index exists + r.Register( + httpmock.REST("GET", "1/indexes/foo/settings"), + httpmock.JSONResponse(search.SettingsResponse{}), + ) r.Register( httpmock.REST("POST", "1/indexes/foo/synonyms/search"), - httpmock.JSONResponse(search.SearchSynonymsRes{ + httpmock.JSONResponse(search.SearchSynonymsResponse{ Hits: tt.hits, }), ) From 091d81e27f1f328f7d27b5b2a9608ece40dcc0d2 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 09:37:23 +0100 Subject: [PATCH 30/45] feat: migrate synonyms delete --- pkg/cmd/synonyms/delete/delete.go | 26 +++++++------- pkg/cmd/synonyms/delete/delete_test.go | 47 ++++++++++++++------------ 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/pkg/cmd/synonyms/delete/delete.go b/pkg/cmd/synonyms/delete/delete.go index e9460704..981e2184 100644 --- a/pkg/cmd/synonyms/delete/delete.go +++ b/pkg/cmd/synonyms/delete/delete.go @@ -5,8 +5,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -21,9 +20,9 @@ type DeleteOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string SynonymIDs []string ForwardToReplicas bool @@ -37,13 +36,13 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "delete --synonyms --confirm", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -59,7 +58,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co $ algolia synonyms delete MOVIES --synonym-ids 1,2 `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] if !confirm { if !opts.IO.CanPrompt() { return cmdutil.FlagErrorf( @@ -93,11 +92,9 @@ func runDeleteCmd(opts *DeleteOptions) error { return err } - indice := client.InitIndex(opts.Indice) - // Tests if the synonyms exists. for _, synonymID := range opts.SynonymIDs { - if _, err := indice.GetSynonym(synonymID); err != nil { + if _, err := client.GetSynonym(client.NewApiGetSynonymRequest(opts.Index, synonymID)); err != nil { // The original error is not helpful, so we print a more helpful message extra := "Operation aborted, no deletion action taken" if strings.Contains(err.Error(), "Synonym set does not exist") { @@ -113,7 +110,7 @@ func runDeleteCmd(opts *DeleteOptions) error { fmt.Sprintf( "Delete the %s from %s?", utils.Pluralize(len(opts.SynonymIDs), "synonym"), - opts.Indice, + opts.Index, ), &confirmed, ) @@ -126,7 +123,10 @@ func runDeleteCmd(opts *DeleteOptions) error { } for _, synonymID := range opts.SynonymIDs { - _, err = indice.DeleteSynonym(synonymID, opt.ForwardToReplicas(opts.ForwardToReplicas)) + _, err = client.DeleteSynonym( + client.NewApiDeleteSynonymRequest(opts.Index, synonymID). + WithForwardToReplicas(opts.ForwardToReplicas), + ) if err != nil { err = fmt.Errorf("failed to delete synonym %s: %w", synonymID, err) return err @@ -140,7 +140,7 @@ func runDeleteCmd(opts *DeleteOptions) error { "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), utils.Pluralize(len(opts.SynonymIDs), "synonym"), - opts.Indice, + opts.Index, ) } diff --git a/pkg/cmd/synonyms/delete/delete_test.go b/pkg/cmd/synonyms/delete/delete_test.go index 39d350b4..d39cbbc3 100644 --- a/pkg/cmd/synonyms/delete/delete_test.go +++ b/pkg/cmd/synonyms/delete/delete_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -36,7 +36,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", SynonymIDs: []string{ "1", }, @@ -50,7 +50,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: true, - Indice: "foo", + Index: "foo", SynonymIDs: []string{ "1", }, @@ -64,7 +64,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", SynonymIDs: []string{ "1", "2", @@ -79,7 +79,7 @@ func TestNewDeleteCmd(t *testing.T) { wantsErr: false, wantsOpts: DeleteOptions{ DoConfirm: false, - Indice: "foo", + Index: "foo", SynonymIDs: []string{ "1", "2", @@ -121,7 +121,7 @@ func TestNewDeleteCmd(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, "", stderr.String()) - assert.Equal(t, tt.wantsOpts.Indice, opts.Indice) + assert.Equal(t, tt.wantsOpts.Index, opts.Index) assert.Equal(t, tt.wantsOpts.SynonymIDs, opts.SynonymIDs) assert.Equal(t, tt.wantsOpts.ForwardToReplicas, opts.ForwardToReplicas) assert.Equal(t, tt.wantsOpts.DoConfirm, opts.DoConfirm) @@ -133,15 +133,15 @@ func Test_runDeleteCmd(t *testing.T) { tests := []struct { name string cli string - indice string + index string synonymIDs []string isTTY bool wantOut string }{ { - name: "single synonym-id, no TTY", - cli: "foo --synonym-ids 1 --confirm", - indice: "foo", + name: "single synonym-id, no TTY", + cli: "foo --synonym-ids 1 --confirm", + index: "foo", synonymIDs: []string{ "1", }, @@ -149,9 +149,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "", }, { - name: "single synonym-id, TTY", - cli: "foo --synonym-ids 1 --confirm", - indice: "foo", + name: "single synonym-id, TTY", + cli: "foo --synonym-ids 1 --confirm", + index: "foo", synonymIDs: []string{ "1", }, @@ -159,9 +159,9 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "✓ Successfully deleted 1 synonym from foo\n", }, { - name: "multiple synonym-ids, TTY", - cli: "foo --synonym-ids 1,2 --confirm", - indice: "foo", + name: "multiple synonym-ids, TTY", + cli: "foo --synonym-ids 1,2 --confirm", + index: "foo", synonymIDs: []string{ "1", "2", @@ -176,12 +176,15 @@ func Test_runDeleteCmd(t *testing.T) { r := httpmock.Registry{} for _, id := range tt.synonymIDs { r.Register( - httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), - httpmock.JSONResponse(search.OneWaySynonym{}), + httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.index, id)), + httpmock.JSONResponse(search.SynonymHit{ + ObjectID: "1", + Type: search.SYNONYM_TYPE_ONEWAYSYNONYM, + }), ) r.Register( - httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, id)), - httpmock.JSONResponse(search.DeleteTaskRes{}), + httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.index, id)), + httpmock.JSONResponse(search.DeletedAtResponse{}), ) } From 2776091bc0835927875dbb193b0679fb0ad8c81e Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 09:49:12 +0100 Subject: [PATCH 31/45] feat: migrate synonyms save --- pkg/cmd/synonyms/save/messages_test.go | 2 +- pkg/cmd/synonyms/save/save.go | 29 ++++---- pkg/cmd/synonyms/save/save_test.go | 51 +++++++------- pkg/cmd/synonyms/shared/flags_to_synonym.go | 69 ++++++++++--------- .../synonyms/shared/flags_to_synonym_test.go | 28 ++++---- 5 files changed, 89 insertions(+), 90 deletions(-) diff --git a/pkg/cmd/synonyms/save/messages_test.go b/pkg/cmd/synonyms/save/messages_test.go index 3db665f4..a06bb64d 100644 --- a/pkg/cmd/synonyms/save/messages_test.go +++ b/pkg/cmd/synonyms/save/messages_test.go @@ -91,7 +91,7 @@ func Test_GetSynonymSuccessMessage(t *testing.T) { IOStreams: io, } - message, err := GetSuccessMessage(tt.synonymFlags, tt.saveOptions.Index) + err, message := GetSuccessMessage(tt.synonymFlags, tt.saveOptions.Index) assert.Equal(t, err, nil) assert.Equal( diff --git a/pkg/cmd/synonyms/save/save.go b/pkg/cmd/synonyms/save/save.go index 65dacd4b..5a9bcff5 100644 --- a/pkg/cmd/synonyms/save/save.go +++ b/pkg/cmd/synonyms/save/save.go @@ -4,8 +4,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/shared/handler" @@ -20,11 +19,11 @@ type SaveOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Indice string + Index string ForwardToReplicas bool - Synonym search.Synonym + Synonym search.SynonymHit SuccessMessage string } @@ -33,7 +32,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman opts := &SaveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } flags := &shared.SynonymFlags{} @@ -41,7 +40,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "save --id --synonyms ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -56,7 +55,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman $ algolia synonyms save MOVIES --id 1 --synonyms foo,bar `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Indice = args[0] + opts.Index = args[0] flagsHandler := &handler.SynonymHandler{ Flags: flags, @@ -72,10 +71,9 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman if err != nil { return err } - // Correct flags are passed - opts.Synonym = synonym + opts.Synonym = *synonym - err, successMessage := GetSuccessMessage(*flags, opts.Indice) + err, successMessage := GetSuccessMessage(*flags, opts.Index) if err != nil { return err } @@ -131,11 +129,10 @@ func runSaveCmd(opts *SaveOptions) error { if err != nil { return err } - - indice := client.InitIndex(opts.Indice) - forwardToReplicas := opt.ForwardToReplicas(opts.ForwardToReplicas) - - _, err = indice.SaveSynonym(opts.Synonym, forwardToReplicas) + _, err = client.SaveSynonym( + client.NewApiSaveSynonymRequest(opts.Index, opts.Synonym.ObjectID, &opts.Synonym). + WithForwardToReplicas(opts.ForwardToReplicas), + ) if err != nil { err = fmt.Errorf("failed to save synonym: %w", err) return err diff --git a/pkg/cmd/synonyms/save/save_test.go b/pkg/cmd/synonyms/save/save_test.go index 5417ef34..4b636ba0 100644 --- a/pkg/cmd/synonyms/save/save_test.go +++ b/pkg/cmd/synonyms/save/save_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewSaveCmd(t *testing.T) { @@ -29,11 +29,8 @@ func TestNewSaveCmd(t *testing.T) { tty: false, wantsErr: false, wantsOpts: SaveOptions{ - Indice: "legends", - Synonym: search.NewRegularSynonym( - "1", - "jordan", "mj", - ), + Index: "legends", + Synonym: *search.NewEmptySynonymHit().SetObjectID("1").SetType(search.SYNONYM_TYPE_SYNONYM).SetSynonyms([]string{"jordan", "mj"}), ForwardToReplicas: false, }, }, @@ -43,11 +40,8 @@ func TestNewSaveCmd(t *testing.T) { tty: true, wantsErr: false, wantsOpts: SaveOptions{ - Indice: "legends", - Synonym: search.NewRegularSynonym( - "1", - "jordan", "mj", - ), + Index: "legends", + Synonym: *search.NewEmptySynonymHit().SetObjectID("1").SetType(search.SYNONYM_TYPE_SYNONYM).SetSynonyms([]string{"jordan", "mj"}), ForwardToReplicas: false, }, }, @@ -86,7 +80,7 @@ func TestNewSaveCmd(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, "", stderr.String()) - assert.Equal(t, tt.wantsOpts.Indice, opts.Indice) + assert.Equal(t, tt.wantsOpts.Index, opts.Index) assert.Equal(t, tt.wantsOpts.Synonym, opts.Synonym) assert.Equal(t, tt.wantsOpts.ForwardToReplicas, opts.ForwardToReplicas) }) @@ -97,15 +91,15 @@ func Test_runSaveCmd(t *testing.T) { tests := []struct { name string cli string - indice string + index string synonymID string isTTY bool wantOut string }{ { name: "single id, two synonyms, no TTY", - cli: "legends --id 1 --synonyms jorda,mj", - indice: "legends", + cli: "legends --id 1 --synonyms jordan,mj", + index: "legends", synonymID: "1", isTTY: false, wantOut: "", @@ -113,7 +107,7 @@ func Test_runSaveCmd(t *testing.T) { { name: "single id, two synonyms, TTY", cli: "legends --id 1 --synonyms jordan,mj", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ Synonym '1' successfully saved with 2 synonyms (jordan, mj) to legends\n", @@ -121,7 +115,7 @@ func Test_runSaveCmd(t *testing.T) { { name: "single id, mutiple synonyms, TTY", cli: "legends --id 1 --synonyms jordan,mj,goat,michael,23", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ Synonym '1' successfully saved with 5 synonyms (jordan, mj, goat, michael, 23) to legends\n", @@ -129,15 +123,15 @@ func Test_runSaveCmd(t *testing.T) { { name: "single id, mutiple synonyms, TTY with shorthands", cli: "legends -i 1 -s jordan,mj,goat,michael,23", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ Synonym '1' successfully saved with 5 synonyms (jordan, mj, goat, michael, 23) to legends\n", }, { - name: "single id, oneWaySynonym type, multiple synonyms, TTY", + name: "single id, one-way-synonym type, multiple synonyms, TTY", cli: "legends --id 1 --type oneWaySynonym --synonyms jordan,mj,goat,michael --input 23", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ One way synonym '1' successfully saved with input '23' and 4 synonyms (jordan, mj, goat, michael) to legends\n", @@ -145,15 +139,15 @@ func Test_runSaveCmd(t *testing.T) { { name: "single id, placeholder type, one placeholder, multiple replacements, TTY", cli: "legends -i 1 -t placeholder -l jordan -r mj,goat,michael,23", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ Placeholder synonym '1' successfully saved with placeholder 'jordan' and 4 replacements (mj, goat, michael, 23) to legends\n", }, { - name: "single id, altCorrection1 type, one word, multiple corrections, TTY", + name: "single id, altcorrection1 type, one word, multiple corrections, TTY", cli: "legends -i 1 -t altCorrection1 -w jordan -c mj,goat,michael,23", - indice: "legends", + index: "legends", synonymID: "1", isTTY: true, wantOut: "✓ Alt correction 1 synonym '1' successfully saved with word 'jordan' and 4 corrections (mj, goat, michael, 23) to legends\n", @@ -166,9 +160,12 @@ func Test_runSaveCmd(t *testing.T) { r.Register( httpmock.REST( "PUT", - fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.indice, tt.synonymID), + fmt.Sprintf("1/indexes/%s/synonyms/%s", tt.index, tt.synonymID), ), - httpmock.JSONResponse(search.RegularSynonym{}), + httpmock.JSONResponse(search.SynonymHit{ + ObjectID: "1", + // Type: search.SYNONYM_TYPE_SYNONYM, + }), ) defer r.Verify(t) diff --git a/pkg/cmd/synonyms/shared/flags_to_synonym.go b/pkg/cmd/synonyms/shared/flags_to_synonym.go index 0ab7d8a2..8e0931c0 100644 --- a/pkg/cmd/synonyms/shared/flags_to_synonym.go +++ b/pkg/cmd/synonyms/shared/flags_to_synonym.go @@ -3,7 +3,7 @@ package shared import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" ) type SynonymFlags struct { @@ -22,12 +22,12 @@ type SynonymFlags struct { type SynonymType string const ( - // Matching API https://www.algolia.com/doc/api-reference/api-methods/save-synonym/#method-param-type - Regular string = "synonym" - OneWay string = "oneWaySynonym" - AltCorrection1 string = "altCorrection1" - AltCorrection2 string = "altCorrection2" - Placeholder string = "placeholder" + // "synonym" + Regular string = string(search.SYNONYM_TYPE_SYNONYM) + OneWay string = string(search.SYNONYM_TYPE_ONE_WAY_SYNONYM) + AltCorrection1 string = string(search.SYNONYM_TYPE_ALT_CORRECTION1) + AltCorrection2 string = string(search.SYNONYM_TYPE_ALT_CORRECTION2) + Placeholder string = string(search.SYNONYM_TYPE_PLACEHOLDER) ) func (e *SynonymType) String() string { @@ -55,37 +55,42 @@ func (e *SynonymType) Type() string { return "SynonymType" } -func FlagsToSynonym(flags SynonymFlags) (search.Synonym, error) { +func FlagsToSynonym(flags SynonymFlags) (*search.SynonymHit, error) { switch flags.SynonymType { case OneWay: - return search.NewOneWaySynonym( - flags.SynonymID, - flags.SynonymInput, - flags.Synonyms..., - ), nil + return search.NewEmptySynonymHit(). + SetType(search.SYNONYM_TYPE_ONE_WAY_SYNONYM). + SetObjectID(flags.SynonymID). + SetInput(flags.SynonymInput). + SetSynonyms(flags.Synonyms), + nil case AltCorrection1: - return search.NewAltCorrection1( - flags.SynonymID, - flags.SynonymWord, - flags.SynonymCorrections..., - ), nil + return search.NewEmptySynonymHit(). + SetType(search.SYNONYM_TYPE_ALT_CORRECTION1). + SetObjectID(flags.SynonymID). + SetWord(flags.SynonymWord). + SetCorrections(flags.SynonymCorrections), + nil case AltCorrection2: - return search.NewAltCorrection2( - flags.SynonymID, - flags.SynonymWord, - flags.SynonymCorrections..., - ), nil + return search.NewEmptySynonymHit(). + SetType(search.SYNONYM_TYPE_ALT_CORRECTION2). + SetObjectID(flags.SynonymID). + SetWord(flags.SynonymWord). + SetCorrections(flags.SynonymCorrections), + nil case Placeholder: - return search.NewPlaceholder( - flags.SynonymID, - flags.SynonymPlaceholder, - flags.SynonymReplacements..., - ), nil + return search.NewEmptySynonymHit(). + SetType(search.SYNONYM_TYPE_PLACEHOLDER). + SetObjectID(flags.SynonymID). + SetPlaceholder(flags.SynonymPlaceholder). + SetReplacements(flags.SynonymReplacements), + nil case "", Regular: - return search.NewRegularSynonym( - flags.SynonymID, - flags.Synonyms..., - ), nil + return search.NewEmptySynonymHit(). + SetType(search.SYNONYM_TYPE_SYNONYM). + SetObjectID(flags.SynonymID). + SetSynonyms(flags.Synonyms), + nil } return nil, fmt.Errorf("invalid synonym type") diff --git a/pkg/cmd/synonyms/shared/flags_to_synonym_test.go b/pkg/cmd/synonyms/shared/flags_to_synonym_test.go index 249c5e55..b8d35748 100644 --- a/pkg/cmd/synonyms/shared/flags_to_synonym_test.go +++ b/pkg/cmd/synonyms/shared/flags_to_synonym_test.go @@ -1,9 +1,9 @@ package shared import ( - "reflect" "testing" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" ) @@ -11,7 +11,7 @@ func Test_FlagsToSynonym(t *testing.T) { tests := []struct { name string synonymFlags SynonymFlags - synonymType string + synonymType search.SynonymType wantsErr bool wantsErrMsg string }{ @@ -23,64 +23,64 @@ func Test_FlagsToSynonym(t *testing.T) { SynonymID: "23", Synonyms: []string{"mj", "goat"}, }, - synonymType: "search.RegularSynonym", + synonymType: search.SYNONYM_TYPE_SYNONYM, }, { name: "Regular synonym explicit type", wantsErr: false, synonymFlags: SynonymFlags{ - SynonymType: Regular, + SynonymType: "synonym", SynonymID: "23", Synonyms: []string{"mj", "goat"}, }, - synonymType: "search.RegularSynonym", + synonymType: search.SYNONYM_TYPE_SYNONYM, }, // One way type { name: "One way synonym", wantsErr: false, synonymFlags: SynonymFlags{ - SynonymType: OneWay, + SynonymType: "oneWaySynonym", SynonymID: "23", Synonyms: []string{"mj", "goat"}, SynonymInput: "michael", }, - synonymType: "search.OneWaySynonym", + synonymType: search.SYNONYM_TYPE_ONE_WAY_SYNONYM, }, // Alt correction type { name: "AltCorrection1 synonym", wantsErr: false, synonymFlags: SynonymFlags{ - SynonymType: AltCorrection1, + SynonymType: "altCorrection1", SynonymID: "23", SynonymCorrections: []string{"mj", "goat"}, SynonymWord: "michael", }, - synonymType: "search.AltCorrection1", + synonymType: search.SYNONYM_TYPE_ALT_CORRECTION1, }, { name: "AltCorrection2 synonym", wantsErr: false, synonymFlags: SynonymFlags{ - SynonymType: AltCorrection2, + SynonymType: "altCorrection2", SynonymID: "24", SynonymCorrections: []string{"bryant", "mamba"}, SynonymWord: "kobe", }, - synonymType: "search.AltCorrection2", + synonymType: search.SYNONYM_TYPE_ALT_CORRECTION2, }, // Placeholder type { name: "Placeholder synonym", wantsErr: false, synonymFlags: SynonymFlags{ - SynonymType: Placeholder, + SynonymType: string(search.SYNONYM_TYPE_PLACEHOLDER), SynonymID: "23", SynonymReplacements: []string{"james", "lebron"}, SynonymPlaceholder: "king", }, - synonymType: "search.Placeholder", + synonymType: search.SYNONYM_TYPE_PLACEHOLDER, }, // Wrong type { @@ -105,7 +105,7 @@ func Test_FlagsToSynonym(t *testing.T) { return } - assert.Equal(t, reflect.TypeOf(synonym).String(), tt.synonymType) + assert.Equal(t, synonym.Type, tt.synonymType) }) } } From 6ef692500180394c4bd74dee30d2ff5e79a09bdd Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 10:04:10 +0100 Subject: [PATCH 32/45] feat: migrate synonyms import --- pkg/cmd/synonyms/import/import.go | 85 +++++--------------------- pkg/cmd/synonyms/import/import_test.go | 18 +++--- 2 files changed, 24 insertions(+), 79 deletions(-) diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index 542dddb8..197df43f 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -6,8 +6,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -20,7 +19,7 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) Index string ForwardToReplicas bool @@ -33,7 +32,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } var file string @@ -41,7 +40,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, @@ -101,20 +100,13 @@ func runImportCmd(opts *ImportOptions) error { return err } - indice := client.InitIndex(opts.Index) - defaultBatchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - } - // Only clear existing rules on the first batch - batchOptions := []interface{}{ - opt.ForwardToReplicas(opts.ForwardToReplicas), - opt.ReplaceExistingSynonyms(opts.ReplaceExistingSynonyms), - } + // Only clear existing synonyms on the first batch + clearExistingSynonyms := opts.ReplaceExistingSynonyms // Move the following code to another module? var ( batchSize = 1000 - batch = make([]search.Synonym, 0, batchSize) + synonyms = make([]search.SynonymHit, 0, batchSize) count = 0 totalCount = 0 ) @@ -127,85 +119,38 @@ func runImportCmd(opts *ImportOptions) error { } lineB := []byte(line) - var rawSynonym map[string]interface{} + var synonym search.SynonymHit // Unmarshal as map[string]interface{} to get the type of the synonym - if err := json.Unmarshal(lineB, &rawSynonym); err != nil { + if err := json.Unmarshal(lineB, &synonym); err != nil { err := fmt.Errorf("failed to parse JSON synonym on line %d: %s", count, err) return err } - typeString := rawSynonym["type"].(string) - - // This is really ugly, but algoliasearch package doesn't provide a way to - // unmarshal a synonym from a JSON string. - switch search.SynonymType(typeString) { - case search.RegularSynonymType: - var syn search.RegularSynonym - err = json.Unmarshal(lineB, &syn) - if err != nil { - return err - } - batch = append(batch, syn) - - case search.OneWaySynonymType: - var syn search.OneWaySynonym - err = json.Unmarshal(lineB, &syn) - if err != nil { - return err - } - batch = append(batch, syn) - - case search.AltCorrection1Type: - var syn search.AltCorrection1 - err = json.Unmarshal(lineB, &syn) - if err != nil { - return err - } - batch = append(batch, syn) - - case search.AltCorrection2Type: - var syn search.AltCorrection2 - err = json.Unmarshal(lineB, &syn) - if err != nil { - return err - } - batch = append(batch, syn) - - case search.PlaceholderType: - var syn search.Placeholder - err = json.Unmarshal(lineB, &syn) - if err != nil { - return err - } - batch = append(batch, syn) - - default: - return fmt.Errorf("cannot unmarshal synonym: unknown type %s", typeString) - } + synonyms = append(synonyms, synonym) count++ if count == batchSize { - if _, err := indice.SaveSynonyms(batch, batchOptions...); err != nil { + if _, err := client.SaveSynonyms(client.NewApiSaveSynonymsRequest(opts.Index, synonyms).WithReplaceExistingSynonyms(clearExistingSynonyms).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } - batchOptions = defaultBatchOptions - batch = make([]search.Synonym, 0, batchSize) + synonyms = make([]search.SynonymHit, 0, batchSize) totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d synonyms", totalCount)) count = 0 + clearExistingSynonyms = false } } if count > 0 { totalCount += count - if _, err := indice.SaveSynonyms(batch, batchOptions...); err != nil { + if _, err := client.SaveSynonyms(client.NewApiSaveSynonymsRequest(opts.Index, synonyms).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } if totalCount == 0 && opts.ReplaceExistingSynonyms { - if _, err := indice.ClearSynonyms(); err != nil { + if _, err := client.ClearSynonyms(client.NewApiClearSynonymsRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { return err } } diff --git a/pkg/cmd/synonyms/import/import_test.go b/pkg/cmd/synonyms/import/import_test.go index b2815e1a..1c1ed2b4 100644 --- a/pkg/cmd/synonyms/import/import_test.go +++ b/pkg/cmd/synonyms/import/import_test.go @@ -8,15 +8,15 @@ import ( "strings" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewImportCmd(t *testing.T) { @@ -140,7 +140,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -151,7 +151,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -170,7 +170,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/synonyms/batch"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -182,7 +182,7 @@ func Test_runExportCmd(t *testing.T) { setup: func(r *httpmock.Registry) { r.Register( httpmock.REST("POST", "1/indexes/foo/synonyms/clear"), - httpmock.JSONResponse(search.UpdateTaskRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) }, }, @@ -202,11 +202,11 @@ func Test_runExportCmd(t *testing.T) { r.Register(httpmock.Matcher(func(req *http.Request) bool { return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && req.URL.Query().Get("replaceExistingSynonyms") == "true" - }), httpmock.JSONResponse(search.UpdateTaskRes{})) + }), httpmock.JSONResponse(search.UpdatedAtResponse{})) r.Register(httpmock.Matcher(func(req *http.Request) bool { return httpmock.REST("POST", "1/indexes/foo/synonyms/batch")(req) && req.URL.Query().Get("replaceExistingSynonyms") == "" - }), httpmock.JSONResponse(search.UpdateTaskRes{})) + }), httpmock.JSONResponse(search.UpdatedAtResponse{})) }, }, } From e54cc836c3ecaa94006f86dd3108b64eacc80149 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 11:47:33 +0100 Subject: [PATCH 33/45] feat: add dict shortcut --- pkg/cmd/dictionary/dictionary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/dictionary/dictionary.go b/pkg/cmd/dictionary/dictionary.go index 6fd4889b..e3784fca 100644 --- a/pkg/cmd/dictionary/dictionary.go +++ b/pkg/cmd/dictionary/dictionary.go @@ -13,7 +13,7 @@ import ( func NewDictionaryCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "dictionary", - Aliases: []string{"dictionaries"}, + Aliases: []string{"dictionaries", "dict"}, Short: "Manage your Algolia dictionaries", Annotations: map[string]string{ "help:see-also": heredoc.Doc(` From b71732137da83b790f521aab90dc2dcd5ca71a75 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 11:45:51 +0100 Subject: [PATCH 34/45] feat: migrate dictionary settings get --- pkg/cmd/dictionary/settings/get/get.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/dictionary/settings/get/get.go b/pkg/cmd/dictionary/settings/get/get.go index 04d5fcc4..f8a71cd2 100644 --- a/pkg/cmd/dictionary/settings/get/get.go +++ b/pkg/cmd/dictionary/settings/get/get.go @@ -2,7 +2,7 @@ package get import ( "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -14,7 +14,7 @@ type GetOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) PrintFlags *cmdutil.PrintFlags } @@ -24,7 +24,7 @@ func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command opts := &GetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } cmd := &cobra.Command{ From e9a3d9c02e40cbeb64826d476494db642b604294 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 22:58:03 +0100 Subject: [PATCH 35/45] feat: migrate dictionary settings set --- pkg/cmd/dictionary/settings/set/languages.go | 1 + pkg/cmd/dictionary/settings/set/set.go | 42 ++++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/pkg/cmd/dictionary/settings/set/languages.go b/pkg/cmd/dictionary/settings/set/languages.go index 45425969..2e9116db 100644 --- a/pkg/cmd/dictionary/settings/set/languages.go +++ b/pkg/cmd/dictionary/settings/set/languages.go @@ -1,5 +1,6 @@ package set +// The v4 API client doesn't have the long form of language names var Languages = map[string]string{ "af": "Afrikaans", "sq": "Albanian", diff --git a/pkg/cmd/dictionary/settings/set/set.go b/pkg/cmd/dictionary/settings/set/set.go index c2a73674..ad6a48ba 100644 --- a/pkg/cmd/dictionary/settings/set/set.go +++ b/pkg/cmd/dictionary/settings/set/set.go @@ -4,8 +4,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -17,7 +16,7 @@ type SetOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) DisableStandardEntries []string EnableStandardEntries []string @@ -29,7 +28,7 @@ func NewSetCmd(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command opts := &SetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "set --disable-standard-entries --enable-standard-entries [--reset-standard-entries]", @@ -100,17 +99,17 @@ func NewSetCmd(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command cmd.Flags(). BoolVarP(&opts.ResetStandardEntries, "reset-standard-entries", "r", false, "Reset standard entries to their default values") - SupportedLanguages := make(map[string]string, len(LanguagesWithStopwordsSupport)) + supportedLanguages := make(map[string]string, len(LanguagesWithStopwordsSupport)) for _, languageCode := range LanguagesWithStopwordsSupport { - SupportedLanguages[languageCode] = Languages[languageCode] + supportedLanguages[languageCode] = Languages[languageCode] } _ = cmd.RegisterFlagCompletionFunc( "disable-standard-entries", - cmdutil.StringCompletionFunc(SupportedLanguages), + cmdutil.StringCompletionFunc(supportedLanguages), ) _ = cmd.RegisterFlagCompletionFunc( "enable-standard-entries", - cmdutil.StringCompletionFunc(SupportedLanguages), + cmdutil.StringCompletionFunc(supportedLanguages), ) return cmd @@ -123,37 +122,36 @@ func runSetCmd(opts *SetOptions) error { return err } - var disableStandardEntriesOpt *opt.DisableStandardEntriesOption + var disableStandardEntriesOpt search.StandardEntries if opts.ResetStandardEntries { - disableStandardEntriesOpt = opt.DisableStandardEntries( - map[string]map[string]bool{"stopwords": nil}, - ) + disableStandardEntriesOpt = *search.NewEmptyStandardEntries().SetStopwords(nil) } + stopwords := make(map[string]bool, len(LanguagesWithStopwordsSupport)) if len(opts.DisableStandardEntries) > 0 || len(opts.EnableStandardEntries) > 0 { - stopwords := map[string]map[string]bool{"stopwords": {}} for _, language := range opts.DisableStandardEntries { - stopwords["stopwords"][language] = true + stopwords[language] = true } for _, language := range opts.EnableStandardEntries { - stopwords["stopwords"][language] = false + stopwords[language] = false } - disableStandardEntriesOpt = opt.DisableStandardEntries(stopwords) - } - - dictionarySettings := search.DictionarySettings{ - DisableStandardEntries: disableStandardEntriesOpt, + disableStandardEntriesOpt = *search.NewEmptyStandardEntries().SetStopwords(stopwords) } opts.IO.StartProgressIndicatorWithLabel("Updating dictionary settings") - res, err := client.SetDictionarySettings(dictionarySettings) + + res, err := client.SetDictionarySettings( + client.NewApiSetDictionarySettingsRequest( + search.NewDictionarySettingsParams(disableStandardEntriesOpt), + ), + ) if err != nil { opts.IO.StopProgressIndicator() return err } // Wait for the task to complete (so if the user runs `algolia dictionary settings get` right after, the settings will be updated) - err = res.Wait() + _, err = client.WaitForAppTask(res.TaskID) if err != nil { opts.IO.StopProgressIndicator() return err From efd6c440204332440d801c7ac656224df8942c59 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 16:34:39 +0100 Subject: [PATCH 36/45] feat: migrate dictionary entries browse --- pkg/cmd/dictionary/entries/browse/browse.go | 56 ++++++++---------- .../dictionary/entries/browse/browse_test.go | 59 ++++++++++--------- pkg/cmd/dictionary/shared/constants.go | 41 +++---------- 3 files changed, 62 insertions(+), 94 deletions(-) diff --git a/pkg/cmd/dictionary/entries/browse/browse.go b/pkg/cmd/dictionary/entries/browse/browse.go index d7705aac..0672495f 100644 --- a/pkg/cmd/dictionary/entries/browse/browse.go +++ b/pkg/cmd/dictionary/entries/browse/browse.go @@ -5,8 +5,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/dictionary/shared" @@ -19,25 +18,15 @@ type BrowseOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Dictionaries []search.DictionaryName + Dictionaries []search.DictionaryType All bool IncludeDefaultStopwords bool PrintFlags *cmdutil.PrintFlags } -// DictionaryEntry can be plural, compound or stopword entry. -type DictionaryEntry struct { - Type shared.EntryType - Word string `json:"word,omitempty"` - Words []string `json:"words,omitempty"` - Decomposition string `json:"decomposition,omitempty"` - ObjectID string - Language string -} - // NewBrowseCmd creates and returns a browse command for dictionaries' entries. func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Command { cs := f.IOStreams.ColorScheme() @@ -45,15 +34,17 @@ func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } + cmd := &cobra.Command{ Use: "browse {... | --all} [--include-defaults]", Args: cobra.OnlyValidArgs, - ValidArgs: shared.DictionaryNames(), + Aliases: []string{"list"}, + ValidArgs: shared.DictionaryTypes(), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return shared.DictionaryNames(), cobra.ShellCompDirectiveNoFileComp + return shared.DictionaryTypes(), cobra.ShellCompDirectiveNoFileComp }, Annotations: map[string]string{ "acls": "settings", @@ -83,15 +74,11 @@ func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co } if opts.All { - opts.Dictionaries = []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, - } + opts.Dictionaries = search.AllowedDictionaryTypeEnumValues } else { - opts.Dictionaries = make([]search.DictionaryName, len(args)) - for i, dictionary := range args { - opts.Dictionaries[i] = search.DictionaryName(dictionary) + opts.Dictionaries = make([]search.DictionaryType, len(args)) + for i, dict := range args { + opts.Dictionaries[i] = search.DictionaryType(dict) } } @@ -124,16 +111,19 @@ func runBrowseCmd(opts *BrowseOptions) error { hasNoEntries := true for _, dictionary := range opts.Dictionaries { - pageCount := 0 - maxPages := 1 + var pageCount int32 = 0 + var maxPages int32 = 1 // implement infinite pagination for pageCount < maxPages { res, err := client.SearchDictionaryEntries( - dictionary, - "", - opt.HitsPerPage(1000), - opt.Page(pageCount), + client.NewApiSearchDictionaryEntriesRequest( + dictionary, + search.NewEmptySearchDictionaryEntriesParams(). + SetHitsPerPage(1000). + SetPage(pageCount). + SetQuery(""), + ), ) if err != nil { return err @@ -149,7 +139,7 @@ func runBrowseCmd(opts *BrowseOptions) error { ) } - var entries []DictionaryEntry + var entries []search.DictionaryEntry err = json.Unmarshal(data, &entries) if err != nil { return fmt.Errorf( @@ -168,7 +158,7 @@ func runBrowseCmd(opts *BrowseOptions) error { if err = p.Print(opts.IO, entry); err != nil { return err } - } else if entry.Type == shared.CustomEntryType { + } else if *entry.Type == search.DICTIONARY_ENTRY_TYPE_CUSTOM { // print only custom entries if err = p.Print(opts.IO, entry); err != nil { return err diff --git a/pkg/cmd/dictionary/entries/browse/browse_test.go b/pkg/cmd/dictionary/entries/browse/browse_test.go index 7fce0311..990bcabd 100644 --- a/pkg/cmd/dictionary/entries/browse/browse_test.go +++ b/pkg/cmd/dictionary/entries/browse/browse_test.go @@ -4,18 +4,18 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runBrowseCmd(t *testing.T) { tests := []struct { name string cli string - dictionaries []search.DictionaryName + dictionaries []search.DictionaryType entries bool isTTY bool wantOut string @@ -23,53 +23,53 @@ func Test_runBrowseCmd(t *testing.T) { { name: "one dictionary", cli: "plurals", - dictionaries: []search.DictionaryName{ - search.Plurals, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, entries: true, isTTY: false, - wantOut: "{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n", + wantOut: "{\"objectID\":\"\",\"type\":\"custom\"}\n", }, { name: "multiple dictionaries", cli: "plurals compounds", - dictionaries: []search.DictionaryName{ - search.Plurals, - search.Compounds, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, + search.DICTIONARY_TYPE_COMPOUNDS, }, entries: true, isTTY: false, - wantOut: "{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n", + wantOut: "{\"objectID\":\"\",\"type\":\"custom\"}\n{\"objectID\":\"\",\"type\":\"custom\"}\n", }, { name: "all dictionaries", cli: "--all", - dictionaries: []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_STOPWORDS, + search.DICTIONARY_TYPE_PLURALS, + search.DICTIONARY_TYPE_COMPOUNDS, }, entries: true, isTTY: false, - wantOut: "{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n", + wantOut: "{\"objectID\":\"\",\"type\":\"custom\"}\n{\"objectID\":\"\",\"type\":\"custom\"}\n{\"objectID\":\"\",\"type\":\"custom\"}\n", }, { name: "one dictionary with default stopwords", cli: "--all --include-defaults", - dictionaries: []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_STOPWORDS, + search.DICTIONARY_TYPE_PLURALS, + search.DICTIONARY_TYPE_COMPOUNDS, }, entries: true, isTTY: false, - wantOut: "{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n{\"Type\":\"custom\",\"ObjectID\":\"\",\"Language\":\"\"}\n", + wantOut: "{\"objectID\":\"\",\"type\":\"custom\"}\n{\"objectID\":\"\",\"type\":\"custom\"}\n{\"objectID\":\"\",\"type\":\"custom\"}\n", }, { name: "no entries", cli: "plurals", - dictionaries: []search.DictionaryName{ - search.Plurals, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, entries: false, isTTY: false, @@ -81,19 +81,24 @@ func Test_runBrowseCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} for _, d := range tt.dictionaries { - var entries []DictionaryEntry + var entries []search.DictionaryEntry if tt.entries { - entries = append(entries, DictionaryEntry{Type: "custom"}) + entries = append( + entries, + search.DictionaryEntry{ + Type: search.DICTIONARY_ENTRY_TYPE_CUSTOM.Ptr(), + }, + ) } r.Register( httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), - httpmock.JSONResponse(search.SearchDictionariesRes{ + httpmock.JSONResponse(search.SearchDictionaryEntriesResponse{ Hits: entries, }), ) r.Register( httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), - httpmock.JSONResponse(search.TaskStatusRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) } diff --git a/pkg/cmd/dictionary/shared/constants.go b/pkg/cmd/dictionary/shared/constants.go index d314b708..2684063a 100644 --- a/pkg/cmd/dictionary/shared/constants.go +++ b/pkg/cmd/dictionary/shared/constants.go @@ -1,39 +1,12 @@ package shared -import ( - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" -) +import "github.com/algolia/algoliasearch-client-go/v4/algolia/search" -// EntryType represents the type of an entry in a dictionary. -// It can be either a custom entry or a standard entry. -type ( - EntryType string - DictionaryType int -) - -// DictionaryEntry can be plural, compound or stopword entry. -type DictionaryEntry struct { - Type EntryType - Word string `json:"word,omitempty"` - Words []string `json:"words,omitempty"` - Decomposition []string `json:"decomposition,omitempty"` - ObjectID string - Language string - State string -} - -const ( - // CustomEntryType is the type of a custom entry in a dictionary (i.e. added by the user). - CustomEntryType EntryType = "custom" - // StandardEntryType is the type of a standard entry in a dictionary (i.e. added by Algolia). - StandardEntryType EntryType = "standard" -) - -// DictionaryNames returns the list of available dictionaries. -var DictionaryNames = func() []string { - return []string{ - string(search.Stopwords), - string(search.Compounds), - string(search.Plurals), +// DictionaryTypes returns the allowed dictionary types as strings +func DictionaryTypes() []string { + var types []string + for _, d := range search.AllowedDictionaryTypeEnumValues { + types = append(types, string(d)) } + return types } From 8739b985cacf8eb64bb9bab1da322c3662a96e34 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 17:38:49 +0100 Subject: [PATCH 37/45] feat: migrate dictionary entries clear --- pkg/cmd/dictionary/entries/clear/clear.go | 50 +++++++------ .../dictionary/entries/clear/clear_test.go | 74 +++++++++---------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/pkg/cmd/dictionary/entries/clear/clear.go b/pkg/cmd/dictionary/entries/clear/clear.go index dd616a3c..5d06592d 100644 --- a/pkg/cmd/dictionary/entries/clear/clear.go +++ b/pkg/cmd/dictionary/entries/clear/clear.go @@ -5,8 +5,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/opt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmdutil" @@ -22,18 +21,14 @@ type ClearOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Dictionaries []search.DictionaryName + Dictionaries []search.DictionaryType All bool DoConfirm bool } -type DictionaryEntry struct { - Type shared.EntryType -} - // NewClearCmd creates and returns a clear command for dictionaries' entries. func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Command { var confirm bool @@ -42,14 +37,14 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm opts := &ClearOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "clear {... | --all} [--confirm]", Args: cobra.OnlyValidArgs, - ValidArgs: shared.DictionaryNames(), + ValidArgs: shared.DictionaryTypes(), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return shared.DictionaryNames(), cobra.ShellCompDirectiveNoFileComp + return shared.DictionaryTypes(), cobra.ShellCompDirectiveNoFileComp }, Annotations: map[string]string{ "acls": "settings,editSettings", @@ -76,15 +71,11 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm } if opts.All { - opts.Dictionaries = []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, - } + opts.Dictionaries = search.AllowedDictionaryTypeEnumValues } else { - opts.Dictionaries = make([]search.DictionaryName, len(args)) + opts.Dictionaries = make([]search.DictionaryType, len(args)) for i, dictionary := range args { - opts.Dictionaries[i] = search.DictionaryName(dictionary) + opts.Dictionaries[i] = search.DictionaryType(dictionary) } } @@ -161,8 +152,14 @@ func runClearCmd(opts *ClearOptions) error { } } - for _, dictionary := range dictionaries { - _, err = client.ClearDictionaryEntries(dictionary) + for _, dict := range dictionaries { + _, err = client.BatchDictionaryEntries( + client.NewApiBatchDictionaryEntriesRequest( + dict, + search.NewEmptyBatchDictionaryEntriesParams(). + SetClearExistingDictionaryEntries(true), + ), + ) if err != nil { return err } @@ -181,8 +178,13 @@ func runClearCmd(opts *ClearOptions) error { return nil } -func customEntriesNb(client *search.Client, dictionary search.DictionaryName) (int, error) { - res, err := client.SearchDictionaryEntries(dictionary, "", opt.HitsPerPage(1000)) +func customEntriesNb(client *search.APIClient, dictionary search.DictionaryType) (int, error) { + res, err := client.SearchDictionaryEntries( + client.NewApiSearchDictionaryEntriesRequest( + dictionary, + search.NewEmptySearchDictionaryEntriesParams().SetHitsPerPage(1000).SetQuery(""), + ), + ) if err != nil { return 0, err } @@ -194,7 +196,7 @@ func customEntriesNb(client *search.Client, dictionary search.DictionaryName) (i ) } - var entries []DictionaryEntry + var entries []search.DictionaryEntry err = json.Unmarshal(data, &entries) if err != nil { return 0, fmt.Errorf( @@ -205,7 +207,7 @@ func customEntriesNb(client *search.Client, dictionary search.DictionaryName) (i var customEntriesNb int for _, entry := range entries { - if entry.Type == shared.CustomEntryType { + if entry.Type != nil && *entry.Type == search.DICTIONARY_ENTRY_TYPE_CUSTOM { customEntriesNb++ } } diff --git a/pkg/cmd/dictionary/entries/clear/clear_test.go b/pkg/cmd/dictionary/entries/clear/clear_test.go index daf75c08..ff994ec5 100644 --- a/pkg/cmd/dictionary/entries/clear/clear_test.go +++ b/pkg/cmd/dictionary/entries/clear/clear_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewClearCmd(t *testing.T) { @@ -30,8 +30,8 @@ func TestNewClearCmd(t *testing.T) { wantsErr: true, wantsOpts: ClearOptions{ DoConfirm: true, - Dictionaries: []search.DictionaryName{ - search.Plurals, + Dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, }, }, @@ -42,8 +42,8 @@ func TestNewClearCmd(t *testing.T) { wantsErr: false, wantsOpts: ClearOptions{ DoConfirm: false, - Dictionaries: []search.DictionaryName{ - search.Plurals, + Dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, }, }, @@ -53,12 +53,8 @@ func TestNewClearCmd(t *testing.T) { tty: true, wantsErr: false, wantsOpts: ClearOptions{ - DoConfirm: true, - Dictionaries: []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, - }, + DoConfirm: true, + Dictionaries: search.AllowedDictionaryTypeEnumValues, }, }, { @@ -68,8 +64,8 @@ func TestNewClearCmd(t *testing.T) { wantsErr: true, wantsOpts: ClearOptions{ DoConfirm: false, - Dictionaries: []search.DictionaryName{ - search.Plurals, + Dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, }, }, @@ -126,7 +122,7 @@ func Test_runDeleteCmd(t *testing.T) { tests := []struct { name string cli string - dictionaries []search.DictionaryName + dictionaries []search.DictionaryType entries bool isTTY bool wantOut string @@ -134,8 +130,8 @@ func Test_runDeleteCmd(t *testing.T) { { name: "one dictionary", cli: "plurals --confirm", - dictionaries: []search.DictionaryName{ - search.Plurals, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, entries: true, isTTY: false, @@ -144,31 +140,27 @@ func Test_runDeleteCmd(t *testing.T) { { name: "multiple dictionaries", cli: "plurals compounds --confirm", - dictionaries: []search.DictionaryName{ - search.Plurals, - search.Compounds, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, + search.DICTIONARY_TYPE_COMPOUNDS, }, entries: true, isTTY: false, wantOut: "", }, { - name: "all dictionaries", - cli: "--all --confirm", - dictionaries: []search.DictionaryName{ - search.Stopwords, - search.Plurals, - search.Compounds, - }, - entries: true, - isTTY: false, - wantOut: "", + name: "all dictionaries", + cli: "--all --confirm", + dictionaries: search.AllowedDictionaryTypeEnumValues, + entries: true, + isTTY: false, + wantOut: "", }, { name: "no entries", cli: "plurals --confirm", - dictionaries: []search.DictionaryName{ - search.Plurals, + dictionaries: []search.DictionaryType{ + search.DICTIONARY_TYPE_PLURALS, }, entries: false, isTTY: false, @@ -180,19 +172,27 @@ func Test_runDeleteCmd(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := httpmock.Registry{} for _, d := range tt.dictionaries { - var entries []DictionaryEntry + var entries []search.DictionaryEntry if tt.entries { - entries = append(entries, DictionaryEntry{Type: "custom"}) + entries = append( + entries, + search.DictionaryEntry{ + ObjectID: "1", + Language: search.SUPPORTED_LANGUAGE_EN.Ptr(), + Words: []string{"foo", "bar"}, + Type: search.DICTIONARY_ENTRY_TYPE_CUSTOM.Ptr(), + }, + ) } r.Register( httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/search", d)), - httpmock.JSONResponse(search.SearchDictionariesRes{ + httpmock.JSONResponse(search.SearchDictionaryEntriesResponse{ Hits: entries, }), ) r.Register( httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", d)), - httpmock.JSONResponse(search.TaskStatusRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) } From 163084eed649587630175979af3d144f9ab898c7 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 18:05:39 +0100 Subject: [PATCH 38/45] feat: migrate dictionary entries delete --- pkg/cmd/dictionary/entries/delete/delete.go | 40 +++++++++++---- .../dictionary/entries/delete/delete_test.go | 50 +++++++++---------- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/pkg/cmd/dictionary/entries/delete/delete.go b/pkg/cmd/dictionary/entries/delete/delete.go index ce19c79c..7d0019d5 100644 --- a/pkg/cmd/dictionary/entries/delete/delete.go +++ b/pkg/cmd/dictionary/entries/delete/delete.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/dictionary/shared" @@ -19,10 +19,10 @@ type DeleteOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - Dictionary search.DictionaryName - ObjectIDs []string + DictionaryType search.DictionaryType + ObjectIDs []string DoConfirm bool } @@ -34,14 +34,14 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "delete --object-ids [--confirm]", Args: validators.ExactArgs(1), - ValidArgs: shared.DictionaryNames(), + ValidArgs: shared.DictionaryTypes(), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return shared.DictionaryNames(), cobra.ShellCompDirectiveNoFileComp + return shared.DictionaryTypes(), cobra.ShellCompDirectiveNoFileComp }, Annotations: map[string]string{ "acls": "settings,editSettings", @@ -59,7 +59,11 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co `), RunE: func(cmd *cobra.Command, args []string) error { - opts.Dictionary = search.DictionaryName(args[0]) + d, err := search.NewDictionaryTypeFromValue(args[0]) + if err != nil { + return err + } + opts.DictionaryType = *d if !confirm { if !opts.IO.CanPrompt() { return cmdutil.FlagErrorf( @@ -98,7 +102,7 @@ func runDeleteCmd(opts *DeleteOptions) error { fmt.Sprintf( "Delete the %s from %s?", pluralizeEntry(len(opts.ObjectIDs)), - opts.Dictionary, + opts.DictionaryType, ), &confirmed, ) @@ -110,7 +114,21 @@ func runDeleteCmd(opts *DeleteOptions) error { } } - _, err = client.DeleteDictionaryEntries(opts.Dictionary, opts.ObjectIDs) + var requests []search.BatchDictionaryEntriesRequest + for _, id := range opts.ObjectIDs { + req := search.NewBatchDictionaryEntriesRequest( + search.DICTIONARY_ACTION_DELETE_ENTRY, + *search.NewDictionaryEntry(id), + ) + requests = append(requests, *req) + } + + _, err = client.BatchDictionaryEntries( + client.NewApiBatchDictionaryEntriesRequest( + opts.DictionaryType, + search.NewBatchDictionaryEntriesParams(requests), + ), + ) if err != nil { return err } @@ -122,7 +140,7 @@ func runDeleteCmd(opts *DeleteOptions) error { "%s Successfully deleted %s from %s\n", cs.SuccessIcon(), pluralizeEntry(len(opts.ObjectIDs)), - opts.Dictionary, + opts.DictionaryType, ) } diff --git a/pkg/cmd/dictionary/entries/delete/delete_test.go b/pkg/cmd/dictionary/entries/delete/delete_test.go index e7c55f8a..9f41fa1d 100644 --- a/pkg/cmd/dictionary/entries/delete/delete_test.go +++ b/pkg/cmd/dictionary/entries/delete/delete_test.go @@ -4,15 +4,15 @@ import ( "fmt" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/google/shlex" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/pkg/httpmock/v4" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test" + "github.com/algolia/cli/test/v4" ) func TestNewDeleteCmd(t *testing.T) { @@ -25,18 +25,18 @@ func TestNewDeleteCmd(t *testing.T) { }{ { name: "no --confirm without tty", - cli: "plural --object-ids 1", + cli: "plurals --object-ids 1", tty: false, wantsErr: true, }, { name: "--confirm without tty", - cli: "plural --object-ids 1 --confirm", + cli: "plurals --object-ids 1 --confirm", tty: true, wantsErr: false, wantsOpts: DeleteOptions{ - DoConfirm: false, - Dictionary: "plural", + DoConfirm: false, + DictionaryType: search.DICTIONARY_TYPE_PLURALS, ObjectIDs: []string{ "1", }, @@ -44,12 +44,12 @@ func TestNewDeleteCmd(t *testing.T) { }, { name: "no --confirm with tty", - cli: "plural --object-ids 1", + cli: "plurals --object-ids 1", tty: true, wantsErr: false, wantsOpts: DeleteOptions{ - DoConfirm: true, - Dictionary: "plural", + DoConfirm: true, + DictionaryType: search.DICTIONARY_TYPE_PLURALS, ObjectIDs: []string{ "1", }, @@ -57,12 +57,12 @@ func TestNewDeleteCmd(t *testing.T) { }, { name: "multiple --object-ids", - cli: "plural --object-ids 1,2 --confirm", + cli: "plurals --object-ids 1,2 --confirm", tty: false, wantsErr: false, wantsOpts: DeleteOptions{ - DoConfirm: false, - Dictionary: "plural", + DoConfirm: false, + DictionaryType: search.DICTIONARY_TYPE_PLURALS, ObjectIDs: []string{ "1", "2", @@ -103,7 +103,7 @@ func TestNewDeleteCmd(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, "", stderr.String()) - assert.Equal(t, tt.wantsOpts.Dictionary, opts.Dictionary) + assert.Equal(t, tt.wantsOpts.DictionaryType, opts.DictionaryType) assert.Equal(t, tt.wantsOpts.ObjectIDs, opts.ObjectIDs) assert.Equal(t, tt.wantsOpts.DoConfirm, opts.DoConfirm) }) @@ -114,15 +114,15 @@ func Test_runDeleteCmd(t *testing.T) { tests := []struct { name string cli string - dictionary string + dictionary search.DictionaryType objectIDs []string isTTY bool wantOut string }{ { name: "single object-id, no TTY", - cli: "plural --object-ids 1 --confirm", - dictionary: "plural", + cli: "plurals --object-ids 1 --confirm", + dictionary: search.DICTIONARY_TYPE_PLURALS, objectIDs: []string{ "1", }, @@ -131,24 +131,24 @@ func Test_runDeleteCmd(t *testing.T) { }, { name: "single object-id, TTY", - cli: "plural --object-ids 1 --confirm", - dictionary: "plural", + cli: "plurals --object-ids 1 --confirm", + dictionary: search.DICTIONARY_TYPE_PLURALS, objectIDs: []string{ "1", }, isTTY: true, - wantOut: "✓ Successfully deleted 1 entry from plural\n", + wantOut: "✓ Successfully deleted 1 entry from plurals\n", }, { name: "multiple object-ids, TTY", - cli: "plural --object-ids 1,2 --confirm", - dictionary: "plural", + cli: "plurals --object-ids 1,2 --confirm", + dictionary: search.DICTIONARY_TYPE_PLURALS, objectIDs: []string{ "1", "2", }, isTTY: true, - wantOut: "✓ Successfully deleted 2 entries from plural\n", + wantOut: "✓ Successfully deleted 2 entries from plurals\n", }, } @@ -163,12 +163,12 @@ func Test_runDeleteCmd(t *testing.T) { "GET", fmt.Sprintf("1/dictionaries/%s/search?query=%s", tt.dictionary, id), ), - httpmock.JSONResponse(search.SearchDictionariesRes{}), + httpmock.JSONResponse(search.SearchDictionaryEntriesResponse{}), ) } r.Register( httpmock.REST("POST", fmt.Sprintf("1/dictionaries/%s/batch", tt.dictionary)), - httpmock.JSONResponse(search.TaskStatusRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) f, out := test.NewFactory(tt.isTTY, &r, nil, "") From ebc77afa73c9abedcde2c32f0c78b31edffe4ee3 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 20:43:07 +0100 Subject: [PATCH 39/45] feat: migrate dictionary entries import --- pkg/cmd/dictionary/entries/clear/clear.go | 12 +- pkg/cmd/dictionary/entries/import/import.go | 118 +++++++++++------- .../dictionary/entries/import/import_test.go | 76 ++--------- 3 files changed, 87 insertions(+), 119 deletions(-) diff --git a/pkg/cmd/dictionary/entries/clear/clear.go b/pkg/cmd/dictionary/entries/clear/clear.go index 5d06592d..93ae57d0 100644 --- a/pkg/cmd/dictionary/entries/clear/clear.go +++ b/pkg/cmd/dictionary/entries/clear/clear.go @@ -153,12 +153,12 @@ func runClearCmd(opts *ClearOptions) error { } for _, dict := range dictionaries { - _, err = client.BatchDictionaryEntries( - client.NewApiBatchDictionaryEntriesRequest( - dict, - search.NewEmptyBatchDictionaryEntriesParams(). - SetClearExistingDictionaryEntries(true), - ), + batchParams := search.NewBatchDictionaryEntriesParams( + []search.BatchDictionaryEntriesRequest{}, + search.WithBatchDictionaryEntriesParamsClearExistingDictionaryEntries(true), + ) + _, err := client.BatchDictionaryEntries( + client.NewApiBatchDictionaryEntriesRequest(dict, batchParams), ) if err != nil { return err diff --git a/pkg/cmd/dictionary/entries/import/import.go b/pkg/cmd/dictionary/entries/import/import.go index 58b98fab..0f58d63a 100644 --- a/pkg/cmd/dictionary/entries/import/import.go +++ b/pkg/cmd/dictionary/entries/import/import.go @@ -8,7 +8,7 @@ import ( "time" "github.com/MakeNowJust/heredoc" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/spf13/cobra" "github.com/algolia/cli/pkg/cmd/dictionary/shared" @@ -26,11 +26,10 @@ type ImportOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) + SearchClient func() (*search.APIClient, error) - DictionaryName string - CreateIfNotExists bool - Wait bool + DictionaryType search.DictionaryType + Wait bool File string Scanner *bufio.Scanner @@ -43,13 +42,13 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.SearchClient, + SearchClient: f.V4SearchClient, } cmd := &cobra.Command{ Use: "import -F [--wait] [--continue-on-errors]", Args: validators.ExactArgs(1), - ValidArgs: shared.DictionaryNames(), + ValidArgs: shared.DictionaryTypes(), Annotations: map[string]string{ "acls": "settings,editSettings", }, @@ -67,7 +66,11 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co $ algolia dictionary import plurals -F entries.ndjson --continue-on-errors `), RunE: func(cmd *cobra.Command, args []string) error { - opts.DictionaryName = args[0] + d, err := search.NewDictionaryTypeFromValue(args[0]) + if err != nil { + return err + } + opts.DictionaryType = *d scanner, err := cmdutil.ScanFile(opts.File, opts.IO.In) if err != nil { @@ -104,7 +107,7 @@ func runImportCmd(opts *ImportOptions) error { cs := opts.IO.ColorScheme() var ( - entries []search.DictionaryEntry + entries []*search.DictionaryEntry currentLine = 0 totalEntries = 0 ) @@ -124,19 +127,17 @@ func runImportCmd(opts *ImportOptions) error { totalEntries++ opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Read entries from %s", opts.File)) - var entry shared.DictionaryEntry + var entry search.DictionaryEntry + if err := json.Unmarshal([]byte(line), &entry); err != nil { - err := fmt.Errorf("line %d: %s", currentLine, err) - errors = append(errors, err.Error()) - continue - } - err := ValidateDictionaryEntry(entry, currentLine) - if err != nil { - errors = append(errors, err.Error()) + errors = append(errors, fmt.Errorf("line %d: %s", currentLine, err).Error()) continue } - dictionaryEntry, err := createDictionaryEntry(opts.DictionaryName, entry) + fmt.Printf("TYPE: %v\n", opts.DictionaryType) + fmt.Printf("ENTRY: %v\n", entry) + + dictionaryEntry, err := createDictionaryEntry(opts.DictionaryType, entry) if err != nil { errors = append(errors, fmt.Errorf("line %d: %s", currentLine, err.Error()).Error()) continue @@ -184,11 +185,23 @@ func runImportCmd(opts *ImportOptions) error { fmt.Sprintf( "Updating %s entries on %s", cs.Bold(fmt.Sprint(len(entries))), - cs.Bold(opts.DictionaryName), + cs.Bold(string(opts.DictionaryType)), ), ) - res, err := client.SaveDictionaryEntries(search.DictionaryName(opts.DictionaryName), entries) + var requests []search.BatchDictionaryEntriesRequest + for _, e := range entries { + requests = append( + requests, + *search.NewBatchDictionaryEntriesRequest(search.DICTIONARY_ACTION_ADD_ENTRY, *e), + ) + } + res, err := client.BatchDictionaryEntries( + client.NewApiBatchDictionaryEntriesRequest( + opts.DictionaryType, + search.NewBatchDictionaryEntriesParams(requests), + ), + ) if err != nil { opts.IO.StopProgressIndicator() return err @@ -197,7 +210,7 @@ func runImportCmd(opts *ImportOptions) error { // Wait for the operation to complete if requested if opts.Wait { opts.IO.UpdateProgressIndicatorLabel("Waiting for operation to complete") - if err := res.Wait(); err != nil { + if _, err := client.WaitForAppTask(res.TaskID); err != nil { opts.IO.StopProgressIndicator() return err } @@ -209,42 +222,51 @@ func runImportCmd(opts *ImportOptions) error { "%s Successfully imported %s entries on %s in %v\n", cs.SuccessIcon(), cs.Bold(fmt.Sprint(len(entries))), - cs.Bold(opts.DictionaryName), + cs.Bold(string(opts.DictionaryType)), time.Since(elapsed), ) return err } -func ValidateDictionaryEntry(entry shared.DictionaryEntry, currentLine int) error { +func createDictionaryEntry( + dictionaryType search.DictionaryType, + entry search.DictionaryEntry, +) (*search.DictionaryEntry, error) { if entry.ObjectID == "" { - return fmt.Errorf("line %d: objectID is missing", currentLine) - } - if entry.Word == "" { - return fmt.Errorf("line %d: word is missing", currentLine) + return nil, fmt.Errorf("objectID is missing") } - if entry.Language == "" { - return fmt.Errorf("line %d: language is missing", currentLine) - } - - return nil -} - -func createDictionaryEntry( - dictionaryName string, - entry shared.DictionaryEntry, -) (search.DictionaryEntry, error) { - switch dictionaryName { - case string(search.Plurals): - return search.NewPlural(entry.ObjectID, entry.Language, entry.Words), nil - case string(search.Compounds): - return search.NewCompound( + switch dictionaryType { + case search.DICTIONARY_TYPE_PLURALS: + if len(entry.Words) == 0 { + return nil, fmt.Errorf("words is missing") + } + if entry.Language == nil { + return nil, fmt.Errorf("language is missing.") + } + return search.NewDictionaryEntry( + entry.ObjectID, + search.WithDictionaryEntryLanguage(*entry.Language), + search.WithDictionaryEntryWords(entry.Words), + ), nil + case search.DICTIONARY_TYPE_COMPOUNDS: + if entry.Word == nil { + return nil, fmt.Errorf("word is missing") + } + return search.NewDictionaryEntry( + entry.ObjectID, + search.WithDictionaryEntryLanguage(*entry.Language), + search.WithDictionaryEntryWord(*entry.Word), + search.WithDictionaryEntryDecomposition(entry.Decomposition), + ), nil + case search.DICTIONARY_TYPE_STOPWORDS: + if entry.Word == nil { + return nil, fmt.Errorf("word is missing") + } + return search.NewDictionaryEntry( entry.ObjectID, - entry.Language, - entry.Word, - entry.Decomposition, + search.WithDictionaryEntryLanguage(*entry.Language), + search.WithDictionaryEntryWord(*entry.Word), ), nil - case string(search.Stopwords): - return search.NewStopword(entry.ObjectID, entry.Language, entry.Word, entry.State), nil } return nil, fmt.Errorf("Wrong dictionary name") diff --git a/pkg/cmd/dictionary/entries/import/import_test.go b/pkg/cmd/dictionary/entries/import/import_test.go index f1a4f156..b8c0dd3c 100644 --- a/pkg/cmd/dictionary/entries/import/import_test.go +++ b/pkg/cmd/dictionary/entries/import/import_test.go @@ -6,13 +6,12 @@ import ( "path/filepath" "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/cmd/dictionary/shared" - "github.com/algolia/cli/pkg/httpmock" - "github.com/algolia/cli/test" + "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/test/v4" ) func Test_runImportCmd(t *testing.T) { @@ -20,7 +19,7 @@ func Test_runImportCmd(t *testing.T) { err := os.WriteFile( tmpFile, []byte( - `{"language":"en","word":"test","state":"enabled","objectID":"test","type":"custom"}`, + `{"language":"en","word":"test","objectID":"test"}`, ), 0o600, ) @@ -36,7 +35,7 @@ func Test_runImportCmd(t *testing.T) { { name: "from stdin", cli: "stopwords -F -", - stdin: `{"language":"en","word":"test","state":"enabled","objectID":"test","type":"custom"}`, + stdin: `{"language":"en","word":"test","objectID":"test"}`, wantOut: "✓ Successfully imported 1 entries on stopwords in", }, { @@ -47,14 +46,14 @@ func Test_runImportCmd(t *testing.T) { { name: "from stdin with invalid JSON", cli: "stopwords -F -", - stdin: `{"language":"en","word":"test","state":"enabled","type":"custom"}`, + stdin: `{"language":"en","word":"test"}`, wantErr: "X Found 1 error (out of 1 entries) while parsing the file:\n line 1: objectID is missing\n", }, { name: "from stdin with invalid JSON (multiple operations)", cli: "stopwords -F -", - stdin: `{"word":"test","state":"enabled","objectID":"test","type":"custom"}, - {"language":"fr","state":"enabled","objectID":"testFr","type":"custom"}`, + stdin: `{"word":"test","objectID":"test"}, + {"language":"fr","objectID":"testFr"}`, wantErr: "X Found 2 errors (out of 2 entries) while parsing the file:\n line 1: invalid character ',' after top-level value\n line 2: word is missing\n", }, { @@ -66,8 +65,8 @@ func Test_runImportCmd(t *testing.T) { { name: "from stdin with invalid JSON (2 entries) with --continue-on-error", cli: "stopwords -F - --continue-on-error", - stdin: `{"language":"en","state":"enabled","objectID":"test","type":"custom"} - {"language":"en","word":"test","state":"enabled","type":"custom"}`, + stdin: `{"language":"en","objectID":"test"} + {"language":"en","word":"test"}`, wantErr: "X Found 2 errors (out of 2 entries) while parsing the file:\n line 1: word is missing\n line 2: objectID is missing\n", }, { @@ -88,7 +87,7 @@ func Test_runImportCmd(t *testing.T) { if tt.wantErr == "" { r.Register( httpmock.REST("POST", "1/dictionaries/stopwords/batch"), - httpmock.JSONResponse(search.MultipleBatchRes{}), + httpmock.JSONResponse(search.UpdatedAtResponse{}), ) } defer r.Verify(t) @@ -105,56 +104,3 @@ func Test_runImportCmd(t *testing.T) { }) } } - -func Test_ValidateDictionaryEntry(t *testing.T) { - tests := []struct { - name string - entry shared.DictionaryEntry - currentLine int - wantErr bool - wantErrMsg string - }{ - { - name: "no objectID", - entry: shared.DictionaryEntry{ - Word: "test", - Language: "en", - }, - currentLine: 1, - wantErr: true, - wantErrMsg: "line 1: objectID is missing", - }, - { - name: "no word", - entry: shared.DictionaryEntry{ - ObjectID: "123", - Language: "en", - }, - currentLine: 1, - wantErr: true, - wantErrMsg: "line 1: word is missing", - }, - { - name: "no language", - entry: shared.DictionaryEntry{ - ObjectID: "123", - Word: "test", - }, - currentLine: 1, - wantErr: true, - wantErrMsg: "line 1: language is missing", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ValidateDictionaryEntry(tt.entry, tt.currentLine) - if tt.wantErr { - assert.Error(t, err) - assert.Equal(t, tt.wantErrMsg, err.Error()) - return - } - assert.NoError(t, err) - }) - } -} From 3217b0f6ab4b63950500edec4f0bbff705424426 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 4 Feb 2025 12:18:40 +0100 Subject: [PATCH 40/45] feat: remove art command Shows an outdated logo and has fulfilled it's gimmicky purpose. --- pkg/cmd/art/art.go | 60 -------------------------------------------- pkg/cmd/root/root.go | 10 +++----- 2 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 pkg/cmd/art/art.go diff --git a/pkg/cmd/art/art.go b/pkg/cmd/art/art.go deleted file mode 100644 index b27270bd..00000000 --- a/pkg/cmd/art/art.go +++ /dev/null @@ -1,60 +0,0 @@ -package art - -import ( - "fmt" - "time" - - "github.com/algolia/cli/pkg/auth" - "github.com/algolia/cli/pkg/cmdutil" - "github.com/spf13/cobra" -) - -var art = ` - ________________________ - / \ - | _____ | - | _. XXXXX | - | X/ .<&%%Y%%&>. | - | .%#/ |##+\#%. | - | %#` + "`" + ` |#/ ` + "`" + `#% | - | %# + #% | - | ` + "`" + `%\ /%` + "`" + ` | - | \#%b.___.d%#/ | - | ` + "`" + `` + "`" + `+===+` + "`" + `` + "`" + ` | - | | - \ ________________________ / - - Congratulations on your epic search! - You found the ☼art☼! -` - -func NewArtCmd(f *cmdutil.Factory) *cobra.Command { - loadingMessages := []string{ - "The legends speak of an Algolia developer so strong...", - "so capable...", - "so powerful at search and discovery...", - "that they could find the hidden art at the root of all things...", - "So spake the legends...", - } - - cmd := &cobra.Command{ - Use: "art", - Short: "We've been searching for the art for so long...!", - Run: func(cmd *cobra.Command, args []string) { - io := f.IOStreams - io.StartProgressIndicatorWithLabel("LEGENDARY QUEST ACCEPTED") - time.Sleep(2 * time.Second) - for i := 0; i < len(loadingMessages); i++ { - io.UpdateProgressIndicatorLabel(loadingMessages[i]) - time.Sleep(2 * time.Second) - } - io.StopProgressIndicator() - fmt.Println(art) - }, - Hidden: true, - } - - auth.DisableAuthCheck(cmd) - - return cmd -} diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 4a924251..c0b66ee2 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -22,7 +22,6 @@ import ( "github.com/algolia/cli/internal/update" "github.com/algolia/cli/pkg/auth" "github.com/algolia/cli/pkg/cmd/apikeys" - "github.com/algolia/cli/pkg/cmd/art" "github.com/algolia/cli/pkg/cmd/crawler" "github.com/algolia/cli/pkg/cmd/dictionary" "github.com/algolia/cli/pkg/cmd/events" @@ -111,9 +110,6 @@ func NewRootCmd(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(events.NewEventsCmd(f)) cmd.AddCommand(crawler.NewCrawlersCmd(f)) - // ??? related commands - cmd.AddCommand(art.NewArtCmd(f)) - return cmd } @@ -306,8 +302,8 @@ func checkForUpdate(cfg config.Config, currentVersion string) (*update.ReleaseIn return update.CheckForUpdate(&client, stateFilePath, currentVersion) } -// Check whether the gh binary was found under the Homebrew prefix -func isUnderHomebrew(ghBinary string) bool { +// Check whether the CLI was found under the Homebrew prefix +func isUnderHomebrew(cli string) bool { brewExe, err := safeexec.LookPath("brew") if err != nil { return false @@ -324,5 +320,5 @@ func isUnderHomebrew(ghBinary string) bool { ) + string( filepath.Separator, ) - return strings.HasPrefix(ghBinary, brewBinPrefix) + return strings.HasPrefix(cli, brewBinPrefix) } From 15f9bd36aa296b2a73f61d0dbc2ee46f4cda3677 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 5 Feb 2025 14:39:51 +0100 Subject: [PATCH 41/45] feat: migrate events tail command --- api/insights/client.go | 114 ++++++++++++++++++++---------------- api/insights/types.go | 1 + api/insights/utils.go | 22 ------- pkg/cmd/events/tail/tail.go | 36 +++++------- 4 files changed, 77 insertions(+), 96 deletions(-) delete mode 100644 api/insights/utils.go diff --git a/api/insights/client.go b/api/insights/client.go index 96bffeed..b767ce1e 100644 --- a/api/insights/client.go +++ b/api/insights/client.go @@ -1,71 +1,81 @@ package insights import ( + "encoding/json" "fmt" - "net/http" "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/call" - "github.com/algolia/algoliasearch-client-go/v3/algolia/compression" - _insights "github.com/algolia/algoliasearch-client-go/v3/algolia/insights" - "github.com/algolia/algoliasearch-client-go/v3/algolia/transport" + "github.com/algolia/algoliasearch-client-go/v4/algolia/insights" + algoliaInsights "github.com/algolia/algoliasearch-client-go/v4/algolia/insights" + "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" + "github.com/algolia/cli/pkg/version" ) -// Client provides methods to interact with the Algolia Insights API. +// Client wraps the default Insights API client so that we can declare methods on it type Client struct { - appID string - transport *transport.Transport + *algoliaInsights.APIClient } -// NewClient instantiates a new client able to interact with the Algolia -// Insights API. -func NewClient(appID, apiKey string) *Client { - return NewClientWithConfig( - _insights.Configuration{ - AppID: appID, - APIKey: apiKey, +// NewClient instantiates a new Insights API client +func NewClient(appID, apiKey string, region algoliaInsights.Region) (*Client, error) { + // Get the default user agent + userAgent, err := getUserAgentInfo(appID, apiKey, region, version.Version) + if err != nil { + return nil, err + } + if userAgent == "" { + return nil, fmt.Errorf("User agent info must not be empty") + } + clientConfig := insights.InsightsConfiguration{ + Configuration: transport.Configuration{ + AppID: appID, + ApiKey: apiKey, + UserAgent: userAgent, }, - ) + } + client, err := algoliaInsights.NewClientWithConfig(clientConfig) + if err != nil { + return nil, err + } + return &Client{client}, nil } -// NewClientWithConfig instantiates a new client able to interact with the -// Algolia Insights API. -func NewClientWithConfig(config _insights.Configuration) *Client { - var hosts []*transport.StatefulHost - - if config.Hosts == nil { - hosts = defaultHosts(config.Region) - } else { - for _, h := range config.Hosts { - hosts = append(hosts, transport.NewStatefulHost(h, call.IsReadWrite)) - } +// GetEvents retrieves a number of events from the Algolia Insights API. +func (c *Client) GetEvents(startDate, endDate time.Time, limit int) (*EventsRes, error) { + layout := "2006-01-02T15:04:05.000Z" + params := map[string]any{ + "startDate": startDate.Format(layout), + "endDate": endDate.Format(layout), + "limit": limit, } - - return &Client{ - appID: config.AppID, - transport: transport.New( - hosts, - config.Requester, - config.AppID, - config.APIKey, - config.ReadTimeout, - config.WriteTimeout, - config.Headers, - config.ExtraUserAgent, - compression.None, - ), + res, err := c.CustomGet(c.NewApiCustomGetRequest("1/events").WithParameters(params)) + if err != nil { + return nil, err } + tmp, err := json.Marshal(res) + if err != nil { + return nil, err + } + var eventsRes EventsRes + err = json.Unmarshal(tmp, &eventsRes) + if err != nil { + return nil, err + } + + return &eventsRes, err } -// FetchEvents retrieves events from the Algolia Insights API. -func (c *Client) FetchEvents(startDate, endDate time.Time, limit int) (EventsRes, error) { - var res EventsRes - path := fmt.Sprintf( - "/1/events?startDate=%s&endDate=%s&limit=%d", - startDate.Format("2006-01-02T15:04:05.000Z"), - endDate.Format("2006-01-02T15:04:05.000Z"), - limit, - ) - err := c.transport.Request(&res, http.MethodGet, path, nil, call.Read, nil) - return res, err +// getUserAgentInfo returns the user agent string for the Insights client in the CLI +func getUserAgentInfo( + appID string, + apiKey string, + region algoliaInsights.Region, + appVersion string, +) (string, error) { + client, err := algoliaInsights.NewClient(appID, apiKey, region) + if err != nil { + return "", err + } + + return client.GetConfiguration().UserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), nil } diff --git a/api/insights/types.go b/api/insights/types.go index 58b06120..0d65974a 100644 --- a/api/insights/types.go +++ b/api/insights/types.go @@ -26,6 +26,7 @@ func (t *Timestamp) UnmarshalJSON(data []byte) error { return nil } +// TODO: Replace this with a type from the API. type Event struct { EventType string `json:"eventType"` EventName string `json:"eventName"` diff --git a/api/insights/utils.go b/api/insights/utils.go deleted file mode 100644 index a02e3881..00000000 --- a/api/insights/utils.go +++ /dev/null @@ -1,22 +0,0 @@ -package insights - -import ( - "fmt" - - "github.com/algolia/algoliasearch-client-go/v3/algolia/call" - "github.com/algolia/algoliasearch-client-go/v3/algolia/region" - "github.com/algolia/algoliasearch-client-go/v3/algolia/transport" -) - -func defaultHosts(r region.Region) (hosts []*transport.StatefulHost) { - switch r { - case region.DE, region.US: - hosts = append( - hosts, - transport.NewStatefulHost(fmt.Sprintf("insights.%s.algolia.io", r), call.IsReadWrite), - ) - default: - hosts = append(hosts, transport.NewStatefulHost("insights.algolia.io", call.IsReadWrite)) - } - return -} diff --git a/pkg/cmd/events/tail/tail.go b/pkg/cmd/events/tail/tail.go index 5e00536f..64053ab0 100644 --- a/pkg/cmd/events/tail/tail.go +++ b/pkg/cmd/events/tail/tail.go @@ -9,21 +9,19 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" "github.com/algolia/cli/pkg/iostreams" "github.com/algolia/cli/pkg/printers" "github.com/algolia/cli/pkg/validators" - _insights "github.com/algolia/algoliasearch-client-go/v3/algolia/insights" - region "github.com/algolia/algoliasearch-client-go/v3/algolia/region" + algoliaInsights "github.com/algolia/algoliasearch-client-go/v4/algolia/insights" "github.com/algolia/cli/api/insights" ) const ( // DefaultRegion is the default region to use. - DefaultRegion = region.US + DefaultRegion = algoliaInsights.US // Interval is the interval between each request to fetch events. Interval = 3 * time.Second @@ -34,20 +32,16 @@ type TailOptions struct { Config config.IConfig IO *iostreams.IOStreams - SearchClient func() (*search.Client, error) - - Region string - + Region string PrintFlags *cmdutil.PrintFlags } // NewTailCmd returns a new command for tailing events. func NewTailCmd(f *cmdutil.Factory, runF func(*TailOptions) error) *cobra.Command { opts := &TailOptions{ - IO: f.IOStreams, - Config: f.Config, - SearchClient: f.SearchClient, - PrintFlags: cmdutil.NewPrintFlags(), + IO: f.IOStreams, + Config: f.Config, + PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ Use: "tail", @@ -84,8 +78,8 @@ func NewTailCmd(f *cmdutil.Factory, runF func(*TailOptions) error) *cobra.Comman cmd.Flags(). StringVarP(&opts.Region, "region", "r", string(DefaultRegion), "Region where your analytics data is stored and processed.") _ = cmd.RegisterFlagCompletionFunc("region", cmdutil.StringCompletionFunc(map[string]string{ - string(region.US): "United States", - string(region.DE): "Germany (Europe)", + string(algoliaInsights.US): "United States", + string(algoliaInsights.DE): "Germany (Europe)", })) opts.PrintFlags.AddFlags(cmd) @@ -98,18 +92,16 @@ func runTailCmd(opts *TailOptions) error { if err != nil { return err } - apiKey, err := opts.Config.Profile().GetAdminAPIKey() + apiKey, err := opts.Config.Profile().GetAPIKey() if err != nil { return err } - // We don't use the base insights client because it doesn't support fetching events. - config := _insights.Configuration{ - AppID: appID, - APIKey: apiKey, - Region: region.Region(opts.Region), + // This is the CLIs custom augmented API client + client, err := insights.NewClient(appID, apiKey, algoliaInsights.Region(opts.Region)) + if err != nil { + return err } - insightsClient := insights.NewClientWithConfig(config) var p printers.Printer if opts.PrintFlags.OutputFlagSpecified() && opts.PrintFlags.OutputFormat != nil { @@ -124,7 +116,7 @@ func runTailCmd(opts *TailOptions) error { c := time.Tick(Interval) for t := range c { utc := t.UTC() - events, err := insightsClient.FetchEvents(utc.Add(-1*time.Second), utc, 1000) + events, err := client.GetEvents(utc.Add(-1*time.Second), utc, 1000) if err != nil { if strings.Contains(err.Error(), "The log processing region does not match") { cs := opts.IO.ColorScheme() From 3782391014485aefa2e722f9b6b9c54ec8f23d84 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 5 Feb 2025 14:48:23 +0100 Subject: [PATCH 42/45] feat: update type in Crawler API to v4 --- api/crawler/types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/crawler/types.go b/api/crawler/types.go index 415b65c1..a55d1e60 100644 --- a/api/crawler/types.go +++ b/api/crawler/types.go @@ -3,7 +3,7 @@ package crawler import ( "time" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" ) // ErrResponse is a Crawler API error response. @@ -67,8 +67,8 @@ type Config struct { IgnoreNoFollowTo bool `json:"ignoreNoFollowTo,omitempty"` IgnoreCanonicalTo bool `json:"ignoreCanonicalTo,omitempty"` - SaveBackup bool `json:"saveBackup,omitempty"` - InitialIndexSettings map[string]*search.Settings `json:"initialIndexSettings,omitempty"` + SaveBackup bool `json:"saveBackup,omitempty"` + InitialIndexSettings map[string]*search.IndexSettings `json:"initialIndexSettings,omitempty"` Actions []*Action `json:"actions,omitempty"` } From 6ae5150c050416adc807277bbf56bb53f910a77a Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Wed, 5 Feb 2025 16:02:07 +0100 Subject: [PATCH 43/45] fix: remove v3 client from cli --- go.mod | 3 +- go.sum | 3 - pkg/auth/auth_check.go | 15 ++- pkg/auth/auth_check_test.go | 18 +-- pkg/cmd/apikeys/create/create.go | 6 +- pkg/cmd/apikeys/create/create_test.go | 4 +- pkg/cmd/apikeys/delete/delete.go | 2 +- pkg/cmd/apikeys/delete/delete_test.go | 4 +- pkg/cmd/apikeys/get/get.go | 2 +- pkg/cmd/apikeys/get/get_test.go | 4 +- pkg/cmd/apikeys/list/list.go | 2 +- pkg/cmd/apikeys/list/list_test.go | 4 +- pkg/cmd/dictionary/entries/browse/browse.go | 2 +- .../dictionary/entries/browse/browse_test.go | 4 +- pkg/cmd/dictionary/entries/clear/clear.go | 2 +- .../dictionary/entries/clear/clear_test.go | 4 +- pkg/cmd/dictionary/entries/delete/delete.go | 2 +- .../dictionary/entries/delete/delete_test.go | 4 +- pkg/cmd/dictionary/entries/import/import.go | 2 +- .../dictionary/entries/import/import_test.go | 4 +- pkg/cmd/dictionary/settings/get/get.go | 2 +- pkg/cmd/dictionary/settings/set/set.go | 2 +- pkg/cmd/factory/default.go | 20 +-- pkg/cmd/indices/analyze/analyze.go | 4 +- pkg/cmd/indices/clear/clear.go | 4 +- pkg/cmd/indices/clear/clear_test.go | 4 +- pkg/cmd/indices/config/export/export.go | 4 +- pkg/cmd/indices/config/import/import.go | 4 +- pkg/cmd/indices/copy/copy.go | 4 +- pkg/cmd/indices/copy/copy_test.go | 4 +- pkg/cmd/indices/delete/delete.go | 4 +- pkg/cmd/indices/delete/delete_test.go | 4 +- pkg/cmd/indices/list/list.go | 2 +- pkg/cmd/indices/move/move.go | 4 +- pkg/cmd/indices/move/move_test.go | 4 +- pkg/cmd/objects/browse/browse.go | 4 +- pkg/cmd/objects/browse/browse_test.go | 4 +- pkg/cmd/objects/delete/delete.go | 4 +- pkg/cmd/objects/delete/delete_test.go | 4 +- pkg/cmd/objects/import/import_test.go | 4 +- pkg/cmd/objects/operations/operations.go | 2 +- pkg/cmd/objects/operations/operations_test.go | 4 +- pkg/cmd/objects/update/update.go | 4 +- pkg/cmd/objects/update/update_test.go | 4 +- pkg/cmd/rules/browse/browse.go | 4 +- pkg/cmd/rules/browse/browse_test.go | 4 +- pkg/cmd/rules/delete/delete_test.go | 4 +- pkg/cmd/rules/import/import.go | 4 +- pkg/cmd/rules/import/import_test.go | 4 +- pkg/cmd/search/search.go | 4 +- pkg/cmd/settings/get/list.go | 4 +- pkg/cmd/settings/import/import.go | 4 +- pkg/cmd/settings/import/import_test.go | 4 +- pkg/cmd/settings/set/set.go | 4 +- pkg/cmd/settings/set/set_test.go | 4 +- pkg/cmd/synonyms/browse/browse.go | 4 +- pkg/cmd/synonyms/browse/browse_test.go | 4 +- pkg/cmd/synonyms/delete/delete.go | 4 +- pkg/cmd/synonyms/delete/delete_test.go | 4 +- pkg/cmd/synonyms/import/import.go | 4 +- pkg/cmd/synonyms/import/import_test.go | 4 +- pkg/cmd/synonyms/save/save.go | 4 +- pkg/cmd/synonyms/save/save_test.go | 4 +- pkg/cmdutil/factory.go | 12 +- pkg/cmdutil/valid_args.go | 27 +--- pkg/httpmock/registry.go | 7 +- pkg/httpmock/v4/registry.go | 101 -------------- pkg/httpmock/v4/stub.go | 65 --------- test/helpers.go | 16 ++- test/v4/config.go | 89 ------------- test/v4/helpers.go | 124 ------------------ 71 files changed, 157 insertions(+), 555 deletions(-) delete mode 100644 pkg/httpmock/v4/registry.go delete mode 100644 pkg/httpmock/v4/stub.go delete mode 100644 test/v4/config.go delete mode 100644 test/v4/helpers.go diff --git a/go.mod b/go.mod index 195ca37f..35b05c23 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/BurntSushi/toml v1.4.0 github.com/MakeNowJust/heredoc v1.0.0 - github.com/algolia/algoliasearch-client-go/v3 v3.31.4 github.com/algolia/algoliasearch-client-go/v4 v4.12.0 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/briandowns/spinner v1.23.2 @@ -21,7 +20,6 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.2 github.com/sirupsen/logrus v1.9.3 @@ -59,6 +57,7 @@ require ( github.com/magiconair/properties v1.8.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/go.sum b/go.sum index fb53b635..61ae8d33 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,6 @@ github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/algolia/algoliasearch-client-go/v3 v3.31.4 h1:UJhx6AhZCYf0qZygDz2c1x1+1q2q2sfzsRaQM6yswWk= -github.com/algolia/algoliasearch-client-go/v3 v3.31.4/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= github.com/algolia/algoliasearch-client-go/v4 v4.12.0 h1:YbLMyYZ7ohBTCEBIl3frF2Ga92ulGFev1tGe8SopF7Y= github.com/algolia/algoliasearch-client-go/v4 v4.12.0/go.mod h1:UsYoWx3gl5nyWalsOI85b7NGW4t1uaN05vydu9aPfGM= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -203,7 +201,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/auth/auth_check.go b/pkg/auth/auth_check.go index 52df4fdc..d2021304 100644 --- a/pkg/auth/auth_check.go +++ b/pkg/auth/auth_check.go @@ -81,7 +81,7 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { if err != nil { return err } - _, err = client.ListAPIKeys() + _, err = client.ListApiKeys() if err == nil { return nil // Admin API Key, no need to check ACLs } @@ -92,12 +92,21 @@ func CheckACLs(cmd *cobra.Command, f *cmdutil.Factory) error { } // Check the ACLs of the provided API Key - apiKey, err := client.GetAPIKey(f.Config.Profile().GetAPIKey()) + key, err := f.Config.Profile().GetAPIKey() if err != nil { return err } + apiKey, err := client.GetApiKey(client.NewApiGetApiKeyRequest(key)) + if err != nil { + return err + } + + var hasAcls []string + for _, acl := range apiKey.Acl { + hasAcls = append(hasAcls, string(acl)) + } - missingACLs := utils.Differences(neededACLs, apiKey.ACL) + missingACLs := utils.Differences(neededACLs, hasAcls) if len(missingACLs) > 0 { return errMissingACLs(missingACLs) } diff --git a/pkg/auth/auth_check_test.go b/pkg/auth/auth_check_test.go index dd82354f..8f887710 100644 --- a/pkg/auth/auth_check_test.go +++ b/pkg/auth/auth_check_test.go @@ -3,7 +3,7 @@ package auth import ( "testing" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/test" @@ -16,7 +16,7 @@ func Test_CheckACLs(t *testing.T) { name string cmd *cobra.Command adminKey bool - ACLs []string + ACLs []search.Acl wantErr bool wantErrMessage string }{ @@ -26,7 +26,7 @@ func Test_CheckACLs(t *testing.T) { Annotations: map[string]string{}, }, adminKey: false, - ACLs: []string{}, + ACLs: []search.Acl{}, wantErr: false, }, { @@ -37,7 +37,7 @@ func Test_CheckACLs(t *testing.T) { }, }, adminKey: false, - ACLs: []string{}, + ACLs: []search.Acl{}, wantErr: true, wantErrMessage: "this command requires an admin API key. Use the `--api-key` flag with a valid admin API key", }, @@ -49,7 +49,7 @@ func Test_CheckACLs(t *testing.T) { }, }, adminKey: true, - ACLs: []string{}, + ACLs: []search.Acl{}, wantErr: false, wantErrMessage: "", }, @@ -61,7 +61,7 @@ func Test_CheckACLs(t *testing.T) { }, }, adminKey: false, - ACLs: []string{}, + ACLs: []search.Acl{}, wantErr: true, wantErrMessage: `Missing API key ACL(s): search Edit your profile or use the ` + "`--api-key`" + ` flag to provide an API key with the missing ACLs. @@ -75,7 +75,7 @@ See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restriction }, }, adminKey: false, - ACLs: []string{"search"}, + ACLs: []search.Acl{search.ACL_SEARCH}, wantErr: false, }, } @@ -86,7 +86,7 @@ See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restriction if tt.adminKey { r.Register( httpmock.REST("GET", "1/keys"), - httpmock.JSONResponse(search.ListAPIKeysRes{}), + httpmock.JSONResponse(search.ListApiKeysResponse{}), ) } else { r.Register( @@ -98,7 +98,7 @@ See https://www.algolia.com/doc/guides/security/api-keys/#rights-and-restriction if tt.ACLs != nil && !tt.adminKey { r.Register( httpmock.REST("GET", "1/keys/test"), - httpmock.JSONResponse(search.Key{ACL: tt.ACLs}), + httpmock.JSONResponse(search.ApiKey{Acl: tt.ACLs}), ) } diff --git a/pkg/cmd/apikeys/create/create.go b/pkg/cmd/apikeys/create/create.go index 45388831..21e07b5a 100644 --- a/pkg/cmd/apikeys/create/create.go +++ b/pkg/cmd/apikeys/create/create.go @@ -34,7 +34,7 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co opts := &CreateOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "create", @@ -106,7 +106,7 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co if err != nil { return nil, cobra.ShellCompDirectiveError } - indicesRes, err := client.ListIndices() + indicesRes, err := client.ListIndices(client.NewApiListIndicesRequest()) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -114,7 +114,7 @@ func NewCreateCmd(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co for _, index := range indicesRes.Items { allowedIndices = append( allowedIndices, - fmt.Sprintf("%s\t%s records", index.Name, humanize.Comma(index.Entries)), + fmt.Sprintf("%s\t%s records", index.Name, humanize.Comma(int64(index.Entries))), ) } return allowedIndices, cobra.ShellCompDirectiveNoFileComp diff --git a/pkg/cmd/apikeys/create/create_test.go b/pkg/cmd/apikeys/create/create_test.go index b6eb9921..b5d00b21 100644 --- a/pkg/cmd/apikeys/create/create_test.go +++ b/pkg/cmd/apikeys/create/create_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewCreateCmd(t *testing.T) { diff --git a/pkg/cmd/apikeys/delete/delete.go b/pkg/cmd/apikeys/delete/delete.go index 1b6931ba..47b142a5 100644 --- a/pkg/cmd/apikeys/delete/delete.go +++ b/pkg/cmd/apikeys/delete/delete.go @@ -29,7 +29,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool diff --git a/pkg/cmd/apikeys/delete/delete_test.go b/pkg/cmd/apikeys/delete/delete_test.go index 26f34552..91871bdd 100644 --- a/pkg/cmd/apikeys/delete/delete_test.go +++ b/pkg/cmd/apikeys/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/apikeys/get/get.go b/pkg/cmd/apikeys/get/get.go index 378441cf..592f56f5 100644 --- a/pkg/cmd/apikeys/get/get.go +++ b/pkg/cmd/apikeys/get/get.go @@ -30,7 +30,7 @@ func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command opts := &GetOptions{ IO: f.IOStreams, config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } diff --git a/pkg/cmd/apikeys/get/get_test.go b/pkg/cmd/apikeys/get/get_test.go index ca0892fa..ebd2affd 100644 --- a/pkg/cmd/apikeys/get/get_test.go +++ b/pkg/cmd/apikeys/get/get_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runGetCmd(t *testing.T) { diff --git a/pkg/cmd/apikeys/list/list.go b/pkg/cmd/apikeys/list/list.go index 4ea0376d..85cea010 100644 --- a/pkg/cmd/apikeys/list/list.go +++ b/pkg/cmd/apikeys/list/list.go @@ -30,7 +30,7 @@ func NewListCmd(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman opts := &ListOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ diff --git a/pkg/cmd/apikeys/list/list_test.go b/pkg/cmd/apikeys/list/list_test.go index a9703a55..287e9a22 100644 --- a/pkg/cmd/apikeys/list/list_test.go +++ b/pkg/cmd/apikeys/list/list_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runListCmd(t *testing.T) { diff --git a/pkg/cmd/dictionary/entries/browse/browse.go b/pkg/cmd/dictionary/entries/browse/browse.go index 0672495f..d5ccf34e 100644 --- a/pkg/cmd/dictionary/entries/browse/browse.go +++ b/pkg/cmd/dictionary/entries/browse/browse.go @@ -34,7 +34,7 @@ func NewBrowseCmd(f *cmdutil.Factory, runF func(*BrowseOptions) error) *cobra.Co opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } diff --git a/pkg/cmd/dictionary/entries/browse/browse_test.go b/pkg/cmd/dictionary/entries/browse/browse_test.go index 990bcabd..f63a1675 100644 --- a/pkg/cmd/dictionary/entries/browse/browse_test.go +++ b/pkg/cmd/dictionary/entries/browse/browse_test.go @@ -7,8 +7,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runBrowseCmd(t *testing.T) { diff --git a/pkg/cmd/dictionary/entries/clear/clear.go b/pkg/cmd/dictionary/entries/clear/clear.go index 93ae57d0..84f9c0fe 100644 --- a/pkg/cmd/dictionary/entries/clear/clear.go +++ b/pkg/cmd/dictionary/entries/clear/clear.go @@ -37,7 +37,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm opts := &ClearOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "clear {... | --all} [--confirm]", diff --git a/pkg/cmd/dictionary/entries/clear/clear_test.go b/pkg/cmd/dictionary/entries/clear/clear_test.go index ff994ec5..9dbf461f 100644 --- a/pkg/cmd/dictionary/entries/clear/clear_test.go +++ b/pkg/cmd/dictionary/entries/clear/clear_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewClearCmd(t *testing.T) { diff --git a/pkg/cmd/dictionary/entries/delete/delete.go b/pkg/cmd/dictionary/entries/delete/delete.go index 7d0019d5..d44d33ca 100644 --- a/pkg/cmd/dictionary/entries/delete/delete.go +++ b/pkg/cmd/dictionary/entries/delete/delete.go @@ -34,7 +34,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "delete --object-ids [--confirm]", diff --git a/pkg/cmd/dictionary/entries/delete/delete_test.go b/pkg/cmd/dictionary/entries/delete/delete_test.go index 9f41fa1d..a00939cf 100644 --- a/pkg/cmd/dictionary/entries/delete/delete_test.go +++ b/pkg/cmd/dictionary/entries/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/dictionary/entries/import/import.go b/pkg/cmd/dictionary/entries/import/import.go index 0f58d63a..91af4d6d 100644 --- a/pkg/cmd/dictionary/entries/import/import.go +++ b/pkg/cmd/dictionary/entries/import/import.go @@ -42,7 +42,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ diff --git a/pkg/cmd/dictionary/entries/import/import_test.go b/pkg/cmd/dictionary/entries/import/import_test.go index b8c0dd3c..0f283fcb 100644 --- a/pkg/cmd/dictionary/entries/import/import_test.go +++ b/pkg/cmd/dictionary/entries/import/import_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runImportCmd(t *testing.T) { diff --git a/pkg/cmd/dictionary/settings/get/get.go b/pkg/cmd/dictionary/settings/get/get.go index f8a71cd2..39eb7245 100644 --- a/pkg/cmd/dictionary/settings/get/get.go +++ b/pkg/cmd/dictionary/settings/get/get.go @@ -24,7 +24,7 @@ func NewGetCmd(f *cmdutil.Factory, runF func(*GetOptions) error) *cobra.Command opts := &GetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } cmd := &cobra.Command{ diff --git a/pkg/cmd/dictionary/settings/set/set.go b/pkg/cmd/dictionary/settings/set/set.go index ad6a48ba..5c18e18f 100644 --- a/pkg/cmd/dictionary/settings/set/set.go +++ b/pkg/cmd/dictionary/settings/set/set.go @@ -28,7 +28,7 @@ func NewSetCmd(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command opts := &SetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "set --disable-standard-entries --enable-standard-entries [--reset-standard-entries]", diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index f8b86f1d..896a1de8 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -3,7 +3,6 @@ package factory import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v4/algolia/call" "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" @@ -44,22 +43,13 @@ func searchClient(f *cmdutil.Factory, appVersion string) func() (*search.APIClie defaultClient, _ := search.NewClient(appID, apiKey) defaultUserAgent := defaultClient.GetConfiguration().UserAgent - var hosts []transport.StatefulHost - for _, host := range f.Config.Profile().GetSearchHosts() { - statefulHost := transport.NewStatefulHost("https", host, call.IsReadWrite) - hosts = append(hosts, statefulHost) - } - + // TODO: Doesn't support custom `search_hosts` yet. + // To support it, it's best to transform the GetSearchHosts() function clientConf := search.SearchConfiguration{ Configuration: transport.Configuration{ - AppID: appID, - ApiKey: apiKey, - UserAgent: defaultUserAgent + fmt.Sprintf( - "Algolia CLI (%s)", - appVersion, - ), - Hosts: hosts, - ExposeIntermediateNetworkErrors: true, + AppID: appID, + ApiKey: apiKey, + UserAgent: defaultUserAgent + fmt.Sprintf("Algolia CLI (%s)", appVersion), }, } diff --git a/pkg/cmd/indices/analyze/analyze.go b/pkg/cmd/indices/analyze/analyze.go index 57756ddd..35a50c3f 100644 --- a/pkg/cmd/indices/analyze/analyze.go +++ b/pkg/cmd/indices/analyze/analyze.go @@ -36,14 +36,14 @@ func NewAnalyzeCmd(f *cmdutil.Factory) *cobra.Command { opts := &StatsOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ Use: "analyze ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "browse,settings", }, diff --git a/pkg/cmd/indices/clear/clear.go b/pkg/cmd/indices/clear/clear.go index 00096c75..accafb7f 100644 --- a/pkg/cmd/indices/clear/clear.go +++ b/pkg/cmd/indices/clear/clear.go @@ -29,7 +29,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm opts := &ClearOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -37,7 +37,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm cmd := &cobra.Command{ Use: "clear ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteIndex", }, diff --git a/pkg/cmd/indices/clear/clear_test.go b/pkg/cmd/indices/clear/clear_test.go index 1daa690d..b689ce9b 100644 --- a/pkg/cmd/indices/clear/clear_test.go +++ b/pkg/cmd/indices/clear/clear_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewClearCmd(t *testing.T) { diff --git a/pkg/cmd/indices/config/export/export.go b/pkg/cmd/indices/config/export/export.go index 2bbf6ce5..edb6d751 100644 --- a/pkg/cmd/indices/config/export/export.go +++ b/pkg/cmd/indices/config/export/export.go @@ -22,13 +22,13 @@ func NewExportCmd(f *cmdutil.Factory) *cobra.Command { opts := &config.ExportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "export [--scope ...] [--directory]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings", }, diff --git a/pkg/cmd/indices/config/import/import.go b/pkg/cmd/indices/config/import/import.go index 33edb4d0..ffb5a84c 100644 --- a/pkg/cmd/indices/config/import/import.go +++ b/pkg/cmd/indices/config/import/import.go @@ -19,7 +19,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &config.ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -28,7 +28,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "import -F --scope ...", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings,editSettings", }, diff --git a/pkg/cmd/indices/copy/copy.go b/pkg/cmd/indices/copy/copy.go index 9eb117c8..960f78ea 100644 --- a/pkg/cmd/indices/copy/copy.go +++ b/pkg/cmd/indices/copy/copy.go @@ -36,7 +36,7 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman opts := &CopyOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -44,7 +44,7 @@ func NewCopyCmd(f *cmdutil.Factory, runF func(*CopyOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "copy ", Args: validators.ExactArgs(2), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "settings,editSettings,browse,addObject", }, diff --git a/pkg/cmd/indices/copy/copy_test.go b/pkg/cmd/indices/copy/copy_test.go index 60afb7b0..51b06410 100644 --- a/pkg/cmd/indices/copy/copy_test.go +++ b/pkg/cmd/indices/copy/copy_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewCopyCmd(t *testing.T) { diff --git a/pkg/cmd/indices/delete/delete.go b/pkg/cmd/indices/delete/delete.go index a57b26b3..d1ebbc5f 100644 --- a/pkg/cmd/indices/delete/delete.go +++ b/pkg/cmd/indices/delete/delete.go @@ -31,7 +31,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -39,7 +39,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "delete ", Args: cobra.MinimumNArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteIndex", }, diff --git a/pkg/cmd/indices/delete/delete_test.go b/pkg/cmd/indices/delete/delete_test.go index e15faed6..afd8d633 100644 --- a/pkg/cmd/indices/delete/delete_test.go +++ b/pkg/cmd/indices/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/indices/list/list.go b/pkg/cmd/indices/list/list.go index e9d0178d..386be662 100644 --- a/pkg/cmd/indices/list/list.go +++ b/pkg/cmd/indices/list/list.go @@ -31,7 +31,7 @@ func NewListCmd(f *cmdutil.Factory) *cobra.Command { opts := &ListOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags(), } cmd := &cobra.Command{ diff --git a/pkg/cmd/indices/move/move.go b/pkg/cmd/indices/move/move.go index dcd6dee4..a53283a1 100644 --- a/pkg/cmd/indices/move/move.go +++ b/pkg/cmd/indices/move/move.go @@ -34,7 +34,7 @@ func NewMoveCmd(f *cmdutil.Factory, runF func(*MoveOptions) error) *cobra.Comman opts := &MoveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -42,7 +42,7 @@ func NewMoveCmd(f *cmdutil.Factory, runF func(*MoveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "move ", Args: validators.ExactArgs(2), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "addObject", }, diff --git a/pkg/cmd/indices/move/move_test.go b/pkg/cmd/indices/move/move_test.go index 94ed7024..f35a9c2e 100644 --- a/pkg/cmd/indices/move/move_test.go +++ b/pkg/cmd/indices/move/move_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewMoveCmd(t *testing.T) { diff --git a/pkg/cmd/objects/browse/browse.go b/pkg/cmd/objects/browse/browse.go index 6aa6114c..a2a485ba 100644 --- a/pkg/cmd/objects/browse/browse.go +++ b/pkg/cmd/objects/browse/browse.go @@ -30,7 +30,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -38,7 +38,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { Use: "browse ", Aliases: []string{"list"}, Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "runInWebCLI": "true", "acls": "browse", diff --git a/pkg/cmd/objects/browse/browse_test.go b/pkg/cmd/objects/browse/browse_test.go index 459a28e2..1080d655 100644 --- a/pkg/cmd/objects/browse/browse_test.go +++ b/pkg/cmd/objects/browse/browse_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runBrowseCmd(t *testing.T) { diff --git a/pkg/cmd/objects/delete/delete.go b/pkg/cmd/objects/delete/delete.go index 682d200a..0b67f71e 100644 --- a/pkg/cmd/objects/delete/delete.go +++ b/pkg/cmd/objects/delete/delete.go @@ -39,13 +39,13 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "delete [--object-ids | --filters ...] [--confirm] [--wait]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "deleteObject", }, diff --git a/pkg/cmd/objects/delete/delete_test.go b/pkg/cmd/objects/delete/delete_test.go index a9d34528..c5c41c55 100644 --- a/pkg/cmd/objects/delete/delete_test.go +++ b/pkg/cmd/objects/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/objects/import/import_test.go b/pkg/cmd/objects/import/import_test.go index d7809ef7..a942a35c 100644 --- a/pkg/cmd/objects/import/import_test.go +++ b/pkg/cmd/objects/import/import_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runImportCmd(t *testing.T) { diff --git a/pkg/cmd/objects/operations/operations.go b/pkg/cmd/objects/operations/operations.go index ee3a4e3d..54b871bd 100644 --- a/pkg/cmd/objects/operations/operations.go +++ b/pkg/cmd/objects/operations/operations.go @@ -39,7 +39,7 @@ func NewOperationsCmd(f *cmdutil.Factory, runF func(*OperationsOptions) error) * opts := &OperationsOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ diff --git a/pkg/cmd/objects/operations/operations_test.go b/pkg/cmd/objects/operations/operations_test.go index 79900cbe..6b6fa869 100644 --- a/pkg/cmd/objects/operations/operations_test.go +++ b/pkg/cmd/objects/operations/operations_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runOperationsCmd(t *testing.T) { diff --git a/pkg/cmd/objects/update/update.go b/pkg/cmd/objects/update/update.go index 767cf321..1287e850 100644 --- a/pkg/cmd/objects/update/update.go +++ b/pkg/cmd/objects/update/update.go @@ -41,13 +41,13 @@ func NewUpdateCmd(f *cmdutil.Factory, runF func(*UpdateOptions) error) *cobra.Co opts := &UpdateOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "update -F [--create-if-not-exists] [--wait] [--continue-on-error]", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "addObject", }, diff --git a/pkg/cmd/objects/update/update_test.go b/pkg/cmd/objects/update/update_test.go index d63dbf87..79e55059 100644 --- a/pkg/cmd/objects/update/update_test.go +++ b/pkg/cmd/objects/update/update_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runUpdateCmd(t *testing.T) { diff --git a/pkg/cmd/rules/browse/browse.go b/pkg/cmd/rules/browse/browse.go index 471c525d..cc4d0482 100644 --- a/pkg/cmd/rules/browse/browse.go +++ b/pkg/cmd/rules/browse/browse.go @@ -30,7 +30,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &ExportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -38,7 +38,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { Use: "browse ", Args: validators.ExactArgs(1), Aliases: []string{"list"}, - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Short: "List all the rules of an index", Annotations: map[string]string{ "runInWebCLI": "true", diff --git a/pkg/cmd/rules/browse/browse_test.go b/pkg/cmd/rules/browse/browse_test.go index 337d60dd..adf58473 100644 --- a/pkg/cmd/rules/browse/browse_test.go +++ b/pkg/cmd/rules/browse/browse_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runBrowseCmd(t *testing.T) { diff --git a/pkg/cmd/rules/delete/delete_test.go b/pkg/cmd/rules/delete/delete_test.go index 82537442..2a153c92 100644 --- a/pkg/cmd/rules/delete/delete_test.go +++ b/pkg/cmd/rules/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/rules/import/import.go b/pkg/cmd/rules/import/import.go index bafd067e..33a48283 100644 --- a/pkg/cmd/rules/import/import.go +++ b/pkg/cmd/rules/import/import.go @@ -35,7 +35,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var confirm bool @@ -44,7 +44,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, diff --git a/pkg/cmd/rules/import/import_test.go b/pkg/cmd/rules/import/import_test.go index cfafecdf..25cd74d6 100644 --- a/pkg/cmd/rules/import/import_test.go +++ b/pkg/cmd/rules/import/import_test.go @@ -14,9 +14,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewImportCmd(t *testing.T) { diff --git a/pkg/cmd/search/search.go b/pkg/cmd/search/search.go index ad93d9e2..85cd0b23 100644 --- a/pkg/cmd/search/search.go +++ b/pkg/cmd/search/search.go @@ -30,7 +30,7 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { opts := &SearchOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -38,7 +38,7 @@ func NewSearchCmd(f *cmdutil.Factory) *cobra.Command { Use: "search ", Short: "Search the given index", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Long: `Search for objects in your index.`, Annotations: map[string]string{ "runInWebCLI": "true", diff --git a/pkg/cmd/settings/get/list.go b/pkg/cmd/settings/get/list.go index 6251d7bf..90082e53 100644 --- a/pkg/cmd/settings/get/list.go +++ b/pkg/cmd/settings/get/list.go @@ -29,7 +29,7 @@ func NewGetCmd(f *cmdutil.Factory) *cobra.Command { opts := &GetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } cmd := &cobra.Command{ @@ -44,7 +44,7 @@ func NewGetCmd(f *cmdutil.Factory) *cobra.Command { # Store the settings of an index in a file $ algolia settings get MOVIES > movies_settings.json `), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), RunE: func(cmd *cobra.Command, args []string) error { opts.Index = args[0] diff --git a/pkg/cmd/settings/import/import.go b/pkg/cmd/settings/import/import.go index a72b5b2b..8318392d 100644 --- a/pkg/cmd/settings/import/import.go +++ b/pkg/cmd/settings/import/import.go @@ -30,7 +30,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var settingsFile string @@ -38,7 +38,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, diff --git a/pkg/cmd/settings/import/import_test.go b/pkg/cmd/settings/import/import_test.go index 5bca2f52..4eecd990 100644 --- a/pkg/cmd/settings/import/import_test.go +++ b/pkg/cmd/settings/import/import_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runExportCmd(t *testing.T) { diff --git a/pkg/cmd/settings/set/set.go b/pkg/cmd/settings/set/set.go index ff65978d..a465ddd0 100644 --- a/pkg/cmd/settings/set/set.go +++ b/pkg/cmd/settings/set/set.go @@ -31,7 +31,7 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { opts := &SetOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "set ", @@ -44,7 +44,7 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { # Set the typo tolerance to false on the MOVIES index $ algolia settings set MOVIES --typoTolerance="false" `), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), RunE: func(cmd *cobra.Command, args []string) error { opts.Index = args[0] diff --git a/pkg/cmd/settings/set/set_test.go b/pkg/cmd/settings/set/set_test.go index f6d0ab58..622bfd35 100644 --- a/pkg/cmd/settings/set/set_test.go +++ b/pkg/cmd/settings/set/set_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runSetCmd(t *testing.T) { diff --git a/pkg/cmd/synonyms/browse/browse.go b/pkg/cmd/synonyms/browse/browse.go index 55ca51ff..3ef5be36 100644 --- a/pkg/cmd/synonyms/browse/browse.go +++ b/pkg/cmd/synonyms/browse/browse.go @@ -29,7 +29,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { opts := &BrowseOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, PrintFlags: cmdutil.NewPrintFlags().WithDefaultOutput("json"), } @@ -37,7 +37,7 @@ func NewBrowseCmd(f *cmdutil.Factory) *cobra.Command { Use: "browse ", Aliases: []string{"list"}, Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Short: "List all the the synonyms of the given index", Annotations: map[string]string{ "runInWebCLI": "true", diff --git a/pkg/cmd/synonyms/browse/browse_test.go b/pkg/cmd/synonyms/browse/browse_test.go index e0526651..c3a68261 100644 --- a/pkg/cmd/synonyms/browse/browse_test.go +++ b/pkg/cmd/synonyms/browse/browse_test.go @@ -6,8 +6,8 @@ import ( "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/stretchr/testify/assert" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" ) func Test_runBrowseCmd(t *testing.T) { diff --git a/pkg/cmd/synonyms/delete/delete.go b/pkg/cmd/synonyms/delete/delete.go index 981e2184..c277eb06 100644 --- a/pkg/cmd/synonyms/delete/delete.go +++ b/pkg/cmd/synonyms/delete/delete.go @@ -36,13 +36,13 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co opts := &DeleteOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } cmd := &cobra.Command{ Use: "delete --synonyms --confirm", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, diff --git a/pkg/cmd/synonyms/delete/delete_test.go b/pkg/cmd/synonyms/delete/delete_test.go index d39cbbc3..8133fd84 100644 --- a/pkg/cmd/synonyms/delete/delete_test.go +++ b/pkg/cmd/synonyms/delete/delete_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewDeleteCmd(t *testing.T) { diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index 197df43f..858877f5 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -32,7 +32,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co opts := &ImportOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } var file string @@ -40,7 +40,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co cmd := &cobra.Command{ Use: "import -F ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, diff --git a/pkg/cmd/synonyms/import/import_test.go b/pkg/cmd/synonyms/import/import_test.go index 1c1ed2b4..8bb545fd 100644 --- a/pkg/cmd/synonyms/import/import_test.go +++ b/pkg/cmd/synonyms/import/import_test.go @@ -14,9 +14,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewImportCmd(t *testing.T) { diff --git a/pkg/cmd/synonyms/save/save.go b/pkg/cmd/synonyms/save/save.go index 5a9bcff5..4bde3e6e 100644 --- a/pkg/cmd/synonyms/save/save.go +++ b/pkg/cmd/synonyms/save/save.go @@ -32,7 +32,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman opts := &SaveOptions{ IO: f.IOStreams, Config: f.Config, - SearchClient: f.V4SearchClient, + SearchClient: f.SearchClient, } flags := &shared.SynonymFlags{} @@ -40,7 +40,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman cmd := &cobra.Command{ Use: "save --id --synonyms ", Args: validators.ExactArgs(1), - ValidArgsFunction: cmdutil.V4IndexNames(opts.SearchClient), + ValidArgsFunction: cmdutil.IndexNames(opts.SearchClient), Annotations: map[string]string{ "acls": "editSettings", }, diff --git a/pkg/cmd/synonyms/save/save_test.go b/pkg/cmd/synonyms/save/save_test.go index 4b636ba0..1792566d 100644 --- a/pkg/cmd/synonyms/save/save_test.go +++ b/pkg/cmd/synonyms/save/save_test.go @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/require" "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/httpmock/v4" + "github.com/algolia/cli/pkg/httpmock" "github.com/algolia/cli/pkg/iostreams" - "github.com/algolia/cli/test/v4" + "github.com/algolia/cli/test" ) func TestNewSaveCmd(t *testing.T) { diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index db2c7996..fb88f3b5 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -5,8 +5,7 @@ import ( "path/filepath" "strings" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/config" @@ -14,11 +13,10 @@ import ( ) type Factory struct { - IOStreams *iostreams.IOStreams - Config config.IConfig - SearchClient func() (*search.Client, error) - V4SearchClient func() (*v4.APIClient, error) - CrawlerClient func() (*crawler.Client, error) + IOStreams *iostreams.IOStreams + Config config.IConfig + SearchClient func() (*search.APIClient, error) + CrawlerClient func() (*crawler.Client, error) ExecutableName string } diff --git a/pkg/cmdutil/valid_args.go b/pkg/cmdutil/valid_args.go index d9989fd9..a7e809b7 100644 --- a/pkg/cmdutil/valid_args.go +++ b/pkg/cmdutil/valid_args.go @@ -3,37 +3,14 @@ package cmdutil import ( "fmt" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" - v4 "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" "github.com/algolia/cli/api/crawler" "github.com/spf13/cobra" ) // IndexNames returns a function to list the index names from the given search client. func IndexNames( - clientF func() (*search.Client, error), -) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - client, err := clientF() - if err != nil { - return nil, cobra.ShellCompDirectiveError - } - res, err := client.ListIndices() - if err != nil { - return nil, cobra.ShellCompDirectiveError - } - - names := make([]string, 0, len(res.Items)) - for _, index := range res.Items { - names = append(names, index.Name) - } - return names, cobra.ShellCompDirectiveNoFileComp - } -} - -// IndexNames returns a function to list the index names from the given search client. -func V4IndexNames( - clientF func() (*v4.APIClient, error), + clientF func() (*search.APIClient, error), ) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { client, err := clientF() diff --git a/pkg/httpmock/registry.go b/pkg/httpmock/registry.go index cf32c520..3e942097 100644 --- a/pkg/httpmock/registry.go +++ b/pkg/httpmock/registry.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "sync" + "time" ) type Registry struct { @@ -38,7 +39,11 @@ func (r *Registry) Verify(t Testing) { } // Request satisfies Requester interface -func (r *Registry) Request(req *http.Request) (*http.Response, error) { +func (r *Registry) Request( + req *http.Request, + _ time.Duration, + _ time.Duration, +) (*http.Response, error) { var stub *Stub r.mu.Lock() diff --git a/pkg/httpmock/v4/registry.go b/pkg/httpmock/v4/registry.go deleted file mode 100644 index 3e942097..00000000 --- a/pkg/httpmock/v4/registry.go +++ /dev/null @@ -1,101 +0,0 @@ -package httpmock - -import ( - "fmt" - "net/http" - "sync" - "time" -) - -type Registry struct { - mu sync.Mutex - stubs []*Stub - Requests []*http.Request -} - -func (r *Registry) Register(m Matcher, resp Responder) { - r.stubs = append(r.stubs, &Stub{ - Matcher: m, - Responder: resp, - }) -} - -type Testing interface { - Errorf(string, ...interface{}) - Helper() -} - -func (r *Registry) Verify(t Testing) { - n := 0 - for _, s := range r.stubs { - if !s.matched { - n++ - } - } - if n > 0 { - t.Helper() - t.Errorf("%d unmatched HTTP stubs", n) - } -} - -// Request satisfies Requester interface -func (r *Registry) Request( - req *http.Request, - _ time.Duration, - _ time.Duration, -) (*http.Response, error) { - var stub *Stub - - r.mu.Lock() - for _, s := range r.stubs { - if s.matched || !s.Matcher(req) { - continue - } - - stub = s - break - } - if stub != nil { - stub.matched = true - } - - if stub == nil { - r.mu.Unlock() - return nil, fmt.Errorf("no registered stubs matched %v", req) - } - - r.Requests = append(r.Requests, req) - r.mu.Unlock() - - return stub.Responder(req) -} - -// RoundTrip satisfies http.RoundTripper -func (r *Registry) RoundTrip(req *http.Request) (*http.Response, error) { - var stub *Stub - - r.mu.Lock() - for _, s := range r.stubs { - if s.matched || !s.Matcher(req) { - continue - } - if stub != nil { - r.mu.Unlock() - return nil, fmt.Errorf("more than 1 stub matched %v", req) - } - stub = s - } - if stub != nil { - stub.matched = true - } - - if stub == nil { - r.mu.Unlock() - return nil, fmt.Errorf("no registered stubs matched %v", req) - } - - r.Requests = append(r.Requests, req) - r.mu.Unlock() - - return stub.Responder(req) -} diff --git a/pkg/httpmock/v4/stub.go b/pkg/httpmock/v4/stub.go deleted file mode 100644 index 25648215..00000000 --- a/pkg/httpmock/v4/stub.go +++ /dev/null @@ -1,65 +0,0 @@ -package httpmock - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "strings" -) - -type Matcher func(req *http.Request) bool -type Responder func(req *http.Request) (*http.Response, error) - -type Stub struct { - matched bool - Matcher Matcher - Responder Responder -} - -func REST(method, p string) Matcher { - return func(req *http.Request) bool { - if !strings.EqualFold(req.Method, method) { - return false - } - if req.URL.Path != "/"+p { - return false - } - return true - } -} - -func StringResponse(body string) Responder { - return func(req *http.Request) (*http.Response, error) { - return httpResponse(200, req, bytes.NewBufferString(body)), nil - } -} - -func JSONResponse(body interface{}) Responder { - return func(req *http.Request) (*http.Response, error) { - b, _ := json.Marshal(body) - return httpResponse(200, req, bytes.NewBuffer(b)), nil - } -} - -func ErrorResponse() Responder { - return func(req *http.Request) (*http.Response, error) { - return httpResponse(404, req, bytes.NewBufferString("")), nil - } -} - -func ErrorResponseWithBody(body interface{}) Responder { - return func(req *http.Request) (*http.Response, error) { - b, _ := json.Marshal(body) - return httpResponse(400, req, bytes.NewBuffer(b)), nil - } -} - -func httpResponse(status int, req *http.Request, body io.Reader) *http.Response { - return &http.Response{ - StatusCode: status, - Request: req, - Body: io.NopCloser(body), - Header: http.Header{}, - } -} diff --git a/test/helpers.go b/test/helpers.go index a1fff497..039335ad 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -8,7 +8,8 @@ import ( "github.com/google/shlex" "github.com/spf13/cobra" - "github.com/algolia/algoliasearch-client-go/v3/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" "github.com/algolia/cli/api/crawler" "github.com/algolia/cli/pkg/cmdutil" "github.com/algolia/cli/pkg/config" @@ -69,10 +70,15 @@ func NewFactory( } if r != nil { - f.SearchClient = func() (*search.Client, error) { - return search.NewClientWithConfig(search.Configuration{ - Requester: r, - }), nil + f.SearchClient = func() (*search.APIClient, error) { + cfg := search.SearchConfiguration{ + Configuration: transport.Configuration{ + AppID: "default", + ApiKey: "default", + Requester: r, + }, + } + return search.NewClientWithConfig(cfg) } f.CrawlerClient = func() (*crawler.Client, error) { return crawler.NewClientWithHTTPClient("id", "key", &http.Client{ diff --git a/test/v4/config.go b/test/v4/config.go deleted file mode 100644 index 38e1ebed..00000000 --- a/test/v4/config.go +++ /dev/null @@ -1,89 +0,0 @@ -package test - -import ( - "github.com/algolia/cli/pkg/config" -) - -type ConfigStub struct { - CurrentProfile config.Profile - profiles []*config.Profile -} - -func (c *ConfigStub) InitConfig() {} - -func (c *ConfigStub) Profile() *config.Profile { - return &c.CurrentProfile -} - -func (c *ConfigStub) Default() *config.Profile { - return &c.CurrentProfile -} - -func (c *ConfigStub) ConfiguredProfiles() []*config.Profile { - return c.profiles -} - -func (c *ConfigStub) ProfileNames() []string { - names := make([]string, 0, len(c.ConfiguredProfiles())) - for _, profile := range c.ConfiguredProfiles() { - names = append(names, profile.Name) - } - return names -} - -func (c *ConfigStub) ProfileExists(name string) bool { - for _, profile := range c.ConfiguredProfiles() { - if profile.Name == name { - return true - } - } - return false -} - -func (c *ConfigStub) ApplicationIDExists(appID string) (bool, string) { - for _, profile := range c.ConfiguredProfiles() { - if profile.ApplicationID == appID { - return true, profile.Name - } - } - return false, "" -} - -func (c *ConfigStub) RemoveProfile(name string) error { - for i, profile := range c.ConfiguredProfiles() { - if profile.Name == name { - c.profiles = append(c.profiles[:i], c.profiles[i+1:]...) - return nil - } - } - return nil -} - -func (c *ConfigStub) SetDefaultProfile(name string) error { - for _, profile := range c.ConfiguredProfiles() { - if profile.Name == name { - profile.Default = true - } else { - profile.Default = false - } - } - return nil -} - -func NewConfigStubWithProfiles(p []*config.Profile) *ConfigStub { - return &ConfigStub{ - CurrentProfile: *p[0], - profiles: p, - } -} - -func NewDefaultConfigStub() *ConfigStub { - return NewConfigStubWithProfiles([]*config.Profile{ - { - Name: "default", - ApplicationID: "default", - AdminAPIKey: "default", - Default: true, - }, - }) -} diff --git a/test/v4/helpers.go b/test/v4/helpers.go deleted file mode 100644 index a68b276f..00000000 --- a/test/v4/helpers.go +++ /dev/null @@ -1,124 +0,0 @@ -package test - -import ( - "bytes" - "io" - "net/http" - - "github.com/google/shlex" - "github.com/spf13/cobra" - - "github.com/algolia/algoliasearch-client-go/v4/algolia/search" - "github.com/algolia/algoliasearch-client-go/v4/algolia/transport" - "github.com/algolia/cli/api/crawler" - "github.com/algolia/cli/pkg/cmdutil" - "github.com/algolia/cli/pkg/config" - "github.com/algolia/cli/pkg/httpmock/v4" - "github.com/algolia/cli/pkg/iostreams" -) - -type CmdInOut struct { - InBuf *bytes.Buffer - OutBuf *bytes.Buffer - ErrBuf *bytes.Buffer -} - -func (c CmdInOut) String() string { - return c.OutBuf.String() -} - -func (c CmdInOut) Stderr() string { - return c.ErrBuf.String() -} - -type OutputStub struct { - Out []byte - Error error -} - -func (s OutputStub) Output() ([]byte, error) { - if s.Error != nil { - return s.Out, s.Error - } - return s.Out, nil -} - -func (s OutputStub) Run() error { - if s.Error != nil { - return s.Error - } - return nil -} - -func NewFactory( - isTTY bool, - r *httpmock.Registry, - cfg config.IConfig, - in string, -) (*cmdutil.Factory, *CmdInOut) { - io, stdin, stdout, stderr := iostreams.Test() - io.SetStdoutTTY(isTTY) - io.SetStdinTTY(isTTY) - io.SetStderrTTY(isTTY) - - if in != "" { - stdin.WriteString(in) - } - - f := &cmdutil.Factory{ - IOStreams: io, - } - - if r != nil { - f.V4SearchClient = func() (*search.APIClient, error) { - cfg := search.SearchConfiguration{ - Configuration: transport.Configuration{ - AppID: "default", - ApiKey: "default", - Requester: r, - }, - } - return search.NewClientWithConfig(cfg) - } - f.CrawlerClient = func() (*crawler.Client, error) { - return crawler.NewClientWithHTTPClient("id", "key", &http.Client{ - Transport: r, - }), nil - } - } - - if cfg != nil { - f.Config = cfg - } else { - f.Config = &config.Config{} - } - - return f, &CmdInOut{ - InBuf: stdin, - OutBuf: stdout, - ErrBuf: stderr, - } -} - -func Execute(cmd *cobra.Command, cli string, inOut *CmdInOut) (*CmdInOut, error) { - argv, err := shlex.Split(cli) - if err != nil { - return nil, err - } - cmd.SetArgs(argv) - - if inOut.InBuf != nil { - cmd.SetIn(inOut.InBuf) - } else { - cmd.SetIn(&bytes.Buffer{}) - } - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - - _, err = cmd.ExecuteC() - if err != nil { - return nil, err - } - - return inOut, nil -} From 135ac55e67cb1120f07333f7d5d72c74c5543f7a Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 7 Feb 2025 11:09:33 +0100 Subject: [PATCH 44/45] feat: add --wait flag to all indexing operations For some commands, it was already implemented, but not for all commands. Having the option to wait until the (async) operation is done on Algolia's servers makes it possible to run automatic tests for the commands. --- pkg/cmd/indices/clear/clear.go | 19 ++++++++++++- pkg/cmd/indices/delete/delete.go | 27 ++++++++++++++++-- pkg/cmd/objects/delete/delete.go | 4 ++- pkg/cmd/objects/import/import.go | 22 +++------------ pkg/cmd/rules/delete/delete.go | 3 -- pkg/cmd/rules/import/import.go | 47 +++++++++++++++++++++++++++---- pkg/cmd/settings/import/import.go | 17 +++++++++-- pkg/cmd/settings/set/set.go | 18 ++++++++++-- pkg/cmd/synonyms/delete/delete.go | 21 ++++++++++++-- pkg/cmd/synonyms/import/import.go | 40 ++++++++++++++++++++++++-- pkg/cmd/synonyms/save/save.go | 13 +++++++-- 11 files changed, 187 insertions(+), 44 deletions(-) diff --git a/pkg/cmd/indices/clear/clear.go b/pkg/cmd/indices/clear/clear.go index accafb7f..3532c19d 100644 --- a/pkg/cmd/indices/clear/clear.go +++ b/pkg/cmd/indices/clear/clear.go @@ -22,6 +22,7 @@ type ClearOptions struct { Index string DoConfirm bool + Wait bool } // NewClearCmd creates and returns a clear command for indices @@ -70,6 +71,7 @@ func NewClearCmd(f *cmdutil.Factory, runF func(*ClearOptions) error) *cobra.Comm } cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -94,11 +96,26 @@ func runClearCmd(opts *ClearOptions) error { return err } - _, err = client.ClearObjects(client.NewApiClearObjectsRequest(opts.Index)) + opts.IO.StartProgressIndicatorWithLabel( + fmt.Sprintf("Deleting all records from index %s", opts.Index), + ) + res, err := client.ClearObjects(client.NewApiClearObjectsRequest(opts.Index)) if err != nil { + opts.IO.StopProgressIndicator() return err } + if opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } + + opts.IO.StopProgressIndicator() + cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.Out, "%s Cleared index %s\n", cs.SuccessIcon(), opts.Index) diff --git a/pkg/cmd/indices/delete/delete.go b/pkg/cmd/indices/delete/delete.go index d1ebbc5f..7e14793d 100644 --- a/pkg/cmd/indices/delete/delete.go +++ b/pkg/cmd/indices/delete/delete.go @@ -24,6 +24,7 @@ type DeleteOptions struct { Indices []string DoConfirm bool IncludeReplicas bool + Wait bool } // NewDeleteCmd creates and returns a delete command for indices @@ -84,6 +85,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") cmd.Flags(). BoolVarP(&opts.IncludeReplicas, "include-replicas", "r", false, "delete replica indices too") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -136,6 +138,7 @@ func runDeleteCmd(opts *DeleteOptions) error { ) err = detachReplica(index, *settings.Primary, client) if err != nil { + opts.IO.StopProgressIndicator() return fmt.Errorf("can't detach index %s: %w", index, err) } opts.IO.StopProgressIndicator() @@ -146,14 +149,26 @@ func runDeleteCmd(opts *DeleteOptions) error { ) res, err := client.DeleteIndex(client.NewApiDeleteIndexRequest(index)) if err != nil { + opts.IO.StopProgressIndicator() return fmt.Errorf("can't delete index %s: %w", index, err) } + if !opts.IncludeReplicas && opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") + _, err := client.WaitForTask(index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } + if opts.IncludeReplicas && len(settings.Replicas) > 0 { // Wait for primary to be deleted, otherwise deleting replicas might fail + opts.IO.UpdateProgressIndicatorLabel("Waiting for the primary index to be deleted") _, err := client.WaitForTask(index, res.TaskID) if err != nil { - return fmt.Errorf("error waiting for index %s to be deleted: %w", index, err) + opts.IO.StopProgressIndicator() + return fmt.Errorf("error while waiting for index %s to be deleted: %w", index, err) } for _, replica := range settings.Replicas { @@ -170,10 +185,18 @@ func runDeleteCmd(opts *DeleteOptions) error { opts.IO.UpdateProgressIndicatorLabel( fmt.Sprintf("Deleting replica %s", index), ) - _, err = client.DeleteIndex(client.NewApiDeleteIndexRequest(replica)) + res, err = client.DeleteIndex(client.NewApiDeleteIndexRequest(replica)) if err != nil { + opts.IO.StopProgressIndicator() return fmt.Errorf("can't delete replica %s: %w", replica, err) } + if opts.Wait { + _, err := client.WaitForTask(replica, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } } } opts.IO.StopProgressIndicator() diff --git a/pkg/cmd/objects/delete/delete.go b/pkg/cmd/objects/delete/delete.go index 0b67f71e..b768936d 100644 --- a/pkg/cmd/objects/delete/delete.go +++ b/pkg/cmd/objects/delete/delete.go @@ -204,10 +204,11 @@ func runDeleteCmd(opts *DeleteOptions) error { // Wait for the tasks to complete if opts.Wait { - opts.IO.StartProgressIndicatorWithLabel("Waiting for all of the deletion tasks to complete") + opts.IO.StartProgressIndicatorWithLabel("Waiting for all deletion tasks to complete") for _, taskID := range taskIDs { _, err := client.WaitForTask(opts.Index, taskID) if err != nil { + opts.IO.StopProgressIndicator() return err } } @@ -221,6 +222,7 @@ func runDeleteCmd(opts *DeleteOptions) error { return nil } +// deleteByToSearchParams returns a new SearchParamsObject from a DeleteByParams struct func deleteByToSearchParams(input *search.DeleteByParams) *search.SearchParamsObject { return &search.SearchParamsObject{ Filters: input.Filters, diff --git a/pkg/cmd/objects/import/import.go b/pkg/cmd/objects/import/import.go index 299b3a13..f00ba1ea 100644 --- a/pkg/cmd/objects/import/import.go +++ b/pkg/cmd/objects/import/import.go @@ -22,13 +22,12 @@ type ImportOptions struct { SearchClient func() (*search.APIClient, error) Index string - Scanner *bufio.Scanner - BatchSize int - AutoObjectIDs bool - Wait bool + Scanner *bufio.Scanner + BatchSize int + Wait bool } -// NewImportCmd creates and returns an import command for records +// NewImportCmd creates and returns an import command for indice object func NewImportCmd(f *cmdutil.Factory) *cobra.Command { opts := &ImportOptions{ IO: f.IOStreams, @@ -77,8 +76,6 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { _ = cmd.MarkFlagRequired("file") cmd.Flags().IntVarP(&opts.BatchSize, "batch-size", "b", 1000, "Specify the upload batch size") - cmd.Flags(). - BoolVarP(&opts.AutoObjectIDs, "auto-generate-object-id-if-not-exist", "a", false, "Auto-generate objectIDs if they don't exist") cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -103,17 +100,6 @@ func runImportCmd(opts *ImportOptions) error { err := fmt.Errorf("failed to parse JSON object on line %d: %s", count, err) return err } - - if len(record) == 0 { - return fmt.Errorf("empty object on line %d", count) - } - // The API always automatically generates objectIDs for this operation - // The v3 API clients implemented this option, but not v4, so we'll implement it here - if !opts.AutoObjectIDs { - if _, ok := record["objectID"]; !ok { - return fmt.Errorf("missing objectID on line %d", count) - } - } records = append(records, record) count++ } diff --git a/pkg/cmd/rules/delete/delete.go b/pkg/cmd/rules/delete/delete.go index 35704f48..cd17c467 100644 --- a/pkg/cmd/rules/delete/delete.go +++ b/pkg/cmd/rules/delete/delete.go @@ -141,9 +141,6 @@ func runDeleteCmd(opts *DeleteOptions) error { if len(taskIDs) > 0 { for _, taskID := range taskIDs { _, err := client.WaitForTask(opts.Index, taskID) - if err != nil { - return err - } } } diff --git a/pkg/cmd/rules/import/import.go b/pkg/cmd/rules/import/import.go index 33a48283..88ff5cfe 100644 --- a/pkg/cmd/rules/import/import.go +++ b/pkg/cmd/rules/import/import.go @@ -25,6 +25,7 @@ type ImportOptions struct { Index string ForwardToReplicas bool ClearExistingRules bool + Wait bool Scanner *bufio.Scanner DoConfirm bool @@ -102,6 +103,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the rules to the index replicas") cmd.Flags(). BoolVarP(&opts.ClearExistingRules, "clear-existing-rules", "c", false, "Clear existing rules before importing new ones") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -146,8 +148,8 @@ func runImportCmd(opts *ImportOptions) error { var rule search.Rule if err := json.Unmarshal([]byte(line), &rule); err != nil { - err := fmt.Errorf("failed to parse JSON rule on line %d: %s", count, err) - return err + opts.IO.StopProgressIndicator() + return fmt.Errorf("failed to parse JSON rule on line %d: %s", count, err) } rules = append(rules, rule) @@ -156,17 +158,26 @@ func runImportCmd(opts *ImportOptions) error { // If requested, only clear existing rules the first time clearExistingRules := opts.ClearExistingRules if count == batchSize { - _, err := client.SaveRules( + res, err := client.SaveRules( client.NewApiSaveRulesRequest(opts.Index, rules). WithClearExistingRules(clearExistingRules). WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { + opts.IO.StopProgressIndicator() return err } - rules = make([]search.Rule, 0, batchSize) + if opts.Wait { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d rules", totalCount)) + + rules = make([]search.Rule, 0, batchSize) count = 0 clearExistingRules = false } @@ -174,15 +185,39 @@ func runImportCmd(opts *ImportOptions) error { if count > 0 { totalCount += count - if _, err := client.SaveRules(client.NewApiSaveRulesRequest(opts.Index, rules).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + res, err := client.SaveRules( + client.NewApiSaveRulesRequest(opts.Index, rules). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { + opts.IO.StopProgressIndicator() return err } + if opts.Wait { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } } // Clear rules if 0 rules are imported and the clear existing is set if totalCount == 0 && opts.ClearExistingRules { - if _, err := client.ClearRules(client.NewApiClearRulesRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + res, err := client.ClearRules( + client.NewApiClearRulesRequest(opts.Index). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { + opts.IO.StopProgressIndicator() return err } + if opts.Wait { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } } opts.IO.StopProgressIndicator() diff --git a/pkg/cmd/settings/import/import.go b/pkg/cmd/settings/import/import.go index 8318392d..837a80c7 100644 --- a/pkg/cmd/settings/import/import.go +++ b/pkg/cmd/settings/import/import.go @@ -23,6 +23,7 @@ type ImportOptions struct { Index string Settings search.IndexSettings ForwardToReplicas bool + Wait bool } // NewImportCmd creates and returns an import command for settings @@ -66,6 +67,7 @@ func NewImportCmd(f *cmdutil.Factory) *cobra.Command { _ = cmd.MarkFlagRequired("file") cmd.Flags(). BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the settings to the replicas") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -77,15 +79,26 @@ func runImportCmd(opts *ImportOptions) error { } opts.IO.StartProgressIndicatorWithLabel(fmt.Sprint("Importing settings to index ", opts.Index)) - _, err = client.SetSettings( + res, err := client.SetSettings( client.NewApiSetSettingsRequest(opts.Index, &opts.Settings). WithForwardToReplicas(opts.ForwardToReplicas), ) - opts.IO.StopProgressIndicator() if err != nil { + opts.IO.StopProgressIndicator() return err } + if opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } + + opts.IO.StopProgressIndicator() + cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.Out, "%s Imported settings on %v\n", cs.SuccessIcon(), opts.Index) diff --git a/pkg/cmd/settings/set/set.go b/pkg/cmd/settings/set/set.go index a465ddd0..1a66534e 100644 --- a/pkg/cmd/settings/set/set.go +++ b/pkg/cmd/settings/set/set.go @@ -22,6 +22,7 @@ type SetOptions struct { Settings search.IndexSettings ForwardToReplicas bool + Wait bool Index string } @@ -69,6 +70,7 @@ func NewSetCmd(f *cmdutil.Factory) *cobra.Command { cmd.Flags(). BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", false, "Forward the settings to the replicas") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") cmdutil.AddIndexSettingsFlags(cmd) @@ -84,15 +86,27 @@ func runSetCmd(opts *SetOptions) error { opts.IO.StartProgressIndicatorWithLabel( fmt.Sprintf("Setting settings for index %s", opts.Index), ) - _, err = client.SetSettings( + + res, err := client.SetSettings( client.NewApiSetSettingsRequest(opts.Index, &opts.Settings). WithForwardToReplicas(opts.ForwardToReplicas), ) - opts.IO.StopProgressIndicator() if err != nil { + opts.IO.StopProgressIndicator() return err } + if opts.Wait { + opts.IO.UpdateProgressIndicatorLabel("Waiting for the task to complete") + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + opts.IO.StopProgressIndicator() + return err + } + } + + opts.IO.StopProgressIndicator() + cs := opts.IO.ColorScheme() if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.Out, "%s Set settings on %v\n", cs.SuccessIcon(), opts.Index) diff --git a/pkg/cmd/synonyms/delete/delete.go b/pkg/cmd/synonyms/delete/delete.go index c277eb06..b3ef6616 100644 --- a/pkg/cmd/synonyms/delete/delete.go +++ b/pkg/cmd/synonyms/delete/delete.go @@ -25,6 +25,7 @@ type DeleteOptions struct { Index string SynonymIDs []string ForwardToReplicas bool + Wait bool DoConfirm bool } @@ -82,6 +83,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co BoolVar(&opts.ForwardToReplicas, "forward-to-replicas", false, "Forward the delete request to the replicas") cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -122,14 +124,27 @@ func runDeleteCmd(opts *DeleteOptions) error { } } + var taskIDs []int64 + for _, synonymID := range opts.SynonymIDs { - _, err = client.DeleteSynonym( + res, err := client.DeleteSynonym( client.NewApiDeleteSynonymRequest(opts.Index, synonymID). WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { - err = fmt.Errorf("failed to delete synonym %s: %w", synonymID, err) - return err + return fmt.Errorf("failed to delete synonym %s: %w", synonymID, err) + } + if opts.Wait { + taskIDs = append(taskIDs, res.TaskID) + } + } + + if len(taskIDs) > 0 { + for _, taskID := range taskIDs { + _, err := client.WaitForTask(opts.Index, taskID) + if err != nil { + return err + } } } diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index 858877f5..534544d7 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -24,6 +24,7 @@ type ImportOptions struct { Index string ForwardToReplicas bool ReplaceExistingSynonyms bool + Wait bool Scanner *bufio.Scanner } @@ -90,6 +91,7 @@ func NewImportCmd(f *cmdutil.Factory, runF func(*ImportOptions) error) *cobra.Co BoolVarP(&opts.ForwardToReplicas, "forward-to-replicas", "f", true, "Forward the synonyms to the replicas of the index") cmd.Flags(). BoolVarP(&opts.ReplaceExistingSynonyms, "replace-existing-synonyms", "r", false, "Replace existing synonyms in the index") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "wait for the operation to complete") return cmd } @@ -109,6 +111,7 @@ func runImportCmd(opts *ImportOptions) error { synonyms = make([]search.SynonymHit, 0, batchSize) count = 0 totalCount = 0 + taskIDs []int64 ) opts.IO.StartProgressIndicatorWithLabel("Importing synonyms") @@ -131,9 +134,17 @@ func runImportCmd(opts *ImportOptions) error { count++ if count == batchSize { - if _, err := client.SaveSynonyms(client.NewApiSaveSynonymsRequest(opts.Index, synonyms).WithReplaceExistingSynonyms(clearExistingSynonyms).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + res, err := client.SaveSynonyms( + client.NewApiSaveSynonymsRequest(opts.Index, synonyms). + WithReplaceExistingSynonyms(clearExistingSynonyms). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { return err } + if opts.Wait { + taskIDs = append(taskIDs, res.TaskID) + } synonyms = make([]search.SynonymHit, 0, batchSize) totalCount += count opts.IO.UpdateProgressIndicatorLabel(fmt.Sprintf("Imported %d synonyms", totalCount)) @@ -144,15 +155,38 @@ func runImportCmd(opts *ImportOptions) error { if count > 0 { totalCount += count - if _, err := client.SaveSynonyms(client.NewApiSaveSynonymsRequest(opts.Index, synonyms).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + res, err := client.SaveSynonyms( + client.NewApiSaveSynonymsRequest(opts.Index, synonyms). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { return err } + if opts.Wait { + taskIDs = append(taskIDs, res.TaskID) + } } if totalCount == 0 && opts.ReplaceExistingSynonyms { - if _, err := client.ClearSynonyms(client.NewApiClearSynonymsRequest(opts.Index).WithForwardToReplicas(opts.ForwardToReplicas)); err != nil { + res, err := client.ClearSynonyms( + client.NewApiClearSynonymsRequest(opts.Index). + WithForwardToReplicas(opts.ForwardToReplicas), + ) + if err != nil { return err } + if opts.Wait { + taskIDs = append(taskIDs, res.TaskID) + } + } + + if len(taskIDs) > 0 { + for _, taskID := range taskIDs { + _, err := client.WaitForTask(opts.Index, taskID) + if err != nil { + return err + } + } } opts.IO.StopProgressIndicator() diff --git a/pkg/cmd/synonyms/save/save.go b/pkg/cmd/synonyms/save/save.go index 4bde3e6e..58bb0644 100644 --- a/pkg/cmd/synonyms/save/save.go +++ b/pkg/cmd/synonyms/save/save.go @@ -25,6 +25,7 @@ type SaveOptions struct { ForwardToReplicas bool Synonym search.SynonymHit SuccessMessage string + Wait bool } // NewSaveCmd creates and returns a save command for index synonyms @@ -120,6 +121,7 @@ func NewSaveCmd(f *cmdutil.Factory, runF func(*SaveOptions) error) *cobra.Comman StringVarP(&flags.SynonymWord, "word", "w", "", "A single word, used as the basis for the array of corrections (alt correction synonyms only)") cmd.Flags(). StringSliceVarP(&flags.SynonymCorrections, "corrections", "c", nil, "A list of corrections of the word (alt correction synonyms only)") + cmd.Flags().BoolVarP(&opts.Wait, "wait", "", false, "wait for the operation to complete") return cmd } @@ -129,13 +131,18 @@ func runSaveCmd(opts *SaveOptions) error { if err != nil { return err } - _, err = client.SaveSynonym( + res, err := client.SaveSynonym( client.NewApiSaveSynonymRequest(opts.Index, opts.Synonym.ObjectID, &opts.Synonym). WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { - err = fmt.Errorf("failed to save synonym: %w", err) - return err + return fmt.Errorf("failed to save synonym: %w", err) + } + if opts.Wait { + _, err := client.WaitForTask(opts.Index, res.TaskID) + if err != nil { + return err + } } if opts.IO.IsStdoutTTY() { From 31db9f6b2249e8db6d2fbfb069882e3ea0cf0e11 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 10 Feb 2025 11:29:26 +0100 Subject: [PATCH 45/45] chore: pin everything to go 1.19 --- .github/workflows/releases.yml | 2 +- devbox.json | 2 +- devbox.lock | 196 ++------------------------------- go.mod | 4 +- 4 files changed, 15 insertions(+), 189 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index dfee9b52..53cf05b0 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.23 + go-version: 1.19 - name: Install chocolatey run: | mkdir -p /opt/chocolatey diff --git a/devbox.json b/devbox.json index 7aff8ab8..67854e95 100644 --- a/devbox.json +++ b/devbox.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json", "packages": [ "go-task@latest", - "go@1.23.4", + "go@1.19", "golangci-lint@1.63.4", "gofumpt@latest", "golines@latest" diff --git a/devbox.lock b/devbox.lock index a45b4a51..1e4ab42b 100644 --- a/devbox.lock +++ b/devbox.lock @@ -1,178 +1,6 @@ { "lockfile_version": "1", "packages": { - "curl@latest": { - "last_modified": "2025-02-05T14:25:15Z", - "resolved": "github:NixOS/nixpkgs/ccfae3057498f5a740be4c5a13aa800813a13084#curl", - "source": "devbox-search", - "version": "8.11.1", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "bin", - "path": "/nix/store/4wb2gw5y4d1s9lmll744k7mdkgza1mz1-curl-8.11.1-bin", - "default": true - }, - { - "name": "man", - "path": "/nix/store/02mrnrg0mqps1x6sqaw5dp79gcr010rh-curl-8.11.1-man", - "default": true - }, - { - "name": "out", - "path": "/nix/store/bqkra3fwg9jn301gi14gcizpyrmxqb20-curl-8.11.1" - }, - { - "name": "dev", - "path": "/nix/store/2qfdjp13bid9g5axp74q1sas73wk4kmv-curl-8.11.1-dev" - }, - { - "name": "devdoc", - "path": "/nix/store/l88q3phl5gvv848slpzp9j5kl3gb7qi2-curl-8.11.1-devdoc" - } - ], - "store_path": "/nix/store/4wb2gw5y4d1s9lmll744k7mdkgza1mz1-curl-8.11.1-bin" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "bin", - "path": "/nix/store/b4bfdh987ankyl4dl8hyqka0icbm7ch5-curl-8.11.1-bin", - "default": true - }, - { - "name": "man", - "path": "/nix/store/cz541skcfa3sv96d7fgfl72jln28ai55-curl-8.11.1-man", - "default": true - }, - { - "name": "debug", - "path": "/nix/store/3nxwcrw2nz4psxfa298gz2rvxcxkgwrj-curl-8.11.1-debug" - }, - { - "name": "dev", - "path": "/nix/store/hmrxq7fax0jb25f0p6v77c9idl369wf3-curl-8.11.1-dev" - }, - { - "name": "devdoc", - "path": "/nix/store/xhyfvy56bpwk2xg0sjy0a72p86dbzzny-curl-8.11.1-devdoc" - }, - { - "name": "out", - "path": "/nix/store/5nr2v31bm8zdxky9jci5m7kjqc5dqjvz-curl-8.11.1" - } - ], - "store_path": "/nix/store/b4bfdh987ankyl4dl8hyqka0icbm7ch5-curl-8.11.1-bin" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "bin", - "path": "/nix/store/778ny8fq90spak0p7gwlfa78dxqj4x0s-curl-8.11.1-bin", - "default": true - }, - { - "name": "man", - "path": "/nix/store/w8wfvgpmpiyvmyn94x34bs2sl7w66iqk-curl-8.11.1-man", - "default": true - }, - { - "name": "dev", - "path": "/nix/store/j43v0gxd8n29mlm9v79fnwcnshc73kyg-curl-8.11.1-dev" - }, - { - "name": "devdoc", - "path": "/nix/store/mq7bgy6r8bjjj450b63p1l68k1pmr7lp-curl-8.11.1-devdoc" - }, - { - "name": "out", - "path": "/nix/store/05i854l3qagyd129f1fzg5ybz5fbs8lb-curl-8.11.1" - } - ], - "store_path": "/nix/store/778ny8fq90spak0p7gwlfa78dxqj4x0s-curl-8.11.1-bin" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "bin", - "path": "/nix/store/wzlmn9jq15pbw61nmqcy5nnwv6dhpq09-curl-8.11.1-bin", - "default": true - }, - { - "name": "man", - "path": "/nix/store/qabwv9rmysj9q6pxv9f85zavmw26y9dx-curl-8.11.1-man", - "default": true - }, - { - "name": "out", - "path": "/nix/store/gj70mjxd5zy310wq81znzr73rx6badsl-curl-8.11.1" - }, - { - "name": "debug", - "path": "/nix/store/nidhaw2lykjd4svj0xhh0a7p492r3n05-curl-8.11.1-debug" - }, - { - "name": "dev", - "path": "/nix/store/azfhp07yysbxvdhm78n43cjiwh1lb111-curl-8.11.1-dev" - }, - { - "name": "devdoc", - "path": "/nix/store/fb5dprl6p0rk1hv76wrg695yh2dzhrhv-curl-8.11.1-devdoc" - } - ], - "store_path": "/nix/store/wzlmn9jq15pbw61nmqcy5nnwv6dhpq09-curl-8.11.1-bin" - } - } - }, - "gh@latest": { - "last_modified": "2025-02-01T15:12:02Z", - "resolved": "github:NixOS/nixpkgs/102a39bfee444533e6b4e8611d7e92aa39b7bec1#gh", - "source": "devbox-search", - "version": "2.66.1", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/dvy3rk4jxx10ll5mhh0g8a260l2m1c1c-gh-2.66.1", - "default": true - } - ], - "store_path": "/nix/store/dvy3rk4jxx10ll5mhh0g8a260l2m1c1c-gh-2.66.1" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/k6dk8k0wi06k8wrphrkq8pypcql2jx9w-gh-2.66.1", - "default": true - } - ], - "store_path": "/nix/store/k6dk8k0wi06k8wrphrkq8pypcql2jx9w-gh-2.66.1" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/yhlzsr96n9a5807r8glh89mj66dbk58z-gh-2.66.1", - "default": true - } - ], - "store_path": "/nix/store/yhlzsr96n9a5807r8glh89mj66dbk58z-gh-2.66.1" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/3laqs1ys46ryva9mcmk1q5yvn1k3xhyc-gh-2.66.1", - "default": true - } - ], - "store_path": "/nix/store/3laqs1ys46ryva9mcmk1q5yvn1k3xhyc-gh-2.66.1" - } - } - }, "go-task@latest": { "last_modified": "2025-01-19T08:16:51Z", "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#go-task", @@ -221,51 +49,51 @@ } } }, - "go@1.23.4": { - "last_modified": "2025-01-19T08:16:51Z", - "resolved": "github:NixOS/nixpkgs/50165c4f7eb48ce82bd063e1fb8047a0f515f8ce#go", + "go@1.19": { + "last_modified": "2024-01-27T14:55:31Z", + "resolved": "github:NixOS/nixpkgs/160b762eda6d139ac10ae081f8f78d640dd523eb#go_1_19", "source": "devbox-search", - "version": "1.23.4", + "version": "1.19.13", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/lg7l1czmd5hqwwhjszprdz342q98zmkw-go-1.23.4", + "path": "/nix/store/2llf3dn2m48q6cral32bd8mk1g5pa48d-go-1.19.13", "default": true } ], - "store_path": "/nix/store/lg7l1czmd5hqwwhjszprdz342q98zmkw-go-1.23.4" + "store_path": "/nix/store/2llf3dn2m48q6cral32bd8mk1g5pa48d-go-1.19.13" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/2x7k6yj9z37s3d3x5j3kkz6bcak8k9qr-go-1.23.4", + "path": "/nix/store/5fysyzw0baj5kprrf94iz0p733c4b5w1-go-1.19.13", "default": true } ], - "store_path": "/nix/store/2x7k6yj9z37s3d3x5j3kkz6bcak8k9qr-go-1.23.4" + "store_path": "/nix/store/5fysyzw0baj5kprrf94iz0p733c4b5w1-go-1.19.13" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/8x0ag0v37rfy9z58a8yy3hakj3dzdr9c-go-1.23.4", + "path": "/nix/store/4zq38z7s23nn9pyibb4qkyb90bna9prr-go-1.19.13", "default": true } ], - "store_path": "/nix/store/8x0ag0v37rfy9z58a8yy3hakj3dzdr9c-go-1.23.4" + "store_path": "/nix/store/4zq38z7s23nn9pyibb4qkyb90bna9prr-go-1.19.13" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/0b6vsy4fa4i4qpk1011hi6251nwdg5y8-go-1.23.4", + "path": "/nix/store/ra7zh0hq3gzw7pdmb3ixvr5zd6h8qc9h-go-1.19.13", "default": true } ], - "store_path": "/nix/store/0b6vsy4fa4i4qpk1011hi6251nwdg5y8-go-1.23.4" + "store_path": "/nix/store/ra7zh0hq3gzw7pdmb3ixvr5zd6h8qc9h-go-1.19.13" } } }, diff --git a/go.mod b/go.mod index 35b05c23..c25980e1 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/algolia/cli -go 1.23.0 - -toolchain go1.23.4 +go 1.19 require ( github.com/AlecAivazis/survey/v2 v2.3.7