diff --git a/.betterer.results b/.betterer.results index 2edb5a615c0..81d2c506962 100644 --- a/.betterer.results +++ b/.betterer.results @@ -7347,6 +7347,9 @@ exports[`no undocumented stories`] = { "packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], + "packages/grafana-ui/src/components/Combobox/Combobox.story.tsx:5381": [ + [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] + ], "packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx:5381": [ [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"] ], diff --git a/.drone.yml b/.drone.yml index 57e6b2857d8..9438829a087 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,7 +25,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - ./bin/build verify-drone @@ -76,14 +76,14 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - go install github.com/bazelbuild/buildtools/buildifier@latest - buildifier --lint=warn -mode=check -r . depends_on: - compile-build-cmd - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: lint-starlark trigger: event: @@ -377,7 +377,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -386,21 +386,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend - commands: - apk add --update build-base @@ -409,7 +409,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend-integration trigger: event: @@ -461,7 +461,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - apk add --update curl jq bash @@ -488,16 +488,16 @@ steps: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-openapi-spec trigger: event: @@ -556,7 +556,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -566,7 +566,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -575,14 +575,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - yarn install --immutable || yarn install --immutable @@ -615,7 +615,7 @@ steps: from_secret: drone_token - commands: - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 - -a targz:grafana:linux/arm/v7 --go-version=1.23.0 --yarn-cache=$$YARN_CACHE_FOLDER + -a targz:grafana:linux/arm/v7 --go-version=1.23.1 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER --grafana-dir=$$PWD > packages.txt depends_on: - yarn-install @@ -868,7 +868,7 @@ steps: - /src/grafana-build artifacts -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER - --go-version=1.23.0 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.19.1 --tag-format='{{ + --go-version=1.23.1 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.19.1 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --grafana-dir=$$PWD --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' > docker.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i @@ -1012,7 +1012,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -1026,7 +1026,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1035,14 +1035,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -1063,7 +1063,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql57:3306 -timeout 120s @@ -1084,7 +1084,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql57 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-5.7-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -1105,7 +1105,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -1121,7 +1121,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -1137,7 +1137,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -1154,7 +1154,7 @@ steps: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 failure: ignore - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -1242,7 +1242,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue trigger: event: @@ -1283,7 +1283,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - apt-get update -yq && apt-get install shellcheck @@ -1355,7 +1355,7 @@ steps: environment: GITHUB_TOKEN: from_secret: github_token - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: swagger-gen trigger: event: @@ -1451,7 +1451,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1462,7 +1462,7 @@ steps: - CODEGEN_VERIFY=1 make gen-cue depends_on: - clone-enterprise - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1472,14 +1472,14 @@ steps: - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: - clone-enterprise - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base @@ -1487,7 +1487,7 @@ steps: - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: sqlite-benchmark-integration-tests - commands: - apk add --update build-base @@ -1499,7 +1499,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: postgres-benchmark-integration-tests - commands: - apk add --update build-base @@ -1510,7 +1510,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql57 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-5.7-benchmark-integration-tests - commands: - apk add --update build-base @@ -1521,7 +1521,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-8.0-benchmark-integration-tests trigger: event: @@ -1599,7 +1599,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue trigger: branch: main @@ -1772,7 +1772,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -1781,21 +1781,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend - commands: - apk add --update build-base @@ -1804,7 +1804,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend-integration trigger: branch: main @@ -1849,22 +1849,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-openapi-spec - commands: - ./bin/build verify-drone @@ -1981,7 +1981,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - '# It is required that code generated from Thema/CUE be committed and in sync @@ -1991,7 +1991,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2000,14 +2000,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - yarn install --immutable || yarn install --immutable @@ -2039,7 +2039,7 @@ steps: name: build-frontend-packages - commands: - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 - -a targz:grafana:linux/arm/v7 --go-version=1.23.0 --yarn-cache=$$YARN_CACHE_FOLDER + -a targz:grafana:linux/arm/v7 --go-version=1.23.1 --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER --grafana-dir=$$PWD > packages.txt depends_on: - update-package-json-version @@ -2328,7 +2328,7 @@ steps: - /src/grafana-build artifacts -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER - --go-version=1.23.0 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.19.1 --tag-format='{{ + --go-version=1.23.1 --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.19.1 --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --grafana-dir=$$PWD --ubuntu-tag-format='{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' > docker.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i @@ -2534,7 +2534,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -2548,7 +2548,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2557,14 +2557,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -2585,7 +2585,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql57:3306 -timeout 120s @@ -2606,7 +2606,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql57 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-5.7-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -2627,7 +2627,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -2643,7 +2643,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -2659,7 +2659,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -2676,7 +2676,7 @@ steps: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 failure: ignore - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: remote-alertmanager-integration-tests trigger: branch: main @@ -2986,7 +2986,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -2995,21 +2995,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend - commands: - apk add --update build-base @@ -3018,7 +3018,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend-integration trigger: branch: @@ -3061,22 +3061,22 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - apk add --update make - make gen-go depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - go run scripts/modowners/modowners.go check go.mod - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-modfile - commands: - apk add --update make - make swagger-validate - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: validate-openapi-spec trigger: branch: @@ -3166,7 +3166,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - echo $DRONE_RUNNER_NAME @@ -3180,7 +3180,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -3189,14 +3189,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -3217,7 +3217,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql57:3306 -timeout 120s @@ -3238,7 +3238,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql57 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-5.7-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -3259,7 +3259,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -3275,7 +3275,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -3291,7 +3291,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -3308,7 +3308,7 @@ steps: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 failure: ignore - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: remote-alertmanager-integration-tests trigger: branch: @@ -3411,7 +3411,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3542,7 +3542,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts docker fetch --edition oss @@ -3678,7 +3678,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - ./bin/build artifacts packages --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET} @@ -3763,7 +3763,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - yarn install --immutable || yarn install --immutable @@ -3879,8 +3879,8 @@ steps: - ' echo ''All attempts failed''' - ' exit 1' - ' fi' - - ' echo "Waiting 60 seconds before next attempt..."' - - ' sleep 60' + - ' echo "Waiting 30 seconds before next attempt..."' + - ' sleep 30' - ' fi' - done - 'echo "Step 6: Verifying Grafana installation..."' @@ -3928,8 +3928,8 @@ steps: - ' echo ''All attempts failed''' - ' exit 1' - ' fi' - - ' echo "Waiting 60 seconds before next attempt..."' - - ' sleep 60' + - ' echo "Waiting 30 seconds before next attempt..."' + - ' sleep 30' - ' fi' - done - ' echo "Verifying GPG key..."' @@ -3987,7 +3987,7 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - depends_on: - compile-build-cmd @@ -4056,8 +4056,8 @@ steps: - ' echo ''All attempts failed''' - ' exit 1' - ' fi' - - ' echo "Waiting 60 seconds before next attempt..."' - - ' sleep 60' + - ' echo "Waiting 30 seconds before next attempt..."' + - ' sleep 30' - ' fi' - done - 'echo "Step 6: Verifying Grafana installation..."' @@ -4106,8 +4106,8 @@ steps: - ' echo ''All attempts failed''' - ' exit 1' - ' fi' - - ' echo "Waiting 60 seconds before next attempt..."' - - ' sleep 60' + - ' echo "Waiting 30 seconds before next attempt..."' + - ' sleep 30' - ' fi' - done - ' echo "Verifying GPG key..."' @@ -4205,7 +4205,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4263,13 +4263,13 @@ steps: depends_on: [] environment: CGO_ENABLED: 0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: compile-build-cmd - commands: - ./bin/build whatsnew-checker depends_on: - compile-build-cmd - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: whats-new-checker trigger: event: @@ -4371,7 +4371,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4380,21 +4380,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend - commands: - apk add --update build-base @@ -4403,7 +4403,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend-integration trigger: event: @@ -4460,7 +4460,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4643,7 +4643,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4792,7 +4792,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -4801,21 +4801,21 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - apk add --update build-base shared-mime-info shared-mime-info-lang - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend - commands: - apk add --update build-base @@ -4824,7 +4824,7 @@ steps: | grep -o '\(.*\)/' | sort -u) depends_on: - wire-install - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: test-backend-integration trigger: cron: @@ -4879,7 +4879,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -5026,7 +5026,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -5115,7 +5115,7 @@ steps: - commands: - 'dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} ' - - --go-version=1.23.0 + - --go-version=1.23.1 environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -5136,7 +5136,7 @@ steps: from_secret: gcp_key_base64 GITHUB_TOKEN: from_secret: github_token - GO_VERSION: 1.23.0 + GO_VERSION: 1.23.1 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -5226,20 +5226,20 @@ steps: - commands: [] depends_on: - clone - image: golang:1.23.0-windowsservercore-1809 + image: golang:1.23.1-windowsservercore-1809 name: windows-init - commands: - go install github.com/google/wire/cmd/wire@v0.5.0 - wire gen -tags oss ./pkg/server depends_on: - windows-init - image: golang:1.23.0-windowsservercore-1809 + image: golang:1.23.1-windowsservercore-1809 name: wire-install - commands: - go test -short -covermode=atomic -timeout=5m ./pkg/... depends_on: - wire-install - image: golang:1.23.0-windowsservercore-1809 + image: golang:1.23.1-windowsservercore-1809 name: test-backend trigger: event: @@ -5332,7 +5332,7 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-cue depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-cue - commands: - '# It is required that generated jsonnet is committed and in sync with its inputs.' @@ -5341,14 +5341,14 @@ steps: - apk add --update make - CODEGEN_VERIFY=1 make gen-jsonnet depends_on: [] - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: verify-gen-jsonnet - commands: - apk add --update make - make gen-go depends_on: - verify-gen-cue - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: wire-install - commands: - dockerize -wait tcp://postgres:5432 -timeout 120s @@ -5369,7 +5369,7 @@ steps: GRAFANA_TEST_DB: postgres PGPASSWORD: grafanatest POSTGRES_HOST: postgres - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: postgres-integration-tests - commands: - dockerize -wait tcp://mysql57:3306 -timeout 120s @@ -5390,7 +5390,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql57 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-5.7-integration-tests - commands: - dockerize -wait tcp://mysql80:3306 -timeout 120s @@ -5411,7 +5411,7 @@ steps: environment: GRAFANA_TEST_DB: mysql MYSQL_HOST: mysql80 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: mysql-8.0-integration-tests - commands: - dockerize -wait tcp://redis:6379 -timeout 120s @@ -5427,7 +5427,7 @@ steps: - wait-for-redis environment: REDIS_URL: redis://redis:6379/0 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: redis-integration-tests - commands: - dockerize -wait tcp://memcached:11211 -timeout 120s @@ -5443,7 +5443,7 @@ steps: - wait-for-memcached environment: MEMCACHED_HOSTS: memcached:11211 - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: memcached-integration-tests - commands: - dockerize -wait tcp://mimir_backend:8080 -timeout 120s @@ -5460,7 +5460,7 @@ steps: AM_TENANT_ID: test AM_URL: http://mimir_backend:8080 failure: ignore - image: golang:1.23.0-alpine + image: golang:1.23.1-alpine name: remote-alertmanager-integration-tests trigger: event: @@ -5815,7 +5815,7 @@ steps: - commands: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.23.0-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.23.1-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:20.9.0-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:20-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 @@ -5852,7 +5852,7 @@ steps: - commands: - trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli - trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1 - - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.23.0-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.23.1-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:20.9.0-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:20-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 @@ -6108,6 +6108,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: 7c752913b444e0efe410d5a8a0d300e1b4d48d2cac8df602c35314bc62b7ac3c +hmac: 64d991988575d9d3a608d0be4fae0d5e6b903b015d9b060e3254b5107ccc4ad7 ... diff --git a/Dockerfile b/Dockerfile index ddc1398f7f2..098a40a938b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG BASE_IMAGE=alpine:3.19.1 ARG JS_IMAGE=node:20-alpine ARG JS_PLATFORM=linux/amd64 -ARG GO_IMAGE=golang:1.23.0-alpine +ARG GO_IMAGE=golang:1.23.1-alpine ARG GO_SRC=go-builder ARG JS_SRC=js-builder diff --git a/Makefile b/Makefile index 1f45d7385aa..b171aacd2d6 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ WIRE_TAGS = "oss" include .bingo/Variables.mk GO = go -GO_VERSION = 1.23.0 +GO_VERSION = 1.23.1 GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh) GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh) SH_FILES ?= $(shell find ./scripts -name *.sh) diff --git a/docs/sources/administration/roles-and-permissions/access-control/_index.md b/docs/sources/administration/roles-and-permissions/access-control/_index.md index d4aac02464b..3460f0e60f7 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/_index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/_index.md @@ -13,12 +13,92 @@ labels: menuTitle: Role-based access control (RBAC) title: Grafana Role-based access control (RBAC) weight: 120 +refs: + api-rbac: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/ + rbac-role-definitions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-fixed-basic-role-definitions/ + rbac-role-definitions-basic-role-assignments: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/#basic-role-assignments + - pattern: /docs/grafana-cloud/ + rbac-manage-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/ + rbac-assign-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/assign-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/assign-rbac-roles/ + rbac-basic-role-uid-mapping: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/#list-permissions-associated-with-roles + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/#list-permissions-associated-with-roles + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ + alerting: + - pattern: /docs/grafana/ + destination: /docs/grafana//alerting/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/alerting/ + data-sources: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/ + roles-and-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/ + dashboards: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/ + dashboards-annotate-visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations/ + dashboards-create-reports: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/create-reports/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/create-reports/ + dashboards-manage-library-panels: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/manage-library-panels/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/manage-library-panels/ + dashboards-create-a-dashboard-folder: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/manage-dashboards/#create-a-dashboard-folder + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/manage-dashboards/#create-a-dashboard-folder + folder-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/manage-dashboards/#folder-permissions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/manage-dashboards/#folder-permissions --- # Role-based access control (RBAC) {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} RBAC provides a standardized way of granting, changing, and revoking access when it comes to viewing and modifying Grafana resources, such as dashboards, reports, and administrative settings. @@ -43,7 +123,7 @@ RBAC roles contain multiple permissions, each of which has an action and a scope - **Action:** `datasources:read` - **Scope:** `datasources:*` -For information on the RBAC API refer to [RBAC API](https://grafana.com/docs/grafana//developers/http_api/access_control/#rbac-api). +For information on the RBAC API refer to [RBAC API](ref:api-rbac). ### Basic roles @@ -77,8 +157,8 @@ You can use RBAC to modify the permissions associated with any basic role, which Note that any modification to any of these basic role is not propagated to the other basic roles. For example, if you modify Viewer basic role and grant additional permission, Editors or Admins won't have that additional grant. -For more information about the permissions associated with each basic role, refer to [Basic role definitions]({{< relref "./rbac-fixed-basic-role-definitions/#basic-role-assignments" >}}). -To interact with the API and view or modify basic roles permissions, refer to [the table]({{< relref "./manage-rbac-roles/#basic-role-uid-mapping" >}}) that maps basic role names to the associated UID. +For more information about the permissions associated with each basic role, refer to [Basic role definitions](ref:rbac-role-definitions-basic-role-assignments). +To interact with the API and view or modify basic roles permissions, refer to [the table](ref:rbac-basic-role-uid-mapping) that maps basic role names to the associated UID. {{% admonition type="note" %}} You cannot use a service account to modify basic roles via the RBAC API. To update basic roles, you must be a Grafana administrator and use basic authentication with the request. @@ -86,31 +166,31 @@ You cannot use a service account to modify basic roles via the RBAC API. To upda ### Fixed roles -Grafana Enterprise includes the ability for you to assign discrete fixed roles to users, teams, and service accounts. This gives you fine-grained control over user permissions than you would have with basic roles alone. These roles are called "fixed" because you cannot change or delete fixed roles. You can also create _custom_ roles of your own; see more information in the [custom roles section]({{< relref "#custom-roles" >}}) below. +Grafana Enterprise includes the ability for you to assign discrete fixed roles to users, teams, and service accounts. This gives you fine-grained control over user permissions than you would have with basic roles alone. These roles are called "fixed" because you cannot change or delete fixed roles. You can also create _custom_ roles of your own; see more information in the [custom roles section](#custom-roles) below. Assign fixed roles when the basic roles do not meet your permission requirements. For example, you might want a user with the basic viewer role to also edit dashboards. Or, you might want anyone with the editor role to also add and manage users. Fixed roles provide users more granular access to create, view, and update the following Grafana resources: -- [Alerting]({{< relref "../../../alerting/" >}}) -- [Annotations]({{< relref "../../../dashboards/build-dashboards/annotate-visualizations" >}}) +- [Alerting](ref:alerting) +- [Annotations](ref:dashboards-annotate-visualizations) - [API keys](/docs/grafana//administration/service-accounts/migrate-api-keys/) -- [Dashboards and folders]({{< relref "../../../dashboards/" >}}) -- [Data sources]({{< relref "../../../datasources/" >}}) -- [Explore]({{< relref "../../../explore/" >}}) -- [Feature Toggles]({{< relref "../../feature-toggles/" >}}) -- [Folders]({{< relref "../../../dashboards/manage-dashboards/#create-a-dashboard-folder" >}}) -- [LDAP]({{< relref "../../../setup-grafana/configure-security/configure-authentication/ldap/" >}}) -- [Library panels]({{< relref "../../../dashboards/build-dashboards/manage-library-panels" >}}) -- [Licenses]({{< relref "../../stats-and-license/" >}}) -- [Organizations]({{< relref "../../organization-management/" >}}) -- [Provisioning]({{< relref "../../provisioning/" >}}) -- [Reports]({{< relref "../../../dashboards/create-reports/" >}}) -- [Roles]({{< relref "../../" >}}) -- [Settings]({{< relref "../../../setup-grafana/configure-grafana/settings-updates-at-runtime" >}}) -- [Service accounts]({{< relref "../../service-accounts/" >}}) -- [Teams]({{< relref "../../team-management/" >}}) -- [Users]({{< relref "../../user-management/" >}}) +- [Dashboards and folders](ref:dashboards) +- [Data sources](ref:data-sources) +- [Explore](/docs/grafana//explore/) +- [Feature Toggles](/docs/grafana//administration/feature-toggles/) +- [Folders](ref:dashboards-create-a-dashboard-folder) +- [LDAP](/docs/grafana//setup-grafana/configure-security/configure-authentication/ldap/) +- [Library panels](ref:dashboards-manage-library-panels) +- [Licenses](/docs/grafana//administration/stats-and-license/) +- [Organizations](/docs/grafana//administration/organization-management/) +- [Provisioning](/docs/grafana//administration/provisioning/) +- [Reports](ref:dashboards-create-reports) +- [Roles](ref:roles-and-permissions) +- [Settings](/docs/grafana//setup-grafana/configure-grafana/settings-updates-at-runtime/) +- [Service accounts](ref:service-accounts) +- [Teams](/docs/grafana//administration/team-management/) +- [Users](/docs/grafana//administration/user-management/) -To learn more about the permissions you can grant for each resource, refer to [RBAC role definitions]({{< relref "./rbac-fixed-basic-role-definitions/" >}}). +To learn more about the permissions you can grant for each resource, refer to [RBAC role definitions](ref:rbac-role-definitions). ### Custom roles @@ -126,11 +206,11 @@ Consider creating a custom role when fixed roles do not meet your permissions re You can use either of the following methods to create, assign, and manage custom roles: -- Grafana provisioning: You can use a YAML file to configure roles. For more information about using provisioning to create custom roles, refer to [Manage RBAC roles]({{< relref "./manage-rbac-roles/" >}}). For more information about using provisioning to assign RBAC roles to users or teams, refer to [Assign RBAC roles]({{< relref "./assign-rbac-roles/" >}}). -- RBAC API: As an alternative, you can use the Grafana HTTP API to create and manage roles. For more information about the HTTP API, refer to [RBAC API]({{< relref "../../../developers/http_api/access_control/" >}}). +- Grafana provisioning: You can use a YAML file to configure roles. For more information about using provisioning to create custom roles, refer to [Manage RBAC roles](ref:rbac-manage-rbac-roles). For more information about using provisioning to assign RBAC roles to users or teams, refer to [Assign RBAC roles](ref:rbac-assign-rbac-roles). +- RBAC API: As an alternative, you can use the Grafana HTTP API to create and manage roles. For more information about the HTTP API, refer to [RBAC API](ref:api-rbac). ### Limitation If you have created a folder with the name `General` or `general`, you cannot manage its permissions with RBAC. -If you set [folder permissions]({{< relref "../../user-management/manage-dashboard-permissions/#grant-dashboard-folder-permissions" >}}) for a folder named `General` or `general`, the system disregards the folder when RBAC is enabled. +If you set [folder permissions](ref:folder-permissions) for a folder named `General` or `general`, the system disregards the folder when RBAC is enabled. diff --git a/docs/sources/administration/roles-and-permissions/access-control/assign-rbac-roles/index.md b/docs/sources/administration/roles-and-permissions/access-control/assign-rbac-roles/index.md index 5f442a00dfa..0cca046cbdf 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/assign-rbac-roles/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/assign-rbac-roles/index.md @@ -11,12 +11,38 @@ labels: menuTitle: Assign RBAC roles title: Assign Grafana RBAC roles weight: 40 +refs: + custom-role-actions-scopes: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/custom-role-actions-scopes/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/custom-role-actions-scopes/ + rbac-grafana-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-grafana-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-grafana-provisioning/ + manage-rbac-roles-create-custom-roles-using-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/#create-custom-roles-using-provisioning + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/#create-custom-roles-using-provisioning + plan-rbac-rollout-strategy: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/plan-rbac-rollout-strategy/ + rbac-role-definitions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-fixed-basic-role-definitions/ --- # Assign RBAC roles {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} In this topic you'll learn how to use the role picker, provisioning, and the HTTP API to assign fixed and custom roles to users and teams. @@ -34,10 +60,10 @@ In both cases, the assignment applies only to the user, team or service account **Before you begin:** -- [Plan your RBAC rollout strategy]({{< relref "./plan-rbac-rollout-strategy/" >}}). +- [Plan your RBAC rollout strategy](ref:plan-rbac-rollout-strategy). - Identify the fixed roles that you want to assign to the user, team or service account. - For more information about available fixed roles, refer to [RBAC role definitions]({{< relref "./rbac-fixed-basic-role-definitions/" >}}). + For more information about available fixed roles, refer to [RBAC role definitions](ref:rbac-role-definitions). - Ensure that your own user account has the correct permissions: - If you are assigning permissions to a user, team or service account within an organization, you must have organization administrator or server administrator permissions. @@ -52,7 +78,7 @@ In both cases, the assignment applies only to the user, team or service account 1. Sign in to Grafana. 2. Switch to the organization that contains the user, team or service account. - For more information about switching organizations, refer to [Switch organizations]({{< relref "../../../user-management/user-preferences/_index.md#switch-organizations" >}}). + For more information about switching organizations, refer to [Switch organizations](/docs/grafana//administration/user-management/user-preferences/#switch-organizations). 3. In the left-side menu, click **Administration**, **Users and access**, and then **Users**, **Teams**, or **Service accounts**. 4. In the **Role** column, select the fixed role that you want to assign to the user, team, or service account. @@ -73,8 +99,8 @@ Instead of using the Grafana role picker, you can use file-based provisioning to **Before you begin:** -- Refer to [Role provisioning]({{< relref "./rbac-grafana-provisioning/" >}}) -- Ensure that the team to which you are adding the fixed role exists. For more information about creating teams, refer to [Manage teams]({{< relref "../../../team-management/" >}}) +- Refer to [Role provisioning](ref:rbac-grafana-provisioning) +- Ensure that the team to which you are adding the fixed role exists. For more information about creating teams, refer to [Manage teams](/docs/grafana//administration/team-management/) **To assign a role to a team:** @@ -82,25 +108,25 @@ Instead of using the Grafana role picker, you can use file-based provisioning to 1. Refer to the following table to add attributes and values. - | Attribute | Description | - | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `roles` | Enter the custom role or custom roles you want to create/update. | - | `roles > name` | Enter the name of the custom role. | - | `roles > version` | Enter the custom role version number. Role assignments are independent of the role version number. | - | `roles > global` | Enter `true`. You can specify the `orgId` otherwise. | - | `roles > permissions` | Enter the permissions `action` and `scope` values. For more information about permissions actions and scopes, refer to [RBAC permissions, actions, and scopes]({{< relref "./custom-role-actions-scopes/" >}}) | - | `teams` | Enter the team or teams to which you are adding the custom role. | - | `teams > orgId` | Because teams belong to organizations, you must add the `orgId` value. | - | `teams > name` | Enter the name of the team. | - | `teams > roles` | Enter the custom or fixed role or roles that you want to grant to the team. | - | `teams > roles > name` | Enter the name of the role. | - | `teams > roles > global` | Enter `true`, or specify `orgId` of the role you want to assign to the team. Fixed roles are global. | + | Attribute | Description | + | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `roles` | Enter the custom role or custom roles you want to create/update. | + | `roles > name` | Enter the name of the custom role. | + | `roles > version` | Enter the custom role version number. Role assignments are independent of the role version number. | + | `roles > global` | Enter `true`. You can specify the `orgId` otherwise. | + | `roles > permissions` | Enter the permissions `action` and `scope` values. For more information about permissions actions and scopes, refer to [RBAC permissions, actions, and scopes](ref:custom-role-actions-scopes) | + | `teams` | Enter the team or teams to which you are adding the custom role. | + | `teams > orgId` | Because teams belong to organizations, you must add the `orgId` value. | + | `teams > name` | Enter the name of the team. | + | `teams > roles` | Enter the custom or fixed role or roles that you want to grant to the team. | + | `teams > roles > name` | Enter the name of the role. | + | `teams > roles > global` | Enter `true`, or specify `orgId` of the role you want to assign to the team. Fixed roles are global. | - For more information about managing custom roles, refer to [Create custom roles using provisioning]({{< relref "./manage-rbac-roles/#create-custom-roles-using-provisioning" >}}). + For more information about managing custom roles, refer to [Create custom roles using provisioning](ref:manage-rbac-roles-create-custom-roles-using-provisioning). 1. Reload the provisioning configuration file. - For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations]({{< relref "../../../../developers/http_api/admin/#reload-provisioning-configurations" >}}). + For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations](/docs/grafana//developers/http_api/admin/#reload-provisioning-configurations). The following example creates the `custom:users:writer` role and assigns it to the `user writers` and `user admins` teams along with the `fixed:users:writer` role: diff --git a/docs/sources/administration/roles-and-permissions/access-control/configure-rbac/index.md b/docs/sources/administration/roles-and-permissions/access-control/configure-rbac/index.md index c5e22b1f79e..210c4f217a4 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/configure-rbac/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/configure-rbac/index.md @@ -14,10 +14,10 @@ weight: 30 # Configure RBAC in Grafana {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} -The table below describes all RBAC configuration options. Like any other Grafana configuration, you can apply these options as [environment variables]({{< relref "../../../../setup-grafana/configure-grafana/#configure-with-environment-variables" >}}). +The table below describes all RBAC configuration options. Like any other Grafana configuration, you can apply these options as [environment variables](/docs/grafana//setup-grafana/configure-grafana/#override-configuration-with-environment-variables). | Setting | Required | Description | Default | | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | diff --git a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md index 35d8a9a572a..82b5269f979 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/custom-role-actions-scopes/index.md @@ -10,6 +10,17 @@ labels: menuTitle: RBAC permissions, actions, and scopes title: Grafana RBAC permissions, actions, and scopes weight: 80 +refs: + rbac-grafana-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-grafana-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-grafana-provisioning/ + rbac-fixed-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/#fixed-roles + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/#fixed-roles --- # RBAC permissions, actions, and scopes @@ -20,7 +31,7 @@ Available in [Grafana Enterprise](/docs/grafana//introduction/g A permission is comprised of an action and a scope. When creating a custom role, consider the actions the user can perform and the resources on which they can perform those actions. -To learn more about the Grafana resources to which you can apply RBAC, refer to [Resources with RBAC permissions](/docs/grafana//administration/roles-and-permissions/access-control/#fixed-roles). +To learn more about the Grafana resources to which you can apply RBAC, refer to [Resources with RBAC permissions](ref:rbac-fixed-roles). - **Action:** An action describes what tasks a user can perform on a resource. - **Scope:** A scope describes where an action can be performed, such as reading a specific user profile. In this example, a permission is associated with the scope `users:` to the relevant role. @@ -238,7 +249,7 @@ The following list contains role-based access control scopes. |
  • `permissions:type:delegate`
    • | The scope is only applicable for roles associated with the Access Control itself and indicates that you can delegate your permissions only, or a subset of it, by creating a new role or making an assignment. | |
      • `permissions:type:escalate`
        • | The scope is required to trigger the reset of basic roles permissions. It indicates that users might acquire additional permissions they did not previously have. | |
          • `plugins:*`
          • `plugins:id:*`
          | Restrict an action to a set of plugins. For example, `plugins:id:grafana-oncall-app` matches Grafana OnCall plugin, and `plugins:*` matches all plugins. | -|
          • `provisioners:*`
            • | Restrict an action to a set of provisioners. For example, `provisioners:*` matches any provisioner, and `provisioners:accesscontrol` matches the role-based access control [provisioner]({{< relref "./rbac-grafana-provisioning/" >}}). | +|
              • `provisioners:*`
                • | Restrict an action to a set of provisioners. For example, `provisioners:*` matches any provisioner, and `provisioners:accesscontrol` matches the role-based access control [provisioner](ref:rbac-grafana-provisioning). | |
                  • `reports:*`
                  • `reports:id:*`
                  | Restrict an action to a set of reports. For example, `reports:*` matches any report and `reports:id:1` matches the report whose ID is `1`. | |
                  • `roles:*`
                  • `roles:uid:*`
                  | Restrict an action to a set of roles. For example, `roles:*` matches any role and `roles:uid:randomuid` matches only the role whose UID is `randomuid`. | |
                  • `services:accesscontrol`
                    • | Restrict an action to target only the role-based access control service. You can use this in conjunction with the `status:accesscontrol` actions. | diff --git a/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md b/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md index f2dc6479783..ab631eb44c2 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md @@ -12,12 +12,66 @@ labels: menuTitle: Manage RBAC roles title: Manage Grafana RBAC roles weight: 50 +refs: + configure-rbac-configure-rbac-in-grafana: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/configure-rbac/#configure-rbac-in-grafana + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/configure-rbac/#configure-rbac-in-grafana + api-rbac-reset-basic-roles-to-their-default: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#reset-basic-roles-to-their-default + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#reset-basic-roles-to-their-default + api-rbac-delete-a-custom-role: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#delete-a-custom-role + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#delete-a-custom-role + api-rbac-update-a-role: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#update-a-role + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#update-a-role + api-rbac-get-a-role: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#get-a-role + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#get-a-role + api-rbac-create-a-new-custom-role: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#create-a-new-custom-role + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#create-a-new-custom-role + plan-rbac-rollout-strategy: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/plan-rbac-rollout-strategy/ + custom-role-actions-scopes: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/custom-role-actions-scopes/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/custom-role-actions-scopes/ + rbac-grafana-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-grafana-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-grafana-provisioning/ + rbac-role-definitions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-fixed-basic-role-definitions/ + rbac-fixed-basic-role-definitions-basic-role-assignments: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/#basic-role-assignments --- # Manage RBAC roles {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} {{< table-of-contents >}} @@ -28,7 +82,7 @@ The following example includes the base64 username:password Basic Authorization. ## List permissions associated with roles -Use a `GET` command to see the actions and scopes associated with a role. For more information about seeing a list of permissions for each role, refer to [Get a role]({{< relref "../../../../developers/http_api/access_control/#get-a-role" >}}). +Use a `GET` command to see the actions and scopes associated with a role. For more information about seeing a list of permissions for each role, refer to [Get a role](ref:api-rbac-get-a-role). To see the permissions associated with basic roles, refer to the following basic role UIDs: @@ -87,7 +141,7 @@ curl --location --request GET '/api/access-control/roles/qQui_LCMk' } ``` -Refer to the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#get-a-role" >}}) for more details. +Refer to the [RBAC HTTP API](ref:api-rbac-get-a-role) for more details. ## Create custom roles @@ -97,40 +151,40 @@ Create a custom role when basic roles and fixed roles do not meet your permissio **Before you begin:** -- [Plan your RBAC rollout strategy]({{< relref "./plan-rbac-rollout-strategy/" >}}). -- Determine which permissions you want to add to the custom role. To see a list of actions and scope, refer to [RBAC permissions, actions, and scopes]({{< relref "./custom-role-actions-scopes/" >}}). -- [Enable role provisioning]({{< relref "./rbac-grafana-provisioning/" >}}). +- [Plan your RBAC rollout strategy](ref:plan-rbac-rollout-strategy). +- Determine which permissions you want to add to the custom role. To see a list of actions and scope, refer to [RBAC permissions, actions, and scopes](ref:custom-role-actions-scopes). +- [Enable role provisioning](ref:rbac-grafana-provisioning). - Ensure that you have permissions to create a custom role. - By default, the Grafana Admin role has permission to create custom roles. - A Grafana Admin can delegate the custom role privilege to another user by creating a custom role with the relevant permissions and adding the `permissions:type:delegate` scope. ### Create custom roles using provisioning -[File-based provisioning]({{< relref "./rbac-grafana-provisioning" >}}) is one method you can use to create custom roles. +[File-based provisioning](ref:rbac-grafana-provisioning) is one method you can use to create custom roles. 1. Open the YAML configuration file and locate the `roles` section. 1. Refer to the following table to add attributes and values. -| Attribute | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `name` | A human-friendly identifier for the role that helps administrators understand the purpose of a role. `name` is required and cannot be longer than 190 characters. We recommend that you use ASCII characters. Role names must be unique within an organization. | -| `uid` | A unique identifier associated with the role. The UID enables you to change or delete the role. You can either generate a UID yourself, or let Grafana generate one for you. You cannot use the same UID within the same Grafana instance. | -| `orgId` | Identifies the organization to which the role belongs. The [default org ID]({{< relref "../../../../setup-grafana/configure-grafana/#auto_assign_org_id" >}}) is used if you do not specify `orgId`. | -| `global` | Global roles are not associated with any specific organization, which means that you can reuse them across all organizations. This setting overrides `orgId`. | -| `displayName` | Human-friendly text that is displayed in the UI. Role display name cannot be longer than 190 ASCII-based characters. For fixed roles, the display name is shown as specified. If you do not set a display name the display name replaces `':'` (a colon) with `' '` (a space). | -| `description` | Human-friendly text that describes the permissions a role provides. | -| `group` | Organizes roles in the role picker. | -| `version` | A positive integer that defines the current version of the role, which prevents overwriting newer changes. | -| `hidden` | Hidden roles do not appear in the role picker. | -| `state` | State of the role. Defaults to `present`, but if set to `absent` the role will be removed. | -| `force` | Can be used in addition to state `absent`, to force the removal of a role and all its assignments. | -| `from` | An optional list of roles from which you want to copy permissions. | -| `permissions` | Provides users access to Grafana resources. For a list of permissions, refer to [RBAC permissions actions and scopes]({{< relref "./rbac-fixed-basic-role-definitions/" >}}). If you do not know which permissions to assign, you can create and assign roles without any permissions as a placeholder. Using the `from` attribute, you can specify additional permissions or permissions to remove by adding a `state` to your permission list. | +| Attribute | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | A human-friendly identifier for the role that helps administrators understand the purpose of a role. `name` is required and cannot be longer than 190 characters. We recommend that you use ASCII characters. Role names must be unique within an organization. | +| `uid` | A unique identifier associated with the role. The UID enables you to change or delete the role. You can either generate a UID yourself, or let Grafana generate one for you. You cannot use the same UID within the same Grafana instance. | +| `orgId` | Identifies the organization to which the role belongs. The [default org ID](/docs/grafana//setup-grafana/configure-grafana/#auto_assign_org_id) is used if you do not specify `orgId`. | +| `global` | Global roles are not associated with any specific organization, which means that you can reuse them across all organizations. This setting overrides `orgId`. | +| `displayName` | Human-friendly text that is displayed in the UI. Role display name cannot be longer than 190 ASCII-based characters. For fixed roles, the display name is shown as specified. If you do not set a display name the display name replaces `':'` (a colon) with `' '` (a space). | +| `description` | Human-friendly text that describes the permissions a role provides. | +| `group` | Organizes roles in the role picker. | +| `version` | A positive integer that defines the current version of the role, which prevents overwriting newer changes. | +| `hidden` | Hidden roles do not appear in the role picker. | +| `state` | State of the role. Defaults to `present`, but if set to `absent` the role will be removed. | +| `force` | Can be used in addition to state `absent`, to force the removal of a role and all its assignments. | +| `from` | An optional list of roles from which you want to copy permissions. | +| `permissions` | Provides users access to Grafana resources. For a list of permissions, refer to [RBAC permissions actions and scopes](ref:rbac-role-definitions). If you do not know which permissions to assign, you can create and assign roles without any permissions as a placeholder. Using the `from` attribute, you can specify additional permissions or permissions to remove by adding a `state` to your permission list. | 1. Reload the provisioning configuration file. - For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations]({{< relref "../../../../developers/http_api/admin/#reload-provisioning-configurations" >}}). + For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations](/docs/grafana//developers/http_api/admin/#reload-provisioning-configurations). The following example creates a local role: @@ -199,7 +253,7 @@ roles: ### Create custom roles using the HTTP API -The following examples show you how to create a custom role using the Grafana HTTP API. For more information about the HTTP API, refer to [Create a new custom role]({{< relref "../../../../developers/http_api/access_control/#create-a-new-custom-role" >}}). +The following examples show you how to create a custom role using the Grafana HTTP API. For more information about the HTTP API, refer to [Create a new custom role](ref:api-rbac-create-a-new-custom-role). {{% admonition type="note" %}} You cannot create a custom role with permissions that you do not have. For example, if you only have `users:create` permissions, then you cannot create a role that includes other permissions. @@ -250,7 +304,7 @@ curl --location --request POST '/api/access-control/roles/' \ } ``` -Refer to the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#create-a-new-custom-role" >}}) for more details. +Refer to the [RBAC HTTP API](ref:api-rbac-create-a-new-custom-role) for more details. ## Update basic role permissions @@ -258,7 +312,7 @@ If the default basic role definitions do not meet your requirements, you can cha **Before you begin:** -- Determine the permissions you want to add or remove from a basic role. For more information about the permissions associated with basic roles, refer to [RBAC role definitions]({{< relref "./rbac-fixed-basic-role-definitions/#basic-role-assignments" >}}). +- Determine the permissions you want to add or remove from a basic role. For more information about the permissions associated with basic roles, refer to [RBAC role definitions](ref:rbac-fixed-basic-role-definitions-basic-role-assignments). {{% admonition type="note" %}} You cannot modify the `No Basic Role` permissions. @@ -281,7 +335,7 @@ You cannot modify the `No Basic Role` permissions. 1. Reload the provisioning configuration file. - For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations]({{< relref "../../../../developers/http_api/admin/#reload-provisioning-configurations" >}}). + For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations](/docs/grafana//developers/http_api/admin/#reload-provisioning-configurations). The following example modifies the `Grafana Admin` basic role permissions. @@ -322,7 +376,7 @@ You can add multiple `fixed`, `basic` or `custom` roles to the `from` section. T Make sure to **increment** the role version for the changes to be accounted for. {{% /admonition %}} -You can also change basic roles' permissions using the API. Refer to the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#update-a-role" >}}) for more details. +You can also change basic roles' permissions using the API. Refer to the [RBAC HTTP API](ref:api-rbac-update-a-role) for more details. ## Reset basic roles to their default @@ -336,7 +390,7 @@ You have two options to reset the basic roles permissions to their default. > Warning: If this option is left to true, permissions will be reset on every boot. -Use the [reset_basic_roles]({{< relref "../configure-rbac/#configure-rbac-in-grafana" >}}) option to reset +Use the [reset_basic_roles](ref:configure-rbac-configure-rbac-in-grafana) option to reset basic roles permissions to their default on Grafana instance boot up. 1. Open you configuration file and update the rbac section as follow: @@ -369,7 +423,7 @@ An alternative to the configuration option is to use the HTTP endpoint. scope: 'permissions:type:escalate' ``` -1. As a `Grafana Admin`, call the API endpoint to reset the basic roles to their default. Refer to the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#reset-basic-roles-to-their-default" >}}) for more details. +1. As a `Grafana Admin`, call the API endpoint to reset the basic roles to their default. Refer to the [RBAC HTTP API](ref:api-rbac-reset-basic-roles-to-their-default) for more details. ## Delete a custom role using Grafana provisioning @@ -395,7 +449,7 @@ Delete a custom role when you no longer need it. When you delete a custom role, 1. Reload the provisioning configuration file. - For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations]({{< relref "../../../../developers/http_api/admin/#reload-provisioning-configurations" >}}). + For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations](/docs/grafana//developers/http_api/admin/#reload-provisioning-configurations). The following example deletes a custom role: @@ -410,4 +464,4 @@ roles: force: true ``` -You can also delete a custom role using the API. Refer to the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#delete-a-custom-role" >}}) for more details. +You can also delete a custom role using the API. Refer to the [RBAC HTTP API](ref:api-rbac-delete-a-custom-role) for more details. diff --git a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md index 0672a5bb697..09c2fc4c3d4 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/index.md @@ -11,12 +11,38 @@ labels: menuTitle: Plan your RBAC rollout strategy title: Plan your Grafana RBAC rollout strategy weight: 20 +refs: + api-rbac-update-a-role: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#update-a-role + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#update-a-role + rbac-fixed-basic-role-definitions-basic-role-assignments: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/#basic-role-assignments + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-fixed-basic-role-definitions/#basic-role-assignments + rbac-fixed-basic-role-definitions-fixed-role-definitions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/#fixed-role-definitions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-fixed-basic-role-definitions/#fixed-role-definitions + manage-rbac-roles-update-basic-role-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/#update-basic-role-permissions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/#update-basic-role-permissions + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ --- # Plan your RBAC rollout strategy {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} An RBAC rollout strategy helps you determine _how_ you want to implement RBAC prior to assigning RBAC roles to users and teams. @@ -35,8 +61,8 @@ As a first step in determining your permissions rollout strategy, we recommend t To learn more about basic roles and fixed roles, refer to the following documentation: -- [Basic role definitions]({{< relref "./rbac-fixed-basic-role-definitions/#basic-role-assignments" >}}) -- [Fixed role definitions]({{< relref "./rbac-fixed-basic-role-definitions/#fixed-role-definitions" >}}) +- [Basic role definitions](ref:rbac-fixed-basic-role-definitions-basic-role-assignments) +- [Fixed role definitions](ref:rbac-fixed-basic-role-definitions-fixed-role-definitions) ## User and team considerations @@ -56,7 +82,7 @@ For example: 1. Map SAML, LDAP, or Oauth roles to Grafana basic roles (viewer, editor, or admin). -2. Use the Grafana Enterprise team sync feature to synchronize teams from your SAML, LDAP, or Oauth provider to Grafana. For more information about team sync, refer to [Team sync]({{< relref "../../../../setup-grafana/configure-security/configure-team-sync/" >}}). +2. Use the Grafana Enterprise team sync feature to synchronize teams from your SAML, LDAP, or Oauth provider to Grafana. For more information about team sync, refer to [Team sync](/docs/grafana//setup-grafana/configure-security/configure-team-sync/). 3. Within Grafana, assign RBAC permissions to users and teams. @@ -67,7 +93,7 @@ Consider the following guidelines when you determine if you should modify basic - **Modify basic roles** when Grafana's definitions of what viewers, editors, and admins can do does not match your definition of these roles. You can add or remove permissions from any basic role. {{% admonition type="note" %}} - Changes that you make to basic roles impact the role definition for all [organizations]({{< relref "../../../organization-management/" >}}) in the Grafana instance. For example, when you add the `fixed:users:writer` role's permissions to the viewer basic role, all viewers in any org in the Grafana instance can create users within that org. + Changes that you make to basic roles impact the role definition for all [organizations](/docs/grafana//administration/organization-management/) in the Grafana instance. For example, when you add the `fixed:users:writer` role's permissions to the viewer basic role, all viewers in any org in the Grafana instance can create users within that org. {{% /admonition %}} {{% admonition type="note" %}} @@ -97,13 +123,13 @@ If you have a use case that you'd like to share, feel free to contribute to this 1. In Grafana, create a team with the name `Internal employees`. 1. Assign the `fixed:datasources:explorer` role to the `Internal employees` team. -1. Add internal employees to the `Internal employees` team, or map them from a SAML, LDAP, or Oauth team using [Team Sync]({{< relref "../../../../setup-grafana/configure-security/configure-team-sync/" >}}). +1. Add internal employees to the `Internal employees` team, or map them from a SAML, LDAP, or Oauth team using [Team Sync](/docs/grafana//setup-grafana/configure-security/configure-team-sync/). 1. Assign the viewer role to both internal employees and contractors. ### Limit viewer, editor, or admin permissions 1. Review the list of permissions associated with the basic role. -1. [Change the permissions of the basic role]({{< relref "./manage-rbac-roles/#update-basic-role-permissions" >}}). +1. [Change the permissions of the basic role](ref:manage-rbac-roles-update-basic-role-permissions). ### Allow only members of one team to manage Alerts @@ -168,9 +194,9 @@ curl --location --request POST '/api/access-control/roles/' \ ### Enable an editor to create custom roles -By default, only a Grafana Server Admin can create and manage custom roles. If you want your `Editors` to do the same, [update the `Editor` basic role permissions]({{< ref "./manage-rbac-roles.md#update-basic-role-permissions" >}}). There are two ways to achieve this: +By default, only a Grafana Server Admin can create and manage custom roles. If you want your `Editors` to do the same, [update the `Editor` basic role permissions](ref:manage-rbac-roles-update-basic-role-permissions). There are two ways to achieve this: -- Add the following permissions to the `basic:editor` role, using provisioning or the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#update-a-role" >}}): +- Add the following permissions to the `basic:editor` role, using provisioning or the [RBAC HTTP API](ref:api-rbac-update-a-role): | action | scope | | -------------- | --------------------------- | @@ -212,9 +238,9 @@ By default, only a Grafana Server Admin can create and manage custom roles. If y ### Enable viewers to create reports -If you want your `Viewers` to create reports, [update the `Viewer` basic role permissions]({{< ref "./manage-rbac-roles.md#update-basic-role-permissions" >}}). There are two ways to achieve this: +If you want your `Viewers` to create reports, [update the `Viewer` basic role permissions](ref:manage-rbac-roles-update-basic-role-permissions). There are two ways to achieve this: -- Add the following permissions to the `basic:viewer` role, using provisioning or the [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#update-a-role" >}}): +- Add the following permissions to the `basic:viewer` role, using provisioning or the [RBAC HTTP API](ref:api-rbac-update-a-role): | Action | Scope | | ---------------- | ------------------------------- | @@ -253,11 +279,11 @@ If you want your `Viewers` to create reports, [update the `Viewer` basic role pe global: true ``` -> **Note:** The `fixed:reports:writer` role assigns more permissions than just creating reports. For more information about fixed role permission assignments, refer to [Fixed role definitions]({{< relref "./rbac-fixed-basic-role-definitions/#fixed-role-definitions" >}}). +> **Note:** The `fixed:reports:writer` role assigns more permissions than just creating reports. For more information about fixed role permission assignments, refer to [Fixed role definitions](ref:rbac-fixed-basic-role-definitions-fixed-role-definitions). ### Prevent a Grafana Admin from creating and inviting users -To prevent a Grafana Admin from creating users and inviting them to join an organization, you must [update a basic role permission]({{< relref "./manage-rbac-roles/#update-basic-role-permissions" >}}). +To prevent a Grafana Admin from creating users and inviting them to join an organization, you must [update a basic role permission](ref:manage-rbac-roles-update-basic-role-permissions). The permissions to remove are: | Action | Scope | @@ -267,7 +293,7 @@ The permissions to remove are: There are two ways to achieve this: -- Use [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#update-a-role" >}}). +- Use [RBAC HTTP API](ref:api-rbac-update-a-role). As an example, here is a small bash script that fetches the role, modifies it using `jq` and updates it: @@ -306,7 +332,7 @@ There are two ways to achieve this: ### Prevent Viewers from accessing an App Plugin By default, Viewers, Editors and Admins have access to all App Plugins that their organization role allows them to access. -To change this default behavior and prevent Viewers from accessing an App plugin, you must [update a basic role's permissions]({{< relref "./manage-rbac-roles/#update-basic-role-permissions" >}}). +To change this default behavior and prevent Viewers from accessing an App plugin, you must [update a basic role's permissions](ref:manage-rbac-roles-update-basic-role-permissions). In this example, three App plugins have been installed and enabled: | Name | ID | Required Org role | @@ -329,7 +355,7 @@ If you want to revoke their access to the On Call App plugin, you need to: Here are two ways to achieve this: -- Use [RBAC HTTP API]({{< relref "../../../../developers/http_api/access_control/#update-a-role" >}}). +- Use [RBAC HTTP API](ref:api-rbac-update-a-role). As an example, here is a small bash script that fetches the role, modifies it using `jq` and updates it: @@ -397,7 +423,7 @@ A user will be added to the default organization automatically but won't have an Using Service Accounts is an efficient way to facilitate M2M communications. However, they can pose a security threat if not scoped appropriately. To limit the scope of a service account, you can begin by creating a Service Account with `No Basic Role` and then assign the necessary permissions for the account. -1. Refer to [Service Accounts](https://grafana.com/docs/grafana/latest/administration/service-accounts/) and add a new Service Account. +1. Refer to [Service Accounts](ref:service-accounts) and add a new Service Account. 1. Set the basic role to `No Basic Role`. 1. Set the fixed roles needed for the Service Account. diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md index e6864815761..68ea28a7814 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md @@ -11,35 +11,66 @@ labels: menuTitle: RBAC role definitions title: Grafana RBAC role definitions weight: 70 +refs: + rbac-basic-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/#basic-roles + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/#basic-roles + rbac-terraform-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-terraform-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-terraform-provisioning/ + rbac-manage-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/ + plan-rbac-rollout-strategy-create-a-custom-role-to-access-alerts-in-a-folder: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/plan-rbac-rollout-strategy/#create-a-custom-role-to-access-alerts-in-a-folder + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/plan-rbac-rollout-strategy/#create-a-custom-role-to-access-alerts-in-a-folder + oncall: + - pattern: /docs/grafana/ + destination: /docs/oncall// + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/oncall/ + available-grafana-oncall-rbac-roles--granted-actions: + - pattern: /docs/grafana/ + destination: /docs/oncall//user-and-team-management/#available-grafana-oncall-rbac-roles--granted-actions + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/alerting-and-irm/oncall/user-and-team-management/#available-grafana-oncall-rbac-roles--granted-actions --- # RBAC role definitions {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} The following tables list permissions associated with basic and fixed roles. ## Basic role assignments -| Basic role | UID | Associated fixed roles | Description | -| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`
                      `fixed:roles:writer`
                      `fixed:users:reader`
                      `fixed:users:writer`
                      `fixed:org.users:reader`
                      `fixed:org.users:writer`
                      `fixed:ldap:reader`
                      `fixed:ldap:writer`
                      `fixed:stats:reader`
                      `fixed:settings:reader`
                      `fixed:settings:writer`
                      `fixed:provisioning:writer`
                      `fixed:organization:reader`
                      `fixed:organization:maintainer`
                      `fixed:licensing:reader`
                      `fixed:licensing:writer`
                      `fixed:datasources.caching:reader`
                      `fixed:datasources.caching:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:plugins:maintainer`
                      `fixed:authentication.config:writer`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:writer`
                      `fixed:library.panels:general.writer` | Default [Grafana server administrator]({{< relref "../../#grafana-server-administrators" >}}) assignments. | -| Admin | `basic_admin` | `fixed:reports:reader`
                      `fixed:reports:writer`
                      `fixed:datasources:reader`
                      `fixed:datasources:writer`
                      `fixed:organization:writer`
                      `fixed:datasources.permissions:reader`
                      `fixed:datasources.permissions:writer`
                      `fixed:teams:writer`
                      `fixed:dashboards:reader`
                      `fixed:dashboards:writer`
                      `fixed:dashboards.permissions:reader`
                      `fixed:dashboards.permissions:writer`
                      `fixed:dashboards.public:writer`
                      `fixed:folders:reader`
                      `fixed:folders:writer`
                      `fixed:folders.permissions:reader`
                      `fixed:folders.permissions:writer`
                      `fixed:alerting:writer`
                      `fixed:apikeys:reader`
                      `fixed:apikeys:writer`
                      `fixed:alerting.provisioning.secrets:reader`
                      `fixed:alerting.provisioning:writer`
                      `fixed:datasources.caching:reader`
                      `fixed:datasources.caching:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:plugins:writer`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:writer`
                      `fixed:library.panels:general.writer`
                      `fixed:alerting.provisioning.status:writer` | Default [Grafana organization administrator]({{< relref "../#basic-roles" >}}) assignments. | -| Editor | `basic_editor` | `fixed:datasources:explorer`
                      `fixed:dashboards:creator`
                      `fixed:folders:creator`
                      `fixed:annotations:writer`
                      `fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled
                      `fixed:alerting:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:general.writer`
                      `fixed:alerting.provisioning.status:writer` | Default [Editor]({{< relref "../#basic-roles" >}}) assignments. | -| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
                      `fixed:organization:reader`
                      `fixed:annotations:reader`
                      `fixed:annotations.dashboard:writer`
                      `fixed:alerting:reader`
                      `fixed:plugins.app:reader`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer]({{< relref "../#basic-roles" >}}) assignments. | -| No Basic Role | n/a | | Default [No Basic Role]({{< relref "../#basic-roles" >}}) | +| Basic role | UID | Associated fixed roles | Description | +| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`
                      `fixed:roles:writer`
                      `fixed:users:reader`
                      `fixed:users:writer`
                      `fixed:org.users:reader`
                      `fixed:org.users:writer`
                      `fixed:ldap:reader`
                      `fixed:ldap:writer`
                      `fixed:stats:reader`
                      `fixed:settings:reader`
                      `fixed:settings:writer`
                      `fixed:provisioning:writer`
                      `fixed:organization:reader`
                      `fixed:organization:maintainer`
                      `fixed:licensing:reader`
                      `fixed:licensing:writer`
                      `fixed:datasources.caching:reader`
                      `fixed:datasources.caching:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:plugins:maintainer`
                      `fixed:authentication.config:writer`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:writer`
                      `fixed:library.panels:general.writer` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | +| Admin | `basic_admin` | `fixed:reports:reader`
                      `fixed:reports:writer`
                      `fixed:datasources:reader`
                      `fixed:datasources:writer`
                      `fixed:organization:writer`
                      `fixed:datasources.permissions:reader`
                      `fixed:datasources.permissions:writer`
                      `fixed:teams:writer`
                      `fixed:dashboards:reader`
                      `fixed:dashboards:writer`
                      `fixed:dashboards.permissions:reader`
                      `fixed:dashboards.permissions:writer`
                      `fixed:dashboards.public:writer`
                      `fixed:folders:reader`
                      `fixed:folders:writer`
                      `fixed:folders.permissions:reader`
                      `fixed:folders.permissions:writer`
                      `fixed:alerting:writer`
                      `fixed:apikeys:reader`
                      `fixed:apikeys:writer`
                      `fixed:alerting.provisioning.secrets:reader`
                      `fixed:alerting.provisioning:writer`
                      `fixed:datasources.caching:reader`
                      `fixed:datasources.caching:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:plugins:writer`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:writer`
                      `fixed:library.panels:general.writer`
                      `fixed:alerting.provisioning.status:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | +| Editor | `basic_editor` | `fixed:datasources:explorer`
                      `fixed:dashboards:creator`
                      `fixed:folders:creator`
                      `fixed:annotations:writer`
                      `fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled
                      `fixed:alerting:writer`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:library.panels:creator`
                      `fixed:library.panels:general.reader`
                      `fixed:library.panels:general.writer`
                      `fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. | +| Viewer | `basic_viewer` | `fixed:datasources.id:reader`
                      `fixed:organization:reader`
                      `fixed:annotations:reader`
                      `fixed:annotations.dashboard:writer`
                      `fixed:alerting:reader`
                      `fixed:plugins.app:reader`
                      `fixed:dashboards.insights:reader`
                      `fixed:datasources.insights:reader`
                      `fixed:library.panels:general.reader`
                      `fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. | +| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) | ## Fixed role definitions The following table has the existing built-in fixed role definitions. Other fixed roles might be added by plugins installed in Grafana. -The UUID presented here can be used as an identifier for [Terraform provisioning](../rbac-terraform-provisioning). +The UUID presented here can be used as an identifier for [Terraform provisioning](ref:rbac-terraform-provisioning). {{< admonition type="caution" >}} These UUIDs won't be available if your instance was created before Grafana v10.2.0. -To learn how to use the roles API to determine the role UUIDs, refer to [Manage RBAC roles](/docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/). +To learn how to use the roles API to determine the role UUIDs, refer to [Manage RBAC roles](ref:rbac-manage-rbac-roles). {{< /admonition >}} | Fixed role | UUID | Permissions | Description | @@ -129,7 +160,7 @@ Access to Grafana alert rules is an intersection of many permissions: There is only one exclusion at this moment. Role `fixed:alerting.provisioning:writer` does not require user to have any additional permissions and provides access to all aspects of the alerting configuration via special provisioning API. -For more information about the permissions required to access alert rules, refer to [Create a custom role to access alerts in a folder]({{< relref "./plan-rbac-rollout-strategy/#create-a-custom-role-to-access-alerts-in-a-folder" >}}). +For more information about the permissions required to access alert rules, refer to [Create a custom role to access alerts in a folder](ref:plan-rbac-rollout-strategy-create-a-custom-role-to-access-alerts-in-a-folder). ### Grafana OnCall roles (beta) @@ -139,17 +170,17 @@ Available from Grafana 9.4 in early access. {{% admonition type="note" %}} This feature is behind the `accessControlOnCall` feature toggle. -You can enable feature toggles through configuration file or environment variables. See configuration [docs]({{< relref "../../../../setup-grafana/configure-grafana/#feature_toggles" >}}) for details. +You can enable feature toggles through configuration file or environment variables. See configuration [docs](/docs/grafana//setup-grafana/configure-grafana/#feature_toggles) for details. {{% /admonition %}} -If you are using [Grafana OnCall](https://grafana.com/docs/oncall/latest/get-started/), you can try out the integration between Grafana OnCall and RBAC. -For a detailed list of the available OnCall RBAC roles, refer to the table in [Available Grafana OnCall RBAC roles and granted actions](https://grafana.com/docs/oncall/latest/user-and-team-management/#available-grafana-oncall-rbac-roles--granted-actions). +If you are using [Grafana OnCall](ref:oncall), you can try out the integration between Grafana OnCall and RBAC. +For a detailed list of the available OnCall RBAC roles, refer to the table in [Available Grafana OnCall RBAC roles and granted actions](ref:available-grafana-oncall-rbac-roles--granted-actions). The following table lists the default RBAC OnCall role assignments to the basic roles: -| Basic role | Associated fixed roles | Description | -| ------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------- | -| Grafana Admin | `plugins:grafana-oncall-app:admin` | Default [Grafana server administrator]({{< relref "../#grafana-server-administrators" >}}) assignments. | -| Admin | `plugins:grafana-oncall-app:admin` | Default [Grafana organization administrator]({{< relref "../#basic-roles" >}}) assignments. | -| Editor | `plugins:grafana-oncall-app:editor` | Default [Editor]({{< relref "../#basic-roles" >}}) assignments. | -| Viewer | `plugins:grafana-oncall-app:reader` | Default [Viewer]({{< relref "../#basic-roles" >}}) assignments. | +| Basic role | Associated fixed roles | Description | +| ------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Grafana Admin | `plugins:grafana-oncall-app:admin` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | +| Admin | `plugins:grafana-oncall-app:admin` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | +| Editor | `plugins:grafana-oncall-app:editor` | Default [Editor](ref:rbac-basic-roles) assignments. | +| Viewer | `plugins:grafana-oncall-app:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. | diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md index cfbfa21f5f6..22feb31d65a 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md @@ -11,15 +11,51 @@ labels: menuTitle: Provisioning RBAC with Grafana title: Provisioning RBAC with Grafana weight: 60 +refs: + api-rbac-create-and-manage-custom-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#create-and-manage-custom-roles + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#create-and-manage-custom-roles + rbac-terraform-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-terraform-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-terraform-provisioning/ + rbac-manage-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/ + rbac-assign-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/assign-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/assign-rbac-roles/ + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ + manage-rbac-roles-create-custom-roles-using-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/manage-rbac-roles/#create-custom-roles-using-provisioning + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/manage-rbac-roles/#create-custom-roles-using-provisioning + assign-rbac-roles-assign-a-fixed-role-to-a-basic-role-using-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/assign-rbac-roles/#assign-a-fixed-role-to-a-basic-role-using-provisioning + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/assign-rbac-roles/##assign-a-fixed-role-to-a-basic-role-using-provisioning --- # Provisioning RBAC with Grafana {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} -You can create, change or remove [Custom roles]({{< relref "./manage-rbac-roles/#create-custom-roles-using-provisioning" >}}) and create or remove [basic role assignments]({{< relref "./assign-rbac-roles/#assign-a-fixed-role-to-a-basic-role-using-provisioning" >}}), by adding one or more YAML configuration files in the `provisioning/access-control/` directory. +You can create, change or remove [Custom roles](ref:manage-rbac-roles-create-custom-roles-using-provisioning) and create or remove [basic role assignments](ref:assign-rbac-roles-assign-a-fixed-role-to-a-basic-role-using-provisioning), by adding one or more YAML configuration files in the `provisioning/access-control/` directory. Grafana performs provisioning during startup. After you make a change to the configuration file, you can reload it during runtime. You do not need to restart the Grafana server for your changes to take effect. @@ -37,13 +73,13 @@ Grafana performs provisioning during startup. After you make a change to the con 1. Add RBAC provisioning details to the configuration file. - Refer to [Manage RBAC roles]({{< relref "./manage-rbac-roles/" >}}) and [Assign RBAC roles]({{< relref "./assign-rbac-roles/" >}}) for instructions. + Refer to [Manage RBAC roles](ref:rbac-manage-rbac-roles) and [Assign RBAC roles](ref:rbac-assign-rbac-roles) for instructions. - Refer to [example role provisioning file]({{< relref "#example-role-configuration-file-using-grafana-provisioning" >}}) for a complete example of a provisioning file. + Refer to [example role provisioning file](#example-role-configuration-file-using-grafana-provisioning) for a complete example of a provisioning file. 1. Reload the provisioning configuration file. - For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations]({{< relref "../../../../developers/http_api/admin/#reload-provisioning-configurations" >}}). + For more information about reloading the provisioning configuration at runtime, refer to [Reload provisioning configurations](/docs/grafana//developers/http_api/admin/#reload-provisioning-configurations). ## Example role configuration file using Grafana provisioning @@ -131,6 +167,6 @@ teams: ## Useful Links -[Provisioning RBAC setup with Terraform]({{< relref "./rbac-terraform-provisioning">}}) +[Provisioning RBAC setup with Terraform](ref:rbac-terraform-provisioning) [Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md index 5d841535473..ce6acbaf2b5 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md @@ -10,12 +10,43 @@ labels: menuTitle: Provisioning RBAC with Terraform title: Provisioning RBAC with Terraform weight: 60 +refs: + api-rbac-create-and-manage-custom-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/access_control/#create-and-manage-custom-roles + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/access_control/#create-and-manage-custom-roles + rbac-grafana-provisioning: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-grafana-provisioning/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-grafana-provisioning/ + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ + service-accounts-create-a-service-account-in-grafana: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/#create-a-service-account-in-grafana + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#create-a-service-account-in-grafana + service-accounts-assign-roles-to-a-service-account-in-grafana: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/#assign-roles-to-a-service-account-in-grafana + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#assign-roles-to-a-service-account-in-grafana + service-accounts-to-add-a-token-to-a-service-account: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/#to-add-a-token-to-a-service-account + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#to-add-a-token-to-a-service-account --- # Provisioning RBAC with Terraform {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} You can create, change or remove [Custom roles](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role) and create or remove [basic and custom role assignments](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role_assignment), by using [Terraform's Grafana provider](https://registry.terraform.io/providers/grafana/grafana/latest/docs). @@ -28,15 +59,15 @@ You can create, change or remove [Custom roles](https://registry.terraform.io/pr ## Create a Service Account Token for provisioning -We recommend using service account tokens for provisioning. [Service accounts]({{< relref "../../../service-accounts/" >}}) support fine grained permissions, which allows you to easily authenticate and use the minimum set of permissions needed to provision your RBAC infrastructure. +We recommend using service account tokens for provisioning. [Service accounts](ref:service-accounts) support fine grained permissions, which allows you to easily authenticate and use the minimum set of permissions needed to provision your RBAC infrastructure. To create a service account token for provisioning, complete the following steps. -1. [Create a new service account]({{< relref "../../../service-accounts/#create-a-service-account-in-grafana" >}}) for your CI pipeline. -1. [Assign permissions to service account]({{< relref "../../../service-accounts/#assign-roles-to-a-service-account-in-grafana" >}}): +1. [Create a new service account](ref:service-accounts-create-a-service-account-in-grafana) for your CI pipeline. +1. [Assign permissions to service account](ref:service-accounts-assign-roles-to-a-service-account-in-grafana): - You will need roles “Role reader”, "Role writer" and roles including any permissions that will be provisioned. For example, to create or assign a role that allows creating users, a service account needs permissions to create users. - Alternatively, you can assign "Admin" basic role to the service account. -1. [Create a new service account token]({{< relref "../../../service-accounts/#to-add-a-token-to-a-service-account" >}}) for use in Terraform. +1. [Create a new service account token](ref:service-accounts-to-add-a-token-to-a-service-account) for use in Terraform. Alternatively, you can use basic authentication. To view all the supported authentication formats, see [here](https://registry.terraform.io/providers/grafana/grafana/latest/docs#authentication). @@ -147,11 +178,11 @@ resource "grafana_role_assignment" "my_new_role_assignment" { ![Service Account Role Assignment](/static/img/docs/enterprise/tf_service_account_role_assignment.png) Note that instead of using a provisioned role, you can also look up the `uid` of an already existing fixed or custom role and use that instead. -You can use the [API endpoint for listing roles](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/#create-and-manage-custom-roles) to look up role `uid`s. +You can use the [API endpoint for listing roles](ref:api-rbac-create-and-manage-custom-roles) to look up role `uid`s. Similarly, you can look up and use `id`s of users, teams and service accounts that have not been provisioned to assign roles to them. ## Useful Links -[RBAC setup with Grafana provisioning]({{< relref "./rbac-grafana-provisioning">}}) +[RBAC setup with Grafana provisioning](ref:rbac-grafana-provisioning) -[Grafana Cloud Terraform provisioning](/docs/grafana-cloud/infrastructure-as-code/terraform/) +[Grafana Cloud Terraform provisioning](/docs/grafana-cloud/developer-resources/infrastructure-as-code/terraform/) diff --git a/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md b/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md index 006d91496d3..207c47ac66b 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/troubleshooting/index.md @@ -27,7 +27,7 @@ filters = accesscontrol:debug accesscontrol.evaluator:debug dashboard.permission ## Enable audit logging {{% admonition type="note" %}} -Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud). {{% /admonition %}} You can enable auditing in the Grafana configuration file. @@ -38,11 +38,11 @@ enabled = true ``` All permission and role updates, and role assignments are added to audit logs. -Learn more about [access control audit logs]({{< relref "../../../../setup-grafana/configure-security/audit-grafana/#access-control" >}}). +Learn more about [access control audit logs](/docs/grafana//setup-grafana/configure-security/audit-grafana/#access-control). ## Missing dashboard, folder or data source permissions -[Dashboard and folder permissions]({{< relref "../../#dashboard-permissions" >}}) and [data source permissions]({{< relref "../../#data-source-permissions" >}}) can go out of sync if a Grafana instance version is upgraded, downgraded and then upgraded again. +[Dashboard and folder permissions](/docs/grafana//administration/roles-and-permissions/#dashboard-permissions) and [data source permissions](/docs/grafana//administration/roles-and-permissions/#data-source-permissions) can go out of sync if a Grafana instance version is upgraded, downgraded and then upgraded again. This happens when an instance is downgraded from a version that uses RBAC to a version that uses the legacy access control, and dashboard, folder or data source permissions are updated. These permission updates will not be applied to RBAC, so permissions will be out of sync when the instance is next upgraded to a version with RBAC. diff --git a/docs/sources/administration/service-accounts/_index.md b/docs/sources/administration/service-accounts/_index.md index c5c44cb4dac..eac4ac84d1e 100644 --- a/docs/sources/administration/service-accounts/_index.md +++ b/docs/sources/administration/service-accounts/_index.md @@ -16,15 +16,56 @@ labels: menuTitle: Service accounts title: Service accounts weight: 800 +refs: + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ + migrate-api-keys: + - pattern: /docs/ + destination: /docs/grafana//administration/service-accounts/migrate-api-keys/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/migrate-api-keys/ + roles-and-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/ + rbac: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/ + rbac-assign-rbac-roles: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/access-control/assign-rbac-roles/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/assign-rbac-roles/ + api-create-service-account: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/serviceaccount/#create-service-account + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/serviceaccount/#create-service-account + api-create-service-account-tokens: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/serviceaccount/#create-service-account-tokens + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/serviceaccount/#create-service-account-tokens + api-update-service-account: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/serviceaccount/#update-service-account + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/serviceaccount/#update-service-account --- # Service accounts You can use a service account to run automated workloads in Grafana, such as dashboard provisioning, configuration, or report generation. Create service accounts and tokens to authenticate applications, such as Terraform, with the Grafana API. -{{% admonition type="note" %}} -Service accounts replace [API keys](/docs/grafana//administration/service-accounts/migrate-api-keys/) as the primary way to authenticate applications that interact with Grafana. -{{% /admonition %}} +{{< admonition type="note" >}} +Service accounts replace [API keys](ref:migrate-api-keys) as the primary way to authenticate applications that interact with Grafana. +{{< /admonition >}} A common use case for creating a service account is to perform operations on automated or triggered tasks. You can use service accounts to: @@ -33,17 +74,17 @@ A common use case for creating a service account is to perform operations on aut - Set up an external SAML authentication provider - Interact with Grafana without signing in as a user -In [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}), you can also use service accounts in combination with [role-based access control]({{< relref "../roles-and-permissions/access-control/" >}}) to grant very specific permissions to applications that interact with Grafana. +In [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/), you can also use service accounts in combination with [role-based access control](ref:rbac) to grant very specific permissions to applications that interact with Grafana. {{% admonition type="note" %}} Service accounts can only act in the organization they are created for. If you have the same task that is needed for multiple organizations, we recommend creating service accounts in each organization. -Service accounts can't be used for instance-wide operations, such as global user management and organization management. For these tasks, you need to use a user with [Grafana server administrator permissions]({{< relref "../roles-and-permissions/#grafana-server-administrators" >}}). +Service accounts can't be used for instance-wide operations, such as global user management and organization management. For these tasks, you need to use a user with [Grafana server administrator permissions](ref:roles-and-permissions). {{% /admonition %}} {{< vimeo 742056367 >}} -_Video shows service accounts in Grafana v9.1. Refer to [Create a service account in Grafana]({{< relref "#create-a-service-account-in-grafana" >}}) for current instructions._ +_Video shows service accounts in Grafana v9.1. Refer to [Create a service account in Grafana](#create-a-service-account-in-grafana) for current instructions._ ## Service account tokens @@ -65,19 +106,19 @@ The added benefits of service accounts to API keys include: - Service accounts resemble Grafana users and can be enabled/disabled, granted specific permissions, and remain active until they are deleted or disabled. API keys are only valid until their expiry date. - Service accounts can be associated with multiple tokens. - Unlike API keys, service account tokens are not associated with a specific user, which means that applications can be authenticated even if a Grafana user is deleted. -- You can grant granular permissions to service accounts by leveraging [role-based access control]({{< relref "../roles-and-permissions/access-control/" >}}). For more information about permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/" >}}). +- You can grant granular permissions to service accounts by leveraging [role-based access control](ref:rbac). For more information about permissions, refer to [About users and permissions](ref:roles-and-permissions). ## Create a service account in Grafana -A service account can be used to run automated workloads in Grafana, like dashboard provisioning, configuration, or report generation. For more information about how you can use service accounts, refer to [About service accounts]({{< ref "#about-service-accounts" >}}). +A service account can be used to run automated workloads in Grafana, like dashboard provisioning, configuration, or report generation. For more information about how you can use service accounts, refer to [About service accounts](ref:service-accounts). -For more information about creating service accounts via the API, refer to [Create a service account in the HTTP API]({{< relref "../../developers/http_api/serviceaccount/#create-service-account" >}}). +For more information about creating service accounts via the API, refer to [Create a service account in the HTTP API](ref:api-create-service-account). Note that the user who created a service account will also be able to read, update and delete the service account that they created, as well as permissions associated with that service account. ### Before you begin -- Ensure you have permission to create and edit service accounts. By default, the organization administrator role is required to create and edit service accounts. For more information about user permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/#" >}}). +- Ensure you have permission to create and edit service accounts. By default, the organization administrator role is required to create and edit service accounts. For more information about user permissions, refer to [About users and permissions](ref:roles-and-permissions). ### To create a service account @@ -93,13 +134,13 @@ Note that the user who created a service account will also be able to read, upda ## Add a token to a service account in Grafana -A service account token is a generated random string that acts as an alternative to a password when authenticating with Grafana’s HTTP API. For more information about service accounts, refer to [About service accounts in Grafana]({{< ref "#about-service-accounts" >}}). +A service account token is a generated random string that acts as an alternative to a password when authenticating with Grafana’s HTTP API. For more information about service accounts, refer to [About service accounts in Grafana](ref:service-accounts). -You can create a service account token using the Grafana UI or via the API. For more information about creating a service account token via the API, refer to [Create service account tokens using the HTTP API]({{< relref "../../developers/http_api/serviceaccount/#create-service-account-tokens" >}}). +You can create a service account token using the Grafana UI or via the API. For more information about creating a service account token via the API, refer to [Create service account tokens using the HTTP API](ref:api-create-service-account-tokens). ### Before you begin -- Ensure you have permission to create and edit service accounts. By default, the organization administrator role is required to create and edit service accounts. For more information about user permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/#" >}}). +- Ensure you have permission to create and edit service accounts. By default, the organization administrator role is required to create and edit service accounts. For more information about user permissions, refer to [About users and permissions](ref:roles-and-permissions). ### Service account token expiration dates @@ -121,9 +162,9 @@ By default, service account tokens don't have an expiration date, meaning they w ## Assign roles to a service account in Grafana You can assign roles to a Grafana service account to control access for the associated service account tokens. -You can assign roles to a service account using the Grafana UI or via the API. For more information about assigning a role to a service account via the API, refer to [Update service account using the HTTP API]({{< relref "../../developers/http_api/serviceaccount/#update-service-account" >}}). +You can assign roles to a service account using the Grafana UI or via the API. For more information about assigning a role to a service account via the API, refer to [Update service account using the HTTP API](ref:api-update-service-account). -In [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}), you can also [assign RBAC roles]({{< relref "../roles-and-permissions/access-control/assign-rbac-roles" >}}) to grant very specific permissions to applications that interact with Grafana. +In [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/), you can also [assign RBAC roles](ref:rbac-assign-rbac-roles) to grant very specific permissions to applications that interact with Grafana. {{% admonition type="note" %}} Since Grafana 10.2.0, the `No Basic Role` is available for organization users or service accounts. This role has no permissions. Permissions can be granted with RBAC. @@ -131,7 +172,7 @@ Since Grafana 10.2.0, the `No Basic Role` is available for organization users or ### Before you begin -- Ensure you have permission to update service accounts roles. By default, the organization administrator role is required to update service accounts permissions. For more information about user permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/#" >}}). +- Ensure you have permission to update service accounts roles. By default, the organization administrator role is required to update service accounts permissions. For more information about user permissions, refer to [About users and permissions](ref:roles-and-permissions). ### To assign a role to a service account @@ -147,7 +188,7 @@ To control what and who can do with the service account you can assign permissio ### Before you begin -- Ensure you have permission to update user and team permissions of a service accounts. By default, the organization administrator role is required to update user and teams permissions for a service account. For more information about user permissions, refer to [About users and permissions]({{< relref "../roles-and-permissions/#" >}}). +- Ensure you have permission to update user and team permissions of a service accounts. By default, the organization administrator role is required to update user and teams permissions for a service account. For more information about user permissions, refer to [About users and permissions](ref:roles-and-permissions). - Ensure you have permission to read teams. ### User and team permissions for a service account @@ -186,7 +227,7 @@ This can help you diagnose permissions-related issues with token authorization. These endpoints provide details on a service account's token. If you haven't added a token to a service account, do so before proceeding. -For details, refer to [Add a token to a service account]({{< relref "#add-a-token-to-a-service-account-in-grafana" >}}). +For details, refer to [Add a token to a service account](#add-a-token-to-a-service-account-in-grafana). ### List a service account token's permissions diff --git a/docs/sources/administration/service-accounts/migrate-api-keys.md b/docs/sources/administration/service-accounts/migrate-api-keys.md index b6801913780..6093d0cd315 100644 --- a/docs/sources/administration/service-accounts/migrate-api-keys.md +++ b/docs/sources/administration/service-accounts/migrate-api-keys.md @@ -15,17 +15,38 @@ labels: menuTitle: Migrate API keys title: Migrate API keys to service account tokens weight: 700 +refs: + service-accounts: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/ + service-accounts-benefits: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/service-accounts/#service-account-benefits + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/service-accounts/#service-account-benefits + roles-and-permissions: + - pattern: /docs/grafana/ + destination: /docs/grafana//administration/roles-and-permissions/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/ + api-service-account: + - pattern: /docs/grafana/ + destination: /docs/grafana//developers/http_api/serviceaccount/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/developer-resources/api-reference/http-api/serviceaccount/ --- # Migrate API keys to service account tokens {{% admonition type="note" %}} -API keys are deprecated. [Service accounts](/docs/grafana/latest/administration/service-accounts/) now replace API keys for authenticating with the **HTTP APIs** and interacting with Grafana. +API keys are deprecated. [Service accounts](ref:service-accounts) now replace API keys for authenticating with the **HTTP APIs** and interacting with Grafana. {{% /admonition %}} API keys specify a role—either **Admin**, **Editor**, or **Viewer**—that determine the permissions associated with interacting with Grafana. -Compared to API keys, service accounts have limited scopes that provide more security. For more information on the benefits of service accounts, refer to [service account benefits](/docs/grafana/latest/administration/service-accounts/#service-account-benefits). +Compared to API keys, service accounts have limited scopes that provide more security. For more information on the benefits of service accounts, refer to [service account benefits](ref:service-accounts-benefits). When you migrate an API key to a service account, a service account is created with a service account token. Your existing API key—now migrated to a service account token—will continue working as before. @@ -43,7 +64,7 @@ To follow these instructions, you need at least one of the following: - Editor permissions - Service account writer -For more information about permissions, refer to [Roles and permissions]({{< relref "../roles-and-permissions/#" >}}). +For more information about permissions, refer to [Roles and permissions](ref:roles-and-permissions). #### Steps @@ -64,7 +85,7 @@ To migrate a single API key to a service account, complete the following steps: ## Migrate API keys using the HTTP API -This section shows you how to programmatically migrate API keys to Grafana service accounts using the HTTP API. For API additional information, refer to [Service account HTTP APIs](/docs/grafana/latest/developers/http_api/serviceaccount/). +This section shows you how to programmatically migrate API keys to Grafana service accounts using the HTTP API. For API additional information, refer to [Service account HTTP APIs](ref:api-service-account). #### Before you begin diff --git a/docs/sources/developers/contribute.md b/docs/sources/developers/contribute.md index 2f4bde0145d..8e54d7f044c 100644 --- a/docs/sources/developers/contribute.md +++ b/docs/sources/developers/contribute.md @@ -36,7 +36,7 @@ These resources are useful for all developers. Our [style guides](https://github.com/grafana/grafana/tree/main/contribute/style-guides) outline Grafana style for frontend, backend, documentation, and more, including best practices. Please read through them before you start editing or coding! -- [Backend style guide](https://github.com/grafana/grafana/blob/main/contribute/style-guides/backend.md) explains how we want to write Go code in the future. +- [Backend style guide](https://github.com/grafana/grafana/blob/main/contribute/backend/style-guide.md) explains how we want to write Go code in the future. - [Documentation style guide](https://grafana.com/docs/writers-toolkit/write/style-guide/) applies to all documentation created for Grafana products. diff --git a/docs/sources/developers/http_api/_index.md b/docs/sources/developers/http_api/_index.md index 28ec50df492..a37489074de 100644 --- a/docs/sources/developers/http_api/_index.md +++ b/docs/sources/developers/http_api/_index.md @@ -27,40 +27,11 @@ Since version 8.4, HTTP API details are [specified](https://editor.swagger.io/?u Starting from version 9.1, there is also a [OpenAPI v3 specification](https://editor.swagger.io/?url=https://raw.githubusercontent.com/grafana/grafana/main/public/openapi3.json) (generated by the v2 one). -Users can browser and try out both via the Swagger UI editor (served by the grafana server) by navigating to `/swagger-ui`. +Users can browser and try out both via the Swagger UI editor (served by the Grafana server) by navigating to `/swagger-ui`. -## Authenticating API requests +## Authenticate API requests -You can authenticate requests using basic auth, a service account token or a session cookie (acquired using regular login or OAuth). - -### Basic Auth - -If basic auth is enabled (it is enabled by default), then you can authenticate your HTTP request via -standard basic auth. Basic auth will also authenticate LDAP users. - -curl example: - -```bash -curl http://admin:admin@localhost:3000/api/org -{"id":1,"name":"Main Org."} -``` - -### Service Account Token - -To create a service account token, click on **Administration** in the left-side menu, click **Users and access**, then **Service Accounts**. -For more information on how to use service account tokens, refer to the [Service Accounts]({{< relref "../../administration/service-accounts/" >}}) documentation. - -You use the token in all requests in the `Authorization` header, like this: - -**Example**: - -```http -GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1 -Accept: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk -``` - -The `Authorization` header value should be `Bearer `. +{{< docs/shared lookup="developers/authentication.md" source="grafana" version="" >}} ## X-Grafana-Org-Id Header @@ -110,7 +81,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk ## Grafana Enterprise HTTP APIs -Grafana Enterprise includes all of the Grafana OSS APIs as well as those that follow: +Grafana Enterprise includes all of the Grafana OSS APIs as well as the following APIs: - [Data source permissions API]({{< relref "datasource_permissions/" >}}) - [License API]({{< relref "licensing/" >}}) diff --git a/docs/sources/developers/http_api/auth.md b/docs/sources/developers/http_api/auth.md index 71b1b5c491f..2f3dd915dcb 100644 --- a/docs/sources/developers/http_api/auth.md +++ b/docs/sources/developers/http_api/auth.md @@ -89,47 +89,13 @@ Endpoint is obsolete and has been moved to [Grafana service account API]({{< rel `POST /api/auth/keys` -**Required permissions** - -See note in the [introduction]({{< ref "#authentication-api" >}}) for an explanation. - -| Action | Scope | -| ---------------- | ----- | -| `apikeys:create` | n/a | - -**Example Request**: - -```http -POST /api/auth/keys HTTP/1.1 -Accept: application/json -Content-Type: application/json -Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk - -{ - "name": "mykey", - "role": "Admin", - "secondsToLive": 86400 -} -``` - -JSON Body schema: - -- **name** – The key name -- **role** – Sets the access level/Grafana Role for the key. Can be one of the following values: `None`, `Viewer`, `Editor` or `Admin`. -- **secondsToLive** – Sets the key expiration in seconds. It is optional. If it is a positive number an expiration date for the key is set. If it is null, zero or is omitted completely (unless `api_key_max_seconds_to_live` configuration option is set) the key will never expire. - -Error statuses: - -- **400** – `api_key_max_seconds_to_live` is set but no `secondsToLive` is specified or `secondsToLive` is greater than this value. -- **500** – The key was unable to be stored in the database. - **Example Response**: ```http -HTTP/1.1 301 +HTTP/1.1 410 Content-Type: application/json -"" +{"message":"this endpoint has been removed, please use POST /api/serviceaccounts and POST /api/serviceaccounts/{id}/tokens instead"} ``` ## Delete API Key diff --git a/docs/sources/developers/http_api/authentication.md b/docs/sources/developers/http_api/authentication.md new file mode 100644 index 00000000000..55b5bc46bf3 --- /dev/null +++ b/docs/sources/developers/http_api/authentication.md @@ -0,0 +1,23 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/developers/http_api/authentication/ +description: 'You can authenticate HTTP API requests using basic authentication, a service account token, or a session cookie.' +keywords: + - grafana + - http + - documentation + - api + - role-based-access-control + - acl + - enterprise +labels: + products: + - enterprise + - oss +title: Authentication options for the HTTP API +menuTitle: Authentication +weight: 01 +--- + +# Authentication options for the HTTP API + +{{< docs/shared lookup="developers/authentication.md" source="grafana" version="" >}} diff --git a/docs/sources/developers/http_api/examples/_index.md b/docs/sources/developers/http_api/examples/_index.md new file mode 100644 index 00000000000..6fd3810eaed --- /dev/null +++ b/docs/sources/developers/http_api/examples/_index.md @@ -0,0 +1,18 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/developers/http_api/examples/ +keywords: + - grafana + - tutorials + - API +labels: + products: + - enterprise + - oss +title: 'HTTP API examples' +menuTitle: 'Examples' +weight: 02 +--- + +# HTTP API examples + +{{< section >}} diff --git a/docs/sources/developers/http_api/create-api-tokens-for-org.md b/docs/sources/developers/http_api/examples/create-api-tokens-for-org.md similarity index 92% rename from docs/sources/developers/http_api/create-api-tokens-for-org.md rename to docs/sources/developers/http_api/examples/create-api-tokens-for-org.md index aa047898da2..d5b2ded16a4 100644 --- a/docs/sources/developers/http_api/create-api-tokens-for-org.md +++ b/docs/sources/developers/http_api/examples/create-api-tokens-for-org.md @@ -1,8 +1,8 @@ --- aliases: - - ../../http_api/create-api-tokens-for-org/ - - ../../tutorials/api_org_token_howto/ -canonical: /docs/grafana/latest/developers/http_api/create-api-tokens-for-org/ + - ../../../http_api/create-api-tokens-for-org/ # /docs/grafana//http_api/create-api-tokens-for-org/ + - ../../../tutorials/api_org_token_howto/ # /docs/grafana//tutorials/api_org_token_howto/ +canonical: /docs/grafana/latest/developers/http_api/examples/create-api-tokens-for-org/ keywords: - grafana - tutorials @@ -15,7 +15,6 @@ labels: - enterprise - oss title: 'API Tutorial: Create Service Account tokens and dashboards for an organization' -weight: 150 --- # Create Service Account tokens and dashboards for an organization diff --git a/docs/sources/developers/http_api/curl-examples.md b/docs/sources/developers/http_api/examples/curl-examples.md similarity index 83% rename from docs/sources/developers/http_api/curl-examples.md rename to docs/sources/developers/http_api/examples/curl-examples.md index bea3d4e702d..a7f3c1ca2e8 100644 --- a/docs/sources/developers/http_api/curl-examples.md +++ b/docs/sources/developers/http_api/examples/curl-examples.md @@ -1,7 +1,8 @@ --- aliases: - - ../../http_api/curl-examples/ -canonical: /docs/grafana/latest/developers/http_api/curl-examples/ + - ../../../http_api/curl-examples/ # /docs/grafana//http_api/curl-examples/ + - ../../http_api/curl-examples/ # /docs/grafana//developers/http_api/curl-examples/ +canonical: /docs/grafana/latest/developers/http_api/examples/curl-examples/ description: cURL examples keywords: - grafana diff --git a/docs/sources/explore/simplified-exploration/metrics/index.md b/docs/sources/explore/simplified-exploration/metrics/index.md index 456b8caca85..e03eb7949a5 100644 --- a/docs/sources/explore/simplified-exploration/metrics/index.md +++ b/docs/sources/explore/simplified-exploration/metrics/index.md @@ -22,12 +22,12 @@ Explore Metrics is currently in [public preview](/docs/release-life-cycle/). Gra With Explore Metrics, you can: -- Easily slice and dice metrics based on their labels, so you can immediately see anomalies and identify issues -- See the right visualization for your metric based on its type (gauge vs. counter, for example) without building it yourself -- Surface other metrics relevant to the current metric -- “Explore in a drawer” - expand a drawer over a dashboard with more content so you don’t lose your place -- View a history of user steps when navigating through metrics and their filters - +- Easily segment metrics based on their labels, so you can immediately spot anomalies and identify issues. +- Automatically display the optimal visualization for each metric type (gauge vs. counter, for example) without manual setup. +- Uncover related metrics relevant to the one you're viewing. +- “Explore in a drawer” - overlay additional content on your dashboard without losing your current view. +- View a history of user steps when navigating through metrics and their filters. +- Seamlessly pivot to related telemetry, including log data. {{< docs/play title="Explore Metrics" url="https://play.grafana.org/explore/metrics/trail?from=now-1h&to=now&var-ds=grafanacloud-demoinfra-prom&var-filters=&refresh=&metricPrefix=all" >}} diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index f6ad8b08e08..88106a20c68 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1341,7 +1341,7 @@ Either "OpportunisticStartTLS", "MandatoryStartTLS", "NoStartTLS". Default is `e ### enable_tracing -Enable trace propagation in e-mail headers, using the `traceparent`, `tracestate` and (optionally) `baggage` fields. Default is `false`. To enable, you must first configure tracing in one of the `tracing.oentelemetry.*` sections. +Enable trace propagation in e-mail headers, using the `traceparent`, `tracestate` and (optionally) `baggage` fields. Default is `false`. To enable, you must first configure tracing in one of the `tracing.opentelemetry.*` sections.
                      diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index b3dda154598..150b4273f92 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -192,7 +192,6 @@ Experimental features might be changed or removed without prior notice. | `dashboardRestoreUI` | Enables the frontend to be able to restore a recently deleted dashboard | | `backgroundPluginInstaller` | Enable background plugin installer | | `dataplaneAggregator` | Enable grafana dataplane aggregator | -| `adhocFilterOneOf` | Exposes a new 'one of' operator for ad-hoc filters. This operator allows users to filter by multiple values in a single filter. | | `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | | `lokiSendDashboardPanelNames` | Send dashboard and panel names to Loki when querying | | `singleTopNav` | Unifies the top search bar and breadcrumb bar into one | diff --git a/docs/sources/shared/developers/authentication.md b/docs/sources/shared/developers/authentication.md new file mode 100644 index 00000000000..6e5158dc46f --- /dev/null +++ b/docs/sources/shared/developers/authentication.md @@ -0,0 +1,36 @@ +--- +headless: true +comments: | + This file is used in the following files: developers/http_api/{_index.md,authentication.md} +--- + +You can authenticate HTTP API requests using basic authentication, a service account token, or a session cookie (acquired via regular login or OAuth). + +### Basic auth + +If basic auth is enabled (it is enabled by default), then you can authenticate your HTTP request via +standard basic auth. Basic auth will also authenticate LDAP users. + +curl example: + +```bash +curl http://admin:admin@localhost:3000/api/org +{"id":1,"name":"Main Org."} +``` + +### Service account token + +To create a service account token, click on **Administration** in the left-side menu, click **Users and access**, then **Service Accounts**. +For more information on how to use service account tokens, refer to the [Service Accounts]({{< relref "../../administration/service-accounts/" >}}) documentation. + +You use the token in all requests in the `Authorization` header, like this: + +**Example**: + +```http +GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1 +Accept: application/json +Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk +``` + +The `Authorization` header value should be _`Bearer `_. diff --git a/e2e/dashboards-suite/dashboard-browse.spec.ts b/e2e/dashboards-suite/dashboard-browse.spec.ts index a39c0ca373d..4e9728b7a65 100644 --- a/e2e/dashboards-suite/dashboard-browse.spec.ts +++ b/e2e/dashboards-suite/dashboard-browse.spec.ts @@ -49,12 +49,4 @@ describe('Dashboard browse', () => { e2e.flows.confirmDelete(); e2e.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard').should('not.exist'); }); - - afterEach(() => { - // Permanently delete dashboard - e2e.pages.RecentlyDeleted.visit(); - e2e.pages.Search.table.row('E2E Test - Import Dashboard').find('[type="checkbox"]').click({ force: true }); - cy.contains('button', 'Delete permanently').click(); - e2e.flows.confirmDelete(); - }); }); diff --git a/e2e/dashboards-suite/dashboard-restore.spec.ts b/e2e/dashboards-suite/dashboard-restore.spec.ts deleted file mode 100644 index 270b9794e31..00000000000 --- a/e2e/dashboards-suite/dashboard-restore.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import testDashboard from '../dashboards/TestRestoreDashboard.json'; -import { e2e } from '../utils'; - -describe('Dashboard restore', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Should delete, restore and permanently delete from the Dashboards page', () => { - e2e.flows.importDashboard(testDashboard, 1000, true); - - e2e.pages.Dashboards.visit(); - - // Delete dashboard - e2e.pages.BrowseDashboards.table - .row('E2E Test - Restore Dashboard') - .find('[type="checkbox"]') - .click({ force: true }); - deleteDashboard('Delete'); - - // Dashboard should appear in Recently Deleted - e2e.pages.RecentlyDeleted.visit(); - e2e.pages.Search.table.row('E2E Test - Restore Dashboard').should('exist'); - - // Restore dashboard - e2e.pages.Search.table.row('E2E Test - Restore Dashboard').find('[type="checkbox"]').click({ force: true }); - cy.contains('button', 'Restore').click(); - cy.contains('p', 'This action will restore 1 dashboard.').should('be.visible'); - e2e.pages.ConfirmModal.delete().click(); - e2e.components.Alert.alertV2('success').contains('Dashboard E2E Test - Restore Dashboard restored').should('exist'); - - // Dashboard should appear in Browse - e2e.pages.Dashboards.visit(); - e2e.pages.BrowseDashboards.table.row('E2E Test - Restore Dashboard').should('exist'); - - // Delete dashboard - e2e.pages.BrowseDashboards.table - .row('E2E Test - Restore Dashboard') - .find('[type="checkbox"]') - .click({ force: true }); - deleteDashboard('Delete'); - - // Permanently delete dashboard - permanentlyDeleteDashboard(); - }); - - it('Should delete, restore and permanently delete from the Dashboard settings', () => { - e2e.flows.importDashboard(testDashboard, 1000, true); - - e2e.flows.openDashboard({ uid: '355ac6c2-8a12-4469-8b99-4750eb8d0966' }); - e2e.pages.Dashboard.DashNav.settingsButton().click(); - deleteDashboard('Delete dashboard'); - - // Permanently delete dashboard - permanentlyDeleteDashboard(); - }); -}); - -const deleteDashboard = (buttonName: string) => { - cy.contains('button', buttonName).click(); - e2e.flows.confirmDelete(); - e2e.components.Alert.alertV2('success') - .contains('Dashboard E2E Test - Restore Dashboard moved to Recently deleted') - .should('exist'); - e2e.pages.BrowseDashboards.table.row('E2E Test - Restore Dashboard').should('not.exist'); -}; - -const permanentlyDeleteDashboard = () => { - // Permanently delete dashboard - e2e.pages.RecentlyDeleted.visit(); - e2e.pages.Search.table.row('E2E Test - Restore Dashboard').find('[type="checkbox"]').click({ force: true }); - cy.contains('button', 'Delete permanently').click(); - cy.contains('p', 'This action will delete 1 dashboard.').should('be.visible'); - e2e.flows.confirmDelete(); - e2e.components.Alert.alertV2('success').contains('Dashboard E2E Test - Restore Dashboard deleted').should('exist'); - - // Dashboard should not appear in Recently Deleted or Browse - e2e.pages.Search.table.row('E2E Test - Restore Dashboard').should('not.exist'); - - e2e.pages.Dashboards.visit(); - e2e.pages.BrowseDashboards.table.row('E2E Test - Restore Dashboard').should('not.exist'); -}; diff --git a/e2e/dashboards/TestRestoreDashboard.json b/e2e/dashboards/TestRestoreDashboard.json deleted file mode 100644 index b34ff4c0c05..00000000000 --- a/e2e/dashboards/TestRestoreDashboard.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 322, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 6, - "options": { - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "8.3.0-pre", - "title": "Gauge Example", - "type": "gauge" - }, - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.3.0-pre", - "title": "Stat", - "type": "stat" - }, - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single" - } - }, - "title": "Time series example", - "type": "timeseries" - } - ], - "refresh": false, - "schemaVersion": 31, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "2021-09-01T04:00:00.000Z", - "to": "2021-09-15T04:00:00.000Z" - }, - "timepicker": {}, - "timezone": "", - "title": "E2E Test - Restore Dashboard", - "uid": "355ac6c2-8a12-4469-8b99-4750eb8d0966", - "version": 4 -} diff --git a/e2e/utils/flows/deleteDashboard.ts b/e2e/utils/flows/deleteDashboard.ts index 6b39a0020a8..de492012c2f 100644 --- a/e2e/utils/flows/deleteDashboard.ts +++ b/e2e/utils/flows/deleteDashboard.ts @@ -29,7 +29,6 @@ export const deleteDashboard = ({ quick = false, title, uid }: DeleteDashboardCo const quickDelete = (uid: string) => { cy.request('DELETE', fromBaseUrl(`/api/dashboards/uid/${uid}`)); - cy.request('DELETE', fromBaseUrl(`/api/dashboards/uid/${uid}/trash`)); }; const uiDelete = (uid: string, title: string) => { diff --git a/go.mod b/go.mod index d3a9b94f4f5..d0041802abc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana -go 1.23.0 +go 1.23.1 // contains openapi encoder fixes. remove ASAP replace cuelang.org/go => github.com/grafana/cue v0.0.0-20230926092038-971951014e3f // @grafana/grafana-as-code @@ -11,8 +11,8 @@ replace cuelang.org/go => github.com/grafana/cue v0.0.0-20230926092038-971951014 replace github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.52.0 require ( - buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.4.1-20221222094228-8b1d3d0f62e6.1 // @grafana/observability-traces-and-profiling - buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.33.0-20240414232344-9ca06271cb73.1 // @grafana/observability-traces-and-profiling + buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1 // @grafana/observability-traces-and-profiling + buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1 // @grafana/observability-traces-and-profiling cloud.google.com/go/kms v1.18.5 // @grafana/grafana-backend-group cloud.google.com/go/storage v1.43.0 // @grafana/grafana-backend-group cuelang.org/go v0.6.0-0.dev // @grafana/grafana-as-code @@ -43,7 +43,7 @@ require ( github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // @grafana/grafana-backend-group github.com/bufbuild/connect-go v1.10.0 // @grafana/observability-traces-and-profiling github.com/bwmarrin/snowflake v0.3.0 // @grafan/grafana-app-platform-squad - github.com/centrifugal/centrifuge v0.30.2 // @grafana/grafana-app-platform-squad + github.com/centrifugal/centrifuge v0.33.3 // @grafana/grafana-app-platform-squad github.com/crewjam/saml v0.4.13 // @grafana/identity-access-team github.com/dave/dst v0.27.2 // @grafana/grafana-as-code github.com/dlmiddlecote/sqlstats v1.0.2 // @grafana/grafana-backend-group @@ -54,8 +54,8 @@ require ( github.com/go-jose/go-jose/v3 v3.0.3 // @grafana/identity-access-team github.com/go-kit/log v0.2.1 // @grafana/grafana-backend-group github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team - github.com/go-openapi/loads v0.21.5 // @grafana/alerting-backend - github.com/go-openapi/runtime v0.27.1 // @grafana/alerting-backend + github.com/go-openapi/loads v0.22.0 // @grafana/alerting-backend + github.com/go-openapi/runtime v0.28.0 // @grafana/alerting-backend github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group github.com/go-sourcemap/sourcemap v2.1.3+incompatible // @grafana/grafana-backend-group @@ -74,9 +74,9 @@ require ( github.com/googleapis/gax-go/v2 v2.13.0 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20240827075410-70248a7a3a67 // @grafana/alerting-backend + github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41 // @grafana/alerting-backend github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team - github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e // @grafana/identity-access-team + github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e // @grafana/identity-access-team github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics @@ -86,11 +86,11 @@ require ( github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-aws-sdk v0.30.0 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1 // @grafana/partner-datasources - github.com/grafana/grafana-cloud-migration-snapshot v1.2.0 // @grafana/grafana-operator-experience-squad + github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.1.0 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group github.com/grafana/grafana-plugin-sdk-go v0.246.0 // @grafana/plugins-platform-backend - github.com/grafana/grafana/pkg/aggregator v0.0.0-20240820070818-e7b6b4cf3426 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad // This needs to be here for other projects that import grafana/grafana @@ -138,14 +138,14 @@ require ( github.com/openfga/openfga v1.5.4 // @grafana/identity-access-team github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend - github.com/prometheus/client_golang v1.20.0 // @grafana/alerting-backend + github.com/prometheus/client_golang v1.20.2 // @grafana/alerting-backend github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group github.com/prometheus/common v0.55.0 // @grafana/alerting-backend github.com/prometheus/prometheus v1.8.2-0.20221021121301-51a44e6657c3 // @grafana/alerting-backend github.com/redis/go-redis/v9 v9.1.0 // @grafana/alerting-backend github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group github.com/russellhaering/goxmldsig v1.4.0 // @grafana/grafana-backend-group - github.com/scottlepp/go-duck v0.0.21 // @grafana/grafana-app-platform-squad + github.com/scottlepp/go-duck v0.1.0 // @grafana/grafana-app-platform-squad github.com/spf13/cobra v1.8.1 // @grafana/grafana-app-platform-squad github.com/spf13/pflag v1.0.5 // @grafana-app-platform-squad github.com/spyzhov/ajson v0.9.0 // @grafana/grafana-app-platform-squad @@ -173,14 +173,14 @@ require ( go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage gocloud.dev v0.39.0 // @grafana/grafana-app-platform-squad golang.org/x/crypto v0.26.0 // @grafana/grafana-backend-group - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // @grafana/alerting-backend - golang.org/x/mod v0.18.0 // indirect; @grafana/grafana-backend-group + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // @grafana/alerting-backend + golang.org/x/mod v0.19.0 // indirect; @grafana/grafana-backend-group golang.org/x/net v0.28.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.22.0 // @grafana/identity-access-team golang.org/x/sync v0.8.0 // @grafana/alerting-backend golang.org/x/text v0.17.0 // @grafana/grafana-backend-group golang.org/x/time v0.6.0 // @grafana/grafana-backend-group - golang.org/x/tools v0.22.0 // @grafana/grafana-as-code + golang.org/x/tools v0.23.0 // @grafana/grafana-as-code gonum.org/v1/gonum v0.14.0 // @grafana/observability-metrics google.golang.org/api v0.191.0 // @grafana/grafana-backend-group google.golang.org/grpc v1.65.0 // @grafana/plugins-platform-backend @@ -194,7 +194,7 @@ require ( k8s.io/client-go v0.31.0 // @grafana/grafana-app-platform-squad k8s.io/component-base v0.31.0 // @grafana/grafana-app-platform-squad k8s.io/klog/v2 v2.130.1 // @grafana/grafana-app-platform-squad - k8s.io/kube-aggregator v0.31.0-rc.1 // @grafana/grafana-app-platform-squad + k8s.io/kube-aggregator v0.31.0 // @grafana/grafana-app-platform-squad k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // @grafana/grafana-app-platform-squad k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // @grafana/partner-datasources sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // @grafana-app-platform-squad @@ -231,7 +231,6 @@ require ( github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect github.com/apache/thrift v0.20.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect @@ -252,7 +251,7 @@ require ( github.com/buger/jsonparser v1.1.1 // indirect github.com/caio/go-tdigest v3.1.0+incompatible // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/centrifugal/protocol v0.10.0 // indirect + github.com/centrifugal/protocol v0.13.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240426225625-909263490071 // indirect @@ -279,13 +278,13 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect; @grafana/grafana-app-platform-squad github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.22.2 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect - github.com/go-openapi/spec v0.20.14 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-openapi/validate v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/googleapis v1.4.1 // indirect @@ -302,6 +301,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240821183201-2f012860344d // @grafana/grafana-search-and-storage github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d // @grafana/grafana-search-and-storage github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db // indirect github.com/grafana/sqlds/v3 v3.2.0 // indirect @@ -319,7 +319,6 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/memberlist v0.5.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/igm/sockjs-go/v3 v3.0.2 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.12.0 // indirect @@ -394,7 +393,7 @@ require ( github.com/prometheus/exporter-toolkit v0.11.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect - github.com/redis/rueidis v1.0.16 // indirect + github.com/redis/rueidis v1.0.45 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/cors v1.10.1 // @grafana/identity-access-team @@ -403,7 +402,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/segmentio/encoding v0.3.6 // indirect + github.com/segmentio/encoding v0.4.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -432,7 +431,7 @@ require ( go.etcd.io/etcd/api/v3 v3.5.14 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.etcd.io/etcd/client/v3 v3.5.14 // indirect - go.mongodb.org/mongo-driver v1.15.0 // indirect + go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect @@ -477,12 +476,17 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect ) -require github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240827091659-f1ba609b348c - require ( cloud.google.com/go/longrunning v0.5.12 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect + github.com/dolthub/maphash v0.1.0 // indirect + github.com/gammazero/deque v0.2.1 // indirect github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 // indirect + github.com/hairyhenderson/go-which v0.2.0 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect + github.com/maypok86/otter v1.2.2 // indirect + github.com/planetscale/vtprotobuf v0.6.0 // indirect + github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect ) // Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream diff --git a/go.sum b/go.sum index d1331cc5566..83d51d9fdfc 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.4.1-20221222094228-8b1d3d0f62e6.1 h1:wQ75SnlaD0X30PnrmA+07A/5fnQWrAHy1mzv+CPB5Oo= -buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.4.1-20221222094228-8b1d3d0f62e6.1/go.mod h1:VYzBTKhjl92cl3sv+xznQcJHCezU7qnI0FhBAUb4n8c= -buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.33.0-20240414232344-9ca06271cb73.1 h1:arEscdzM2EZPZT8x7tSzuBVgyZrnsKuvoftapClxUgw= -buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.33.0-20240414232344-9ca06271cb73.1/go.mod h1:/vvnaG5MGgbuJxYTucpo0QOom9xbSb6Y43za3/as9qk= +buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1 h1:q6MBjQ5fj3ygW/ySxOck7iwLhPQWbzOKJRZYzXRBmBk= +buf.build/gen/go/parca-dev/parca/bufbuild/connect-go v1.10.0-20240523185345-933eab74d046.1/go.mod h1:DEC5vsD4oLn7c6QeBVfUS7WpW5iwmW5lXfhpsjVxVt0= +buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1 h1:Osqg+/+sFJKr5iyna6/AvyxXPY0jqaIGr3ltzzSDLRk= +buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.1-20240523185345-933eab74d046.1/go.mod h1:lbDqoSeErWK6pETEKo/LO+JmU2GbZqVE8ILESypLuZU= 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.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -1507,8 +1507,6 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/apache/arrow/go/arrow v0.0.0-20210223225224-5bea62493d91/go.mod h1:c9sxoIT3YgLxH4UhLOCKaBlEojuMhVYpk4Ntv3opUTQ= -github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ= -github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= @@ -1670,10 +1668,10 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/centrifugal/centrifuge v0.30.2 h1:Ru/6lPpOMdqTTDRG/gr74HPUBRq7h574WDVwA47lv0U= -github.com/centrifugal/centrifuge v0.30.2/go.mod h1:6aIBCCjAnwpqr6SfkkU36Z0B9s/IZK+Z5faippXPXbI= -github.com/centrifugal/protocol v0.10.0 h1:Lac48ATVjVjirYPTHxbSMmiQXXajx7dhARKHy1UOL+A= -github.com/centrifugal/protocol v0.10.0/go.mod h1:Tq5I1mBpLHkLxNM9gfb3Gth+sTE2kKU5hH3cVgmVs9s= +github.com/centrifugal/centrifuge v0.33.3 h1:uyqBc27oM+qnC3NX5imvZxuk9+u2ze6QGWHDICZeoSc= +github.com/centrifugal/centrifuge v0.33.3/go.mod h1:GaOF4CiREY5x6lW7zYUz46Qrc6aUZ6J/AMV/kROc2+U= +github.com/centrifugal/protocol v0.13.4 h1:I0YxXtFNfn/ndDIZp5RkkqQcSSNH7DNPUbXKYtJXDzs= +github.com/centrifugal/protocol v0.13.4/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -1815,6 +1813,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ= +github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -1900,6 +1900,8 @@ github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6 github.com/fullstorydev/grpchan v1.1.1/go.mod h1:f4HpiV8V6htfY/K44GWV1ESQzHBTq7DinhzqQ95lpgc= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0= +github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU= github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y= github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34= github.com/getkin/kin-openapi v0.125.0 h1:jyQCyf2qXS1qvs2U00xQzkGCqYPhEhZDmSmVt65fXno= @@ -1953,8 +1955,9 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/analysis v0.21.5/go.mod h1:25YcZosX9Lwz2wBsrFrrsL8bmjjXdlyP6zsr2AMy29M= github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= -github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0= github.com/go-openapi/analysis v0.22.2/go.mod h1:pDF4UbZsQTo/oNuRfAWWd4dAh4yuYf//LYorPTjrpvo= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= @@ -1966,17 +1969,21 @@ github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kO github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.20.3/go.mod h1:FviDZ46i9ivh810gqzFLl5NttD5q3tSlMLqLr6okedM= -github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/loads v0.21.3/go.mod h1:Y3aMR24iHbKHppOj91nQ/SHc0cuPbAr4ndY4a02xydc= -github.com/go-openapi/loads v0.21.5 h1:jDzF4dSoHw6ZFADCGltDb2lE4F6De7aWSpe+IcsRzT0= github.com/go-openapi/loads v0.21.5/go.mod h1:PxTsnFBoBe+z89riT+wYt3prmSBP6GDAQh2l9H1Flz8= -github.com/go-openapi/runtime v0.27.1 h1:ae53yaOoh+fx/X5Eaq8cRmavHgDma65XPZuvBqvJYto= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= github.com/go-openapi/runtime v0.27.1/go.mod h1:fijeJEiEclyS8BRurYE1DE5TLb9/KZl6eAdbzjsrlLU= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= github.com/go-openapi/spec v0.20.12/go.mod h1:iSCgnBcwbMW9SfzJb8iYynXvcY6C/QFrI7otzF7xGM4= github.com/go-openapi/spec v0.20.13/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= -github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do= github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.21.10/go.mod h1:vNDMwbilnl7xKiO/Ve/8H8Bb2JIInBnH+lqiw6QWgis= github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= @@ -1988,8 +1995,9 @@ github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/ 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-openapi/validate v0.22.4/go.mod h1:qm6O8ZIcPVdSY5219468Jv7kBdGvkiZLPOmqnqTUZ2A= -github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw= github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -2118,7 +2126,6 @@ github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl76 github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= @@ -2249,12 +2256,12 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/alerting v0.0.0-20240827075410-70248a7a3a67 h1:3spByRvTR3Qo7uDCEVVLB7+5VYH1q4hxwqVLdNpcS6k= -github.com/grafana/alerting v0.0.0-20240827075410-70248a7a3a67/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= +github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41 h1:p+UsX43BoDH5YlG6xUd9xDS3M4sWouy8VJ+ODv5S6uE= +github.com/grafana/alerting v0.0.0-20240822131459-9daa6239cc41/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= -github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e h1:mwDijVnw0/7L/34B/5nypzT7nXuxJt71TrTbZRyESBQ= -github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= @@ -2277,8 +2284,8 @@ github.com/grafana/grafana-aws-sdk v0.30.0 h1:6IIetM4s2NbvPOI4/fefsyN84BIb0/T09l github.com/grafana/grafana-aws-sdk v0.30.0/go.mod h1:ZSVPU7IIJSi5lEg+K3Js+EUpZLXxUaBdaQWH+As1ihI= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1 h1:90HjoS3kvCd6Thvcl29rtL2+AcSt9AAIDqgQAGk/6T8= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.1/go.mod h1:yqaupYdH8i42m3VRrmVgNNLGvr4NVjoDmstgZzASAnc= -github.com/grafana/grafana-cloud-migration-snapshot v1.2.0 h1:FCUWASPPzGGbF2jTutR5i3rmoQdmnC4bypwJswdW3fI= -github.com/grafana/grafana-cloud-migration-snapshot v1.2.0/go.mod h1:bd6Cm06EK0MzRO5ahUpbDz1SxNOKu+fzladbaRPHZPY= +github.com/grafana/grafana-cloud-migration-snapshot v1.3.0 h1:F0O9eTy4jHjEd1Z3/qIza2GdY7PYpTddUeaq9p3NKGU= +github.com/grafana/grafana-cloud-migration-snapshot v1.3.0/go.mod h1:bd6Cm06EK0MzRO5ahUpbDz1SxNOKu+fzladbaRPHZPY= github.com/grafana/grafana-google-sdk-go v0.1.0 h1:LKGY8z2DSxKjYfr2flZsWgTRTZ6HGQbTqewE3JvRaNA= github.com/grafana/grafana-google-sdk-go v0.1.0/go.mod h1:Vo2TKWfDVmNTELBUM+3lkrZvFtBws0qSZdXhQxRdJrE= github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs= @@ -2286,8 +2293,8 @@ github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/ github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk= github.com/grafana/grafana-plugin-sdk-go v0.246.0 h1:id7MPpONfZhSlhE5K4LU1rBqaVcpe2EDqqPdyb7UTfw= github.com/grafana/grafana-plugin-sdk-go v0.246.0/go.mod h1:bK7/yelc8cANvNzXPbXk64I4qkqeyQfnyIx4U9y/ItY= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20240820070818-e7b6b4cf3426 h1:wpPdM7qtleXOM1CLfkHWejvwEHiuCMixXobfIChgCgA= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20240820070818-e7b6b4cf3426/go.mod h1:tN+IWuH3T93r746qR3kOGKoetB0gEd3/e+vR/8oaTf8= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 h1:2H9x4q53pkfUGtSNYD1qSBpNnxrFgylof/TYADb5xMI= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2/go.mod h1:gBLBniiSUQvyt4LRrpIeysj8Many0DV+hdUKifRE0Ec= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da/go.mod h1:p09fvU5ujNL/Ig8HB7g4f+S0zyYbQq3x/f0jA4ujVOM= github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da h1:xQMb8cRZYu7D0IO9q/lB7qFQpLGAoPUnCase1CGHrXY= @@ -2296,8 +2303,8 @@ github.com/grafana/grafana/pkg/promlib v0.0.6 h1:FuRyHMIgVVXkLuJnCflNfk3gqJflmyi github.com/grafana/grafana/pkg/promlib v0.0.6/go.mod h1:shFkrG1fQ/PPNRGhxAPNMLp0SAeG/jhqaLoG6n2191M= github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 h1:SNEeqY22DrGr5E9kGF1mKSqlOom14W9+b1u4XEGJowA= github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435/go.mod h1:8cz+z0i57IjN6MYmu/zZQdCg9CQcsnEHbaJBBEf3KQo= -github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240827091659-f1ba609b348c h1:OjiLoc6pb1vNUNcID0GIL1o8DcS8jrBuLIoWRDKnQSk= -github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240827091659-f1ba609b348c/go.mod h1:M55oqs8MKOMCUkRCcPf9+a9r1kjiH8Dx5SR91EbfOgA= +github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240821183201-2f012860344d h1:3oeqPfkTy3hJproHFj6NHx0mJDMU8bpU7ERcKF+C+dA= +github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240821183201-2f012860344d/go.mod h1:M55oqs8MKOMCUkRCcPf9+a9r1kjiH8Dx5SR91EbfOgA= github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d h1:cmJmy/KdlD+8EOWn9AogfRMr9tWoWPDnZ180sQxD/IA= github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d/go.mod h1:KL0LyEIlmuRi/zzuCopFZSmSJzBVu2hMGHIK74i4iE8= github.com/grafana/grafana/pkg/util/xorm v0.0.1 h1:72QZjxWIWpSeOF8ob4aMV058kfgZyeetkAB8dmeti2o= @@ -2341,6 +2348,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYp github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hairyhenderson/go-which v0.2.0 h1:vxoCKdgYc6+MTBzkJYhWegksHjjxuXPNiqo5G2oBM+4= +github.com/hairyhenderson/go-which v0.2.0/go.mod h1:U1BQQRCjxYHfOkXDyCgst7OZVknbqI7KuGKhGnmyIik= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= @@ -2445,14 +2454,14 @@ github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4 github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 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/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE= -github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -2555,7 +2564,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= @@ -2671,6 +2679,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= +github.com/maypok86/otter v1.2.2 h1:jJi0y8ruR/ZcKmJ4FbQj3QQTqKwV+LNrSOo2S1zbF5M= +github.com/maypok86/otter v1.2.2/go.mod h1:mKLfoI7v1HOmQMwFgX4QkRk23mX6ge3RDvjdHOWG4R4= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs= @@ -2857,8 +2867,8 @@ github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRah github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -2916,7 +2926,6 @@ github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZu github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= @@ -2933,6 +2942,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA= +github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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= @@ -2959,8 +2970,8 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= -github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -3020,8 +3031,8 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= -github.com/redis/rueidis v1.0.16 h1:ieB3AqZe9GcuTWZL8PFu1Mfn+pfqjBZAJEZh7zOcwSI= -github.com/redis/rueidis v1.0.16/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo= +github.com/redis/rueidis v1.0.45 h1:j7hfcqfLLIqgTK3IkxBhXdeJcP34t3XLXvorDLqXfgM= +github.com/redis/rueidis v1.0.45/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -3065,19 +3076,20 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= -github.com/scottlepp/go-duck v0.0.21 h1:bFg5/8ULOo62vmvIjEOy1EOf7Q86cpzq82BDN5RakVE= -github.com/scottlepp/go-duck v0.0.21/go.mod h1:m6V1VGZ4hdgvCj6+BmNMFo0taqiWhMx3CeL3uKHmP2E= +github.com/scottlepp/go-duck v0.1.0 h1:Lfunl1wd767v0dF0/dr+mBh+KnUFuDmgNycC76NJjeE= +github.com/scottlepp/go-duck v0.1.0/go.mod h1:xGoYUbgph5AbxwsMElWv2i/mgzQl89WIgwE69Ytml7Q= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ= -github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= +github.com/segmentio/encoding v0.4.0 h1:MEBYvRqiUB2nfR2criEXWqwdY6HJOUrCn5hboVOVmy8= +github.com/segmentio/encoding v0.4.0/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= +github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b h1:SCYeryKXBVdW38167VyumGakH+7E4Wxe6b/zxmQxwyM= +github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b/go.mod h1:daNLfX/GJKuZyN4HkMf0h8dVmTmgRbBSkd9bFQyGNIo= github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -3303,8 +3315,8 @@ go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkO go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8= +go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -3490,8 +3502,8 @@ golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N0 golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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= @@ -3545,8 +3557,8 @@ golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -3811,7 +3823,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -4011,8 +4022,8 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= @@ -4180,7 +4191,6 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= @@ -4467,6 +4477,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -4519,6 +4530,7 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -4554,8 +4566,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kms v0.31.0 h1:KchILPfB1ZE+ka7223mpU5zeFNkmb45jl7RHnlImUaI= k8s.io/kms v0.31.0/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= -k8s.io/kube-aggregator v0.31.0-rc.1 h1:kkt+saxeIL7Cxn9CDxvgnk4u+aNnceXpKwt6uuzo4T0= -k8s.io/kube-aggregator v0.31.0-rc.1/go.mod h1:+DH4QiiBYaufKt7kDFLj4/Aj3fw8rrioUKV78ISUSDY= +k8s.io/kube-aggregator v0.31.0 h1:3DqSpmqHF8rey7fY+qYXLJms0tYPhxrgWvjpnKVnS0Y= +k8s.io/kube-aggregator v0.31.0/go.mod h1:Fa+OVSpMQC7zbTTz7/QG7FXe9jZ8usuJQej5sMdCrkM= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= diff --git a/go.work b/go.work index 862037b80de..265fd6b33d9 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.23.0 +go 1.23.1 // The `skip:golangci-lint` comment tag is used to exclude the package from the `golangci-lint` GitHub Action. // The module at the root of the repo (`.`) is excluded because ./pkg/... is included manually in the `golangci-lint` configuration. diff --git a/go.work.sum b/go.work.sum index e20c043e07c..60d634aa36a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,389 +1,256 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= -buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1 h1:vp9EaPFSb75qe/793x58yE5fY1IJ/gdxb/kcDUzavtI= -buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1/go.mod h1:YDq2B5X5BChU0lxAG5MxHpDb8mx1fv9OGtF2mwOe7hY= cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= -cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= -cloud.google.com/go/accessapproval v1.7.5 h1:uzmAMSgYcnlHa9X9YSQZ4Q1wlfl4NNkZyQgho1Z6p04= cloud.google.com/go/accessapproval v1.7.11 h1:MgtE8CI+YJWPGGHnxQ9z1VQqV87h+vSGy2MeM/m0ggQ= cloud.google.com/go/accessapproval v1.7.11/go.mod h1:KGK3+CLDWm4BvjN0wFtZqdFUGhxlTvTF6PhAwQJGL4M= -cloud.google.com/go/accesscontextmanager v1.8.5 h1:2GLNaNu9KRJhJBFTIVRoPwk6xE5mUDgD47abBq4Zp/I= cloud.google.com/go/accesscontextmanager v1.8.11 h1:IQ3KLJmNKPgstN0ZcRw0niU4KfsiOZmzvcGCF+NT618= cloud.google.com/go/accesscontextmanager v1.8.11/go.mod h1:nwPysISS3KR5qXipAU6cW/UbDavDdTBBgPohbkhGSok= -cloud.google.com/go/aiplatform v1.60.0 h1:0cSrii1ZeLr16MbBoocyy5KVnrSdiQ3KN/vtrTe7RqE= cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rBPcG8U= cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= -cloud.google.com/go/analytics v0.23.0 h1:Q+y94XH84jM8SK8O7qiY/PJRexb6n7dRbQ6PiUa4YGM= cloud.google.com/go/analytics v0.23.6 h1:BY8ZY7hQwKBi+lNp1IkiMTOK4xe4lxZCeYv3S9ARXtE= cloud.google.com/go/analytics v0.23.6/go.mod h1:cFz5GwWHrWQi8OHKP9ep3Z4pvHgGcG9lPnFQ+8kXsNo= -cloud.google.com/go/apigateway v1.6.5 h1:sPXnpk+6TneKIrjCjcpX5YGsAKy3PTdpIchoj8/74OE= cloud.google.com/go/apigateway v1.6.11 h1:VtEvpnqqY2T5gZBzo+p7C87yGH3omHUkPIbRQkmGS9I= cloud.google.com/go/apigateway v1.6.11/go.mod h1:4KsrYHn/kSWx8SNUgizvaz+lBZ4uZfU7mUDsGhmkWfM= -cloud.google.com/go/apigeeconnect v1.6.5 h1:CrfIKv9Go3fh/QfQgisU3MeP90Ww7l/sVGmr3TpECo8= cloud.google.com/go/apigeeconnect v1.6.11 h1:CftZgGXFRLJeD2/5ZIdWuAMxW/88UG9tHhRPI/NY75M= cloud.google.com/go/apigeeconnect v1.6.11/go.mod h1:iMQLTeKxtKL+sb0D+pFlS/TO6za2IUOh/cwMEtn/4g0= -cloud.google.com/go/apigeeregistry v0.8.3 h1:C+QU2K+DzDjk4g074ouwHQGkoff1h5OMQp6sblCVreQ= cloud.google.com/go/apigeeregistry v0.8.9 h1:3vLwk0tS9L++6ZyV4RDH4UCydfVoqxJbpWvqG6MTtUw= cloud.google.com/go/apigeeregistry v0.8.9/go.mod h1:4XivwtSdfSO16XZdMEQDBCMCWDp3jkCBRhVgamQfLSA= cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= -cloud.google.com/go/appengine v1.8.5 h1:l2SviT44zWQiOv8bPoMBzW0vOcMO22iO0s+nVtVhdts= cloud.google.com/go/appengine v1.8.11 h1:ZLoWWwakgRzRnXX2bsgk2g1sdzti3wq+ebunTJsZNog= cloud.google.com/go/appengine v1.8.11/go.mod h1:xET3coaDUj+OP4TgnZlgQ+rG2R9fG2nblya13czP56Q= -cloud.google.com/go/area120 v0.8.5 h1:vTs08KPLN/iMzTbxpu5ciL06KcsrVPMjz4IwcQyZ4uY= cloud.google.com/go/area120 v0.8.11 h1:UID1dl7lW2zs8OpYVtVZ5WsXU9kUcxC1nd3nnToHW70= cloud.google.com/go/area120 v0.8.11/go.mod h1:VBxJejRAJqeuzXQBbh5iHBYUkIjZk5UzFZLCXmzap2o= -cloud.google.com/go/artifactregistry v1.14.7 h1:W9sVlyb1VRcUf83w7aM3yMsnp4HS4PoyGqYQNG0O5lI= cloud.google.com/go/artifactregistry v1.14.13 h1:NNK4vYVA5NGQmbmYidfJhnfmYU6SSSRUM2oopNouJNs= cloud.google.com/go/artifactregistry v1.14.13/go.mod h1:zQ/T4xoAFPtcxshl+Q4TJBgsy7APYR/BLd2z3xEAqRA= -cloud.google.com/go/asset v1.17.2 h1:xgFnBP3luSbUcC9RWJvb3Zkt+y/wW6PKwPHr3ssnIP8= cloud.google.com/go/asset v1.19.5 h1:/R2XZS6lR8oj/Y3L+epD2yy7mf44Zp62H4xZ4vzaR/Y= cloud.google.com/go/asset v1.19.5/go.mod h1:sqyLOYaLLfc4ACcn3YxqHno+J7lRt9NJTdO50zCUcY0= -cloud.google.com/go/assuredworkloads v1.11.5 h1:gCrN3IyvqY3cP0wh2h43d99CgH3G+WYs9CeuFVKChR8= cloud.google.com/go/assuredworkloads v1.11.11 h1:pwZp9o8aF5QmX4Z0YNlRe1ZOUzDw0UALmkem3aPobZc= cloud.google.com/go/assuredworkloads v1.11.11/go.mod h1:vaYs6+MHqJvLKYgZBOsuuOhBgNNIguhRU0Kt7JTGcnI= -cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w= -cloud.google.com/go/auth v0.5.1/go.mod h1:vbZT8GjzDf3AVqCcQmqeeM32U9HBFc32vVVAbwDsa6s= -cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4= cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= -cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop2LlzA= cloud.google.com/go/auth v0.8.0/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= -cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= -cloud.google.com/go/automl v1.13.5 h1:ijiJy9sYWh75WrqImXsfWc1e3HR3iO+ef9fvW03Ig/4= cloud.google.com/go/automl v1.13.11 h1:FBCLjGS+Did/wtRHqyS055bRs/EJXx3meTvHPcdZgk8= cloud.google.com/go/automl v1.13.11/go.mod h1:oMJdXRDOVC+Eq3PnGhhxSut5Hm9TSyVx1aLEOgerOw8= -cloud.google.com/go/baremetalsolution v1.2.4 h1:LFydisRmS7hQk9P/YhekwuZGqb45TW4QavcrMToWo5A= cloud.google.com/go/baremetalsolution v1.2.10 h1:VvBiXT9QJ4VpNVyfzHhLScY1aymZxpQgOa20yUvgphw= cloud.google.com/go/baremetalsolution v1.2.10/go.mod h1:eO2c2NMRy5ytcNPhG78KPsWGNsX5W/tUsCOWmYihx6I= -cloud.google.com/go/batch v1.8.0 h1:2HK4JerwVaIcCh/lJiHwh6+uswPthiMMWhiSWLELayk= cloud.google.com/go/batch v1.9.2 h1:o1RAjc0ExGAAm41YB9LbJZyJDgZR4M6SKyITsd/Smr4= cloud.google.com/go/batch v1.9.2/go.mod h1:smqwS4sleDJVAEzBt/TzFfXLktmWjFNugGDWl8coKX4= -cloud.google.com/go/beyondcorp v1.0.4 h1:qs0J0O9Ol2h1yA0AU+r7l3hOCPzs2MjE1d6d/kaHIKo= cloud.google.com/go/beyondcorp v1.0.10 h1:K4blSIQZn3YO4F4LmvWrH52pb8Y0L3NOrwkf22+x67M= cloud.google.com/go/beyondcorp v1.0.10/go.mod h1:G09WxvxJASbxbrzaJUMVvNsB1ZiaKxpbtkjiFtpDtbo= -cloud.google.com/go/bigquery v1.59.1 h1:CpT+/njKuKT3CEmswm6IbhNu9u35zt5dO4yPDLW+nG4= cloud.google.com/go/bigquery v1.62.0 h1:SYEA2f7fKqbSRRBHb7g0iHTtZvtPSPYdXfmqsjpsBwo= cloud.google.com/go/bigquery v1.62.0/go.mod h1:5ee+ZkF1x/ntgCsFQJAQTM3QkAZOecfCmvxhkJsWRSA= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f h1:UR2/6M/bSN8PPQlhaq+57w21VZLcEvq4ujsHd1p/G2s= cloud.google.com/go/bigtable v1.27.2-0.20240802230159-f371928b558f/go.mod h1:avmXcmxVbLJAo9moICRYMgDyTTPoV0MA0lHKnyqV4fQ= -cloud.google.com/go/billing v1.18.2 h1:oWUEQvuC4JvtnqLZ35zgzdbuHt4Itbftvzbe6aEyFdE= cloud.google.com/go/billing v1.18.9 h1:sGRWx7PvsfHuZyx151Xr6CrORIgjvCMO4GRabihSdQQ= cloud.google.com/go/billing v1.18.9/go.mod h1:bKTnh8MBfCMUT1fzZ936CPN9rZG7ZEiHB2J3SjIjByc= -cloud.google.com/go/binaryauthorization v1.8.1 h1:1jcyh2uIUwSZkJ/JmL8kd5SUkL/Krbv8zmYLEbAz6kY= cloud.google.com/go/binaryauthorization v1.8.7 h1:ItT9uR/0/ok2Ru3LCcbSIBUPsKqTA49ZmxCupqQaeFo= cloud.google.com/go/binaryauthorization v1.8.7/go.mod h1:cRj4teQhOme5SbWQa96vTDATQdMftdT5324BznxANtg= -cloud.google.com/go/certificatemanager v1.7.5 h1:UMBr/twXvH3jcT5J5/YjRxf2tvwTYIfrpemTebe0txc= cloud.google.com/go/certificatemanager v1.8.5 h1:ASC9N81NU8JnGzi9kiY2QTqtTgOziwGv48sjt3YG420= cloud.google.com/go/certificatemanager v1.8.5/go.mod h1:r2xINtJ/4xSz85VsqvjY53qdlrdCjyniib9Jp98ZKKM= -cloud.google.com/go/channel v1.17.5 h1:/omiBnyFjm4S1ETHoOmJbL7LH7Ljcei4rYG6Sj3hc80= cloud.google.com/go/channel v1.17.11 h1:AkKyMl2pSoJxBQtjAd6LYOtMgOaCl/kuiKoSg/Gf/H4= cloud.google.com/go/channel v1.17.11/go.mod h1:gjWCDBcTGQce/BSMoe2lAqhlq0dIRiZuktvBKXUawp0= -cloud.google.com/go/cloudbuild v1.15.1 h1:ZB6oOmJo+MTov9n629fiCrO9YZPOg25FZvQ7gIHu5ng= cloud.google.com/go/cloudbuild v1.16.5 h1:RvK5r8JBCLNg9XmfGPy05t3bmhLJV3Xh3sDHGHAATgM= cloud.google.com/go/cloudbuild v1.16.5/go.mod h1:HXLpZ8QeYZgmDIWpbl9Gs22p6o6uScgQ/cV9HF9cIZU= -cloud.google.com/go/clouddms v1.7.4 h1:Sr0Zo5EAcPQiCBgHWICg3VGkcdS/LLP1d9SR7qQBM/s= cloud.google.com/go/clouddms v1.7.10 h1:EA3y9v5TZiAlwgHJh2vPOEelqYiCxXBYZRCNnGK5q+g= cloud.google.com/go/clouddms v1.7.10/go.mod h1:PzHELq0QDyA7VaD9z6mzh2mxeBz4kM6oDe8YxMxd4RA= -cloud.google.com/go/cloudtasks v1.12.6 h1:EUt1hIZ9bLv8Iz9yWaCrqgMnIU+Tdh0yXM1MMVGhjfE= cloud.google.com/go/cloudtasks v1.12.12 h1:p91Brp4nJkyRRI/maYdO+FT+e9tU+2xoGr20s2rvalU= cloud.google.com/go/cloudtasks v1.12.12/go.mod h1:8UmM+duMrQpzzRREo0i3x3TrFjsgI/3FQw3664/JblA= -cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= -cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute v1.27.4 h1:XM8ulx6crjdl09XBfji7viFgZOEQuIxBwKmjRH9Rtmc= cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1x6L9JhTqU= -cloud.google.com/go/contactcenterinsights v1.13.0 h1:6Vs/YnDG5STGjlWMEjN/xtmft7MrOTOnOZYUZtGTx0w= cloud.google.com/go/contactcenterinsights v1.13.6 h1:LRcI5RAlLIbjwT312sGt+gyXcaXTr+v7uEQlNyArO9g= cloud.google.com/go/contactcenterinsights v1.13.6/go.mod h1:mL+DbN3pMQGaAbDC4wZhryLciwSwHf5Tfk4Itr72Zyk= -cloud.google.com/go/container v1.31.0 h1:MAaNH7VRNPWEhvqOypq2j+7ONJKrKzon4v9nS3nLZe0= cloud.google.com/go/container v1.38.0 h1:GP5zLamfvPZeOTifnGBSER/br76D5eJ97xhcXXrh5tM= cloud.google.com/go/container v1.38.0/go.mod h1:U0uPBvkVWOJGY/0qTVuPS7NeafFEUsHSPqT5pB8+fCY= -cloud.google.com/go/containeranalysis v0.11.4 h1:doJ0M1ljS4hS0D2UbHywlHGwB7sQLNrt9vFk9Zyi7vY= cloud.google.com/go/containeranalysis v0.12.1 h1:Xb8Eu7vVmWR5nAl5WPTGTx/dCr+R+oF7VbuYV47EHHs= cloud.google.com/go/containeranalysis v0.12.1/go.mod h1:+/lcJIQSFt45TC0N9Nq7/dPbl0isk6hnC4EvBBqyXsM= -cloud.google.com/go/datacatalog v1.19.3 h1:A0vKYCQdxQuV4Pi0LL9p39Vwvg4jH5yYveMv50gU5Tw= cloud.google.com/go/datacatalog v1.21.0 h1:vl0pQT9TZ5rKi9e69FgtXNCR7I8MVRj4+CnbeXhz6UQ= cloud.google.com/go/datacatalog v1.21.0/go.mod h1:DB0QWF9nelpsbB0eR/tA0xbHZZMvpoFD1XFy3Qv/McI= -cloud.google.com/go/dataflow v0.9.5 h1:RYHtcPhmE664+F0Je46p+NvFbG8z//KCXp+uEqB4jZU= cloud.google.com/go/dataflow v0.9.11 h1:YIhStasKFDESaUdpnsHsp/5bACYL/yvW0OuZ6zPQ6nY= cloud.google.com/go/dataflow v0.9.11/go.mod h1:CCLufd7I4pPfyp54qMgil/volrL2ZKYjXeYLfQmBGJs= -cloud.google.com/go/dataform v0.9.2 h1:5e4eqGrd0iDTCg4Q+VlAao5j2naKAA7xRurNtwmUknU= cloud.google.com/go/dataform v0.9.8 h1:oNtTx9PdH7aPnvrKIsPrh+Y6Mw+8Bw5/ZgLWVHAev/c= cloud.google.com/go/dataform v0.9.8/go.mod h1:cGJdyVdunN7tkeXHPNosuMzmryx55mp6cInYBgxN3oA= -cloud.google.com/go/datafusion v1.7.5 h1:HQ/BUOP8OIGJxuztpYvNvlb+/U+/Bfs9SO8tQbh61fk= cloud.google.com/go/datafusion v1.7.11 h1:GVcVisjVKmoj1eNnIp3G3qjjo+7koHr0Kf8tF6Cjqe0= cloud.google.com/go/datafusion v1.7.11/go.mod h1:aU9zoBHgYmoPp4dzccgm/Gi4xWDMXodSZlNZ4WNeptw= -cloud.google.com/go/datalabeling v0.8.5 h1:GpIFRdm0qIZNsxqURFJwHt0ZBJZ0nF/mUVEigR7PH/8= cloud.google.com/go/datalabeling v0.8.11 h1:7jSuJEAc7upeMmyICzqfU0OyxUV38JSWW+8r5GmoHX0= cloud.google.com/go/datalabeling v0.8.11/go.mod h1:6IGUV3z7hlkAU5ndKVshv/8z+7pxE+k0qXsEjyzO1Xg= -cloud.google.com/go/dataplex v1.14.2 h1:fxIfdU8fxzR3clhOoNI7XFppvAmndxDu1AMH+qX9WKQ= cloud.google.com/go/dataplex v1.18.2 h1:bIU1r1YnsX6P1qTnaRnah/STHoLJ3EHUZVCjJl2+1Eo= cloud.google.com/go/dataplex v1.18.2/go.mod h1:NuBpJJMGGQn2xctX+foHEDKRbizwuiHJamKvvSteY3Q= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= -cloud.google.com/go/dataproc/v2 v2.4.0 h1:/u81Fd+BvCLp+xjctI1DiWVJn6cn9/s3Akc8xPH02yk= cloud.google.com/go/dataproc/v2 v2.5.3 h1:OgTfUARkF8AfkNmoyT0wyLLXNh4LbT3l55s5gUlvFOk= cloud.google.com/go/dataproc/v2 v2.5.3/go.mod h1:RgA5QR7v++3xfP7DlgY3DUmoDSTaaemPe0ayKrQfyeg= -cloud.google.com/go/dataqna v0.8.5 h1:9ybXs3nr9BzxSGC04SsvtuXaHY0qmJSLIpIAbZo9GqQ= cloud.google.com/go/dataqna v0.8.11 h1:bEUidOYRS0EQ7qHbZtcnospuks72iTapboszXU9poz8= cloud.google.com/go/dataqna v0.8.11/go.mod h1:74Icl1oFKKZXPd+W7YDtqJLa+VwLV6wZ+UF+sHo2QZQ= -cloud.google.com/go/datastore v1.15.0 h1:0P9WcsQeTWjuD1H14JIY7XQscIPQ4Laje8ti96IC5vg= cloud.google.com/go/datastore v1.17.1 h1:6Me8ugrAOAxssGhSo8im0YSuy4YvYk4mbGvCadAH5aE= cloud.google.com/go/datastore v1.17.1/go.mod h1:mtzZ2HcVtz90OVrEXXGDc2pO4NM1kiBQy8YV4qGe0ZM= -cloud.google.com/go/datastream v1.10.4 h1:o1QDKMo/hk0FN7vhoUQURREuA0rgKmnYapB+1M+7Qz4= cloud.google.com/go/datastream v1.10.10 h1:klGhjQCLoLIRHMzMFIqM73cPNKliGveqC+Vrms+ce6A= cloud.google.com/go/datastream v1.10.10/go.mod h1:NqchuNjhPlISvWbk426/AU/S+Kgv7srlID9P5XOAbtg= -cloud.google.com/go/deploy v1.17.1 h1:m27Ojwj03gvpJqCbodLYiVmE9x4/LrHGGMjzc0LBfM4= cloud.google.com/go/deploy v1.21.0 h1:/qnNETfztKemA9JmUBOrnH/rG/XFkHOBHygN1Vy5lkg= cloud.google.com/go/deploy v1.21.0/go.mod h1:PaOfS47VrvmYnxG5vhHg0KU60cKeWcqyLbMBjxS8DW8= -cloud.google.com/go/dialogflow v1.49.0 h1:KqG0oxGE71qo0lRVyAoeBozefCvsMfcDzDjoLYSY0F4= cloud.google.com/go/dialogflow v1.55.0 h1:H28O0WAm2waHpNAz2n9jbv8FApfXxeKAkfHObdP2MMk= cloud.google.com/go/dialogflow v1.55.0/go.mod h1:0u0hSlJiFpMkMpMNoFrQETwDjaRm8Q8hYKv+jz5JeRA= -cloud.google.com/go/dlp v1.11.2 h1:lTipOuJaSjlYnnotPMbEhKURLC6GzCMDDzVbJAEbmYM= cloud.google.com/go/dlp v1.16.0 h1:mYjBqgVjseYXlx1TOOFsxSeZLboqxxKR7TqRGOG9vIU= cloud.google.com/go/dlp v1.16.0/go.mod h1:LtPZxZAenBXKzvWIOB2hdHIXuEcK0wW0En8//u+/nNA= -cloud.google.com/go/documentai v1.25.0 h1:lI62GMEEPO6vXJI9hj+G9WjOvnR0hEjvjokrnex4cxA= cloud.google.com/go/documentai v1.31.0 h1:YRkFK+0ZgEciz1svDkuL9fjbQLq8xvVa1d3NUlhO6B4= cloud.google.com/go/documentai v1.31.0/go.mod h1:5ajlDvaPyl9tc+K/jZE8WtYIqSXqAD33Z1YAYIjfad4= -cloud.google.com/go/domains v0.9.5 h1:Mml/R6s3vQQvFPpi/9oX3O5dRirgjyJ8cksK8N19Y7g= cloud.google.com/go/domains v0.9.11 h1:8peNiXtaMNIF9Wybci859M/yprFcEve1R2z08pErUBs= cloud.google.com/go/domains v0.9.11/go.mod h1:efo5552kUyxsXEz30+RaoIS2lR7tp3M/rhiYtKXkhkk= -cloud.google.com/go/edgecontainer v1.1.5 h1:tBY32km78ScpK2aOP84JoW/+wtpx5WluyPUSEE3270U= cloud.google.com/go/edgecontainer v1.2.5 h1:wTo0ulZDSsDzeoVjICJZjZMzZ1Nn9y//AwAQlXbaTbs= cloud.google.com/go/edgecontainer v1.2.5/go.mod h1:OAb6tElD3F3oBujFAup14PKOs9B/lYobTb6LARmoACY= -cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= cloud.google.com/go/errorreporting v0.3.1 h1:E/gLk+rL7u5JZB9oq72iL1bnhVlLrnfslrgcptjJEUE= cloud.google.com/go/errorreporting v0.3.1/go.mod h1:6xVQXU1UuntfAf+bVkFk6nld41+CPyF2NSPCyXE3Ztk= -cloud.google.com/go/essentialcontacts v1.6.6 h1:13eHn5qBnsawxI7mIrv4jRIEmQ1xg0Ztqw5ZGqtUNfA= cloud.google.com/go/essentialcontacts v1.6.12 h1:JaQXS+qCFYs8yectfZHpzw4+NjTvFqTuDMCtfPzMvbw= cloud.google.com/go/essentialcontacts v1.6.12/go.mod h1:UGhWTIYewH8Ma4wDRJp8cMAHUCeAOCKsuwd6GLmmQLc= -cloud.google.com/go/eventarc v1.13.4 h1:ORkd6/UV5FIdA8KZQDLNZYKS7BBOrj0p01DXPmT4tE4= cloud.google.com/go/eventarc v1.13.10 h1:HVJmOVc+7eVFAqMpJRrq0nY0KlYBEBVZW7Gz7TxTio8= cloud.google.com/go/eventarc v1.13.10/go.mod h1:KlCcOMApmUaqOEZUpZRVH+p0nnnsY1HaJB26U4X5KXE= -cloud.google.com/go/filestore v1.8.1 h1:X5G4y/vrUo1B8Nsz93qSWTMAcM8LXbGUldq33OdcdCw= cloud.google.com/go/filestore v1.8.7 h1:LF9t5MClPyFJMuXdez/AjF1uyO9xHKUFF3GUqA+xFPI= cloud.google.com/go/filestore v1.8.7/go.mod h1:dKfyH0YdPAKdYHqAR/bxZeil85Y5QmrEVQwIYuRjcXI= -cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw= cloud.google.com/go/firestore v1.16.0 h1:YwmDHcyrxVRErWcgxunzEaZxtNbc8QoFYA/JOEwDPgc= cloud.google.com/go/firestore v1.16.0/go.mod h1:+22v/7p+WNBSQwdSwP57vz47aZiY+HrDkrOsJNhk7rg= -cloud.google.com/go/functions v1.16.0 h1:IWVylmK5F6hJ3R5zaRW7jI5PrWhCvtBVU4axQLmXSo4= cloud.google.com/go/functions v1.16.6 h1:tPe3/48RpjcFk96VeB6jOKQpK8nliGJLsgjh6pUOyFQ= cloud.google.com/go/functions v1.16.6/go.mod h1:wOzZakhMueNQaBUJdf0yjsJIe0GBRu+ZTvdSTzqHLs0= cloud.google.com/go/gaming v1.10.1 h1:5qZmZEWzMf8GEFgm9NeC3bjFRpt7x4S6U7oLbxaf7N8= -cloud.google.com/go/gkebackup v1.3.5 h1:iuE8KNtTsPOc79qeWoNS8zOWoXPD9SAdOmwgxtlCmh8= cloud.google.com/go/gkebackup v1.5.4 h1:mufh0PNpvqbfLV+TcxzSGESX8jGBcjKgctldv7kwQns= cloud.google.com/go/gkebackup v1.5.4/go.mod h1:V+llvHlRD0bCyrkYaAMJX+CHralceQcaOWjNQs8/Ymw= -cloud.google.com/go/gkeconnect v0.8.5 h1:17d+ZSSXKqG/RwZCq3oFMIWLPI8Zw3b8+a9/BEVlwH0= cloud.google.com/go/gkeconnect v0.8.11 h1:4bZAzvqhuv1uP+i4yG9cEMQ6ggdP26nBVjUgroPU6IM= cloud.google.com/go/gkeconnect v0.8.11/go.mod h1:ejHv5ehbceIglu1GsMwlH0nZpTftjxEY6DX7tvaM8gA= -cloud.google.com/go/gkehub v0.14.5 h1:RboLNFzf9wEMSo7DrKVBlf+YhK/A/jrLN454L5Tz99Q= cloud.google.com/go/gkehub v0.14.11 h1:hQkVCcOiW/vPVYsthvKl1nje430/TpdFfgeIuqcYVOA= cloud.google.com/go/gkehub v0.14.11/go.mod h1:CsmDJ4qbBnSPkoBltEubK6qGOjG0xNfeeT5jI5gCnRQ= -cloud.google.com/go/gkemulticloud v1.1.1 h1:rsSZAGLhyjyE/bE2ToT5fqo1qSW7S+Ubsc9jFOcbhSI= cloud.google.com/go/gkemulticloud v1.2.4 h1:6zV05tyl37HoEjCGGY+zHFNxnKQCjvVpiqWAUVgGaEs= cloud.google.com/go/gkemulticloud v1.2.4/go.mod h1:PjTtoKLQpIRztrL+eKQw8030/S4c7rx/WvHydDJlpGE= -cloud.google.com/go/grafeas v0.3.4 h1:D4x32R/cHX3MTofKwirz015uEdVk4uAxvZkZCZkOrF4= cloud.google.com/go/grafeas v0.3.6 h1:7bcA10EBgTsxeAVypJhz2Dv3fhrdlO7Ml8l7ZZA2IkE= cloud.google.com/go/grafeas v0.3.6/go.mod h1:to6ECAPgRO2xeqD8ISXHc70nObJuaKZThreQOjeOH3o= -cloud.google.com/go/gsuiteaddons v1.6.5 h1:CZEbaBwmbYdhFw21Fwbo+C35HMe36fTE0FBSR4KSfWg= cloud.google.com/go/gsuiteaddons v1.6.11 h1:zydWX0nVT0Ut/P1X25Sy+4Rqe2PH04IzhwlF1BJd8To= cloud.google.com/go/gsuiteaddons v1.6.11/go.mod h1:U7mk5PLBzDpHhgHv5aJkuvLp9RQzZFpa8hgWAB+xVIk= -cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE= cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg= -cloud.google.com/go/iap v1.9.4 h1:94zirc2r4t6KzhAMW0R6Dme005eTP6yf7g6vN4IhRrA= cloud.google.com/go/iap v1.9.10 h1:j7jQqqSkZ2nWAOCiyaZfnJ+REycTJ2NP2dUEjLoW4aA= cloud.google.com/go/iap v1.9.10/go.mod h1:pO0FEirrhMOT1H0WVwpD5dD9r3oBhvsunyBQtNXzzc0= -cloud.google.com/go/ids v1.4.5 h1:xd4U7pgl3GHV+MABnv1BF4/Vy/zBF7CYC8XngkOLzag= cloud.google.com/go/ids v1.4.11 h1:JhlR1d0XhMsj6YmSmbLbbXV5CGkffnUkPj0HNxJYNtc= cloud.google.com/go/ids v1.4.11/go.mod h1:+ZKqWELpJm8WcRRsSvKZWUdkriu4A3XsLLzToTv3418= -cloud.google.com/go/iot v1.7.5 h1:munTeBlbqI33iuTYgXy7S8lW2TCgi5l1hA4roSIY+EE= cloud.google.com/go/iot v1.7.11 h1:UBqSUZA6+7bM+mv6uvhl8tVsyT2Fi50njtBFRbrKSlI= cloud.google.com/go/iot v1.7.11/go.mod h1:0vZJOqFy9kVLbUXwTP95e0dWHakfR4u5IWqsKMGIfHk= cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvajiQ22g= -cloud.google.com/go/language v1.12.3 h1:iaJZg6K4j/2PvZZVcjeO/btcWWIllVRBhuTFjGO4LXs= cloud.google.com/go/language v1.13.0 h1:6Pl97Ei85A3wBJwjXW2S/1IWeUvhQf/lIPQBItnp0FA= cloud.google.com/go/language v1.13.0/go.mod h1:B9FbD17g1EkilctNGUDAdSrBHiFOlKNErLljO7jplDU= -cloud.google.com/go/lifesciences v0.9.5 h1:gXvN70m2p+4zgJFzaz6gMKaxTuF9WJ0USYoMLWAOm8g= cloud.google.com/go/lifesciences v0.9.11 h1:xyPSYICJWZElcELYgWCKs5PltyNX3TzOKaQAZA7d/I0= cloud.google.com/go/lifesciences v0.9.11/go.mod h1:NMxu++FYdv55TxOBEvLIhiAvah8acQwXsz79i9l9/RY= -cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZw= cloud.google.com/go/logging v1.11.0 h1:v3ktVzXMV7CwHq1MBF65wcqLMA7i+z3YxbUsoK7mOKs= cloud.google.com/go/logging v1.11.0/go.mod h1:5LDiJC/RxTt+fHc1LAt20R9TKiUTReDg6RuuFOZ67+A= -cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= -cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= -cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng= -cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c= cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= -cloud.google.com/go/managedidentities v1.6.5 h1:+bpih1piZVLxla/XBqeSUzJBp8gv9plGHIMAI7DLpDM= cloud.google.com/go/managedidentities v1.6.11 h1:YU6NtRRBX5R1f3a8ryqhh1dUb1/pt3rnhSO50b63yZY= cloud.google.com/go/managedidentities v1.6.11/go.mod h1:df+8oZ1D4Eri+NrcpuiR5Hd6MGgiMqn0ZCzNmBYPS0A= -cloud.google.com/go/maps v1.6.4 h1:EVCZAiDvog9So46460BGbCasPhi613exoaQbpilMVlk= cloud.google.com/go/maps v1.11.6 h1:HMI0drvgnT+BtsjBofb1Z80P53n63ybmm7l+1w1og9I= cloud.google.com/go/maps v1.11.6/go.mod h1:MOS/NN0L6b7Kumr8bLux9XTpd8+D54DYxBMUjq+XfXs= -cloud.google.com/go/mediatranslation v0.8.5 h1:c76KdIXljQHSCb/Cy47S8H4s05A4zbK3pAFGzwcczZo= cloud.google.com/go/mediatranslation v0.8.11 h1:QvO405ocKTmcJqjfqL1zps08yrKk8rE+0E1ZNSWfjbw= cloud.google.com/go/mediatranslation v0.8.11/go.mod h1:3sNEm0fx61eHk7rfzBzrljVV9XKr931xI3OFacQBVFg= -cloud.google.com/go/memcache v1.10.5 h1:yeDv5qxRedFosvpMSEswrqUsJM5OdWvssPHFliNFTc4= cloud.google.com/go/memcache v1.10.11 h1:DGPEJOVL4Qix2GLKQKcgzGpNLD7gAnCFLr9ch9YSIhU= cloud.google.com/go/memcache v1.10.11/go.mod h1:ubJ7Gfz/xQawQY5WO5pht4Q0dhzXBFeEszAeEJnwBHU= -cloud.google.com/go/metastore v1.13.4 h1:dR7vqWXlK6IYR8Wbu9mdFfwlVjodIBhd1JRrpZftTEg= cloud.google.com/go/metastore v1.13.10 h1:E5eAxzIRoVP0DrV+ZtTLMYkkjSs4fcfsbL7wv1mXV2U= cloud.google.com/go/metastore v1.13.10/go.mod h1:RPhMnBxUmTLT1fN7fNbPqtH5EoGHueDxubmJ1R1yT84= -cloud.google.com/go/monitoring v1.18.0 h1:NfkDLQDG2UR3WYZVQE8kwSbUIEyIqJUPl+aOQdFH1T4= cloud.google.com/go/monitoring v1.20.3/go.mod h1:GPIVIdNznIdGqEjtRKQWTLcUeRnPjZW85szouimiczU= cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MHNeC0E= cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= -cloud.google.com/go/networkconnectivity v1.14.4 h1:GBfXFhLyPspnaBE3nI/BRjdhW8vcbpT9QjE/4kDCDdc= cloud.google.com/go/networkconnectivity v1.14.10 h1:2EE8pKiv1AI8fBdZCdiUjNgQ+TaBgwE4GxIze4fDdY0= cloud.google.com/go/networkconnectivity v1.14.10/go.mod h1:f7ZbGl4CV08DDb7lw+NmMXQTKKjMhgCEEwFbEukWuOY= -cloud.google.com/go/networkmanagement v1.9.4 h1:aLV5GcosBNmd6M8+a0ekB0XlLRexv4fvnJJrYnqeBcg= cloud.google.com/go/networkmanagement v1.13.6 h1:6TGn7ZZXyj5rloN0vv5Aw0awYbfbheNRg8BKroT7/2g= cloud.google.com/go/networkmanagement v1.13.6/go.mod h1:WXBijOnX90IFb6sberjnGrVtZbgDNcPDUYOlGXmG8+4= -cloud.google.com/go/networksecurity v0.9.5 h1:+caSxBTj0E8OYVh/5wElFdjEMO1S/rZtE1152Cepchc= cloud.google.com/go/networksecurity v0.9.11 h1:6wUzyHCwDEOkDbAJjT6jxsAi+vMfe3aj2JWwqSFVXOQ= cloud.google.com/go/networksecurity v0.9.11/go.mod h1:4xbpOqCwplmFgymAjPFM6ZIplVC6+eQ4m7sIiEq9oJA= -cloud.google.com/go/notebooks v1.11.3 h1:FH48boYmrWVQ6k0Mx/WrnNafXncT5iSYxA8CNyWTgy0= cloud.google.com/go/notebooks v1.11.9 h1:c8I0EaLGqStRmvX29L7jb4mOrpigxn1mGyBt65OdS0s= cloud.google.com/go/notebooks v1.11.9/go.mod h1:JmnRX0eLgHRJiyxw8HOgumW9iRajImZxr7r75U16uXw= -cloud.google.com/go/optimization v1.6.3 h1:63NZaWyN+5rZEKHPX4ACpw3BjgyeuY8+rCehiCMaGPY= cloud.google.com/go/optimization v1.6.9 h1:++U21U9LWFdgnnVFaq4kDeOafft6gI/CHzsiJ173c6U= cloud.google.com/go/optimization v1.6.9/go.mod h1:mcvkDy0p4s5k7iSaiKrwwpN0IkteHhGmuW5rP9nXA5M= -cloud.google.com/go/orchestration v1.8.5 h1:YHgWMlrPttIVGItgGfuvO2KM7x+y9ivN/Yk92pMm1a4= cloud.google.com/go/orchestration v1.9.6 h1:xfczjtNDabsXTnDySAwD/TMfDSkcxEgH1rxfS6BVQtM= cloud.google.com/go/orchestration v1.9.6/go.mod h1:gQvdIsHESZJigimnbUA8XLbYeFlSg/z+A7ppds5JULg= -cloud.google.com/go/orgpolicy v1.12.1 h1:2JbXigqBJVp8Dx5dONUttFqewu4fP0p3pgOdIZAhpYU= cloud.google.com/go/orgpolicy v1.12.7 h1:StymaN9vS7949m15Nwgf5aKd9yaRtzWJ4VqHdbXcOEM= cloud.google.com/go/orgpolicy v1.12.7/go.mod h1:Os3GlUFRPf1UxOHTup5b70BARnhHeQNNVNZzJXPbWYI= -cloud.google.com/go/osconfig v1.12.5 h1:Mo5jGAxOMKH/PmDY7fgY19yFcVbvwREb5D5zMPQjFfo= cloud.google.com/go/osconfig v1.13.2 h1:IbbTg7jtTEn4+iEJwgbCYck5NLMOc2eKrqVpQb7Xx6c= cloud.google.com/go/osconfig v1.13.2/go.mod h1:eupylkWQJCwSIEMkpVR4LqpgKkQi0mD4m1DzNCgpQso= -cloud.google.com/go/oslogin v1.13.1 h1:1K4nOT5VEZNt7XkhaTXupBYos5HjzvJMfhvyD2wWdFs= cloud.google.com/go/oslogin v1.13.7 h1:q9x7tjKtfBpXMpiJKwb5UyhMA3GrwmJHvx56uCEuS8M= cloud.google.com/go/oslogin v1.13.7/go.mod h1:xq027cL0fojpcEcpEQdWayiDn8tIx3WEFYMM6+q7U+E= -cloud.google.com/go/phishingprotection v0.8.5 h1:DH3WFLzEoJdW/6xgsmoDqOwT1xddFi7gKu0QGZQhpGU= cloud.google.com/go/phishingprotection v0.8.11 h1:3Kr7TINZ+8pbdWe3JnJf9c84ibz60NRTvwLdVtI3SK8= cloud.google.com/go/phishingprotection v0.8.11/go.mod h1:Mge0cylqVFs+D0EyxlsTOJ1Guf3qDgrztHzxZqkhRQM= -cloud.google.com/go/policytroubleshooter v1.10.3 h1:c0WOzC6hz964QWNBkyKfna8A2jOIx1zzZa43Gx/P09o= cloud.google.com/go/policytroubleshooter v1.10.9 h1:EHXkBYgHQtVH8P41G2xxmQbMwQh+o5ggno8l3/9CXaA= cloud.google.com/go/policytroubleshooter v1.10.9/go.mod h1:X8HEPVBWz8E+qwI/QXnhBLahEHdcuPO3M9YvSj0LDek= -cloud.google.com/go/privatecatalog v0.9.5 h1:UZ0assTnATXSggoxUIh61RjTQ4P9zCMk/kEMbn0nMYA= cloud.google.com/go/privatecatalog v0.9.11 h1:t8dJpQf22H6COeDvp7TDl7+KuwLT6yVmqAVRIUIUj6U= cloud.google.com/go/privatecatalog v0.9.11/go.mod h1:awEF2a8M6UgoqVJcF/MthkF8SSo6OoWQ7TtPNxUlljY= -cloud.google.com/go/pubsub v1.36.1 h1:dfEPuGCHGbWUhaMCTHUFjfroILEkx55iUmKBZTP5f+Y= cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= -cloud.google.com/go/pubsublite v1.8.1 h1:pX+idpWMIH30/K7c0epN6V703xpIcMXWRjKJsz0tYGY= cloud.google.com/go/pubsublite v1.8.2 h1:jLQozsEVr+c6tOU13vDugtnaBSUy/PD5zK6mhm+uF1Y= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= -cloud.google.com/go/recaptchaenterprise/v2 v2.9.2 h1:U3Wfq12X9cVMuTpsWDSURnXF0Z9hSPTHj+xsnXDRLsw= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2 h1:80Mx0i3uyv5dPNUYsNPFk9GJ+19AmTlnWnXFCTC9NkI= cloud.google.com/go/recaptchaenterprise/v2 v2.14.2/go.mod h1:MwPgdgvBkE46aWuuXeBTCB8hQJ88p+CpXInROZYCTkc= -cloud.google.com/go/recommendationengine v0.8.5 h1:ineqLswaCSBY0csYv5/wuXJMBlxATK6Xc5jJkpiTEdM= cloud.google.com/go/recommendationengine v0.8.11 h1:STJYdA/e/MAh2ZSdjss5YE/d0t0nt0WotBF9V0pgpPQ= cloud.google.com/go/recommendationengine v0.8.11/go.mod h1:cEkU4tCXAF88a4boMFZym7U7uyxvVwcQtKzS85IbQio= -cloud.google.com/go/recommender v1.12.1 h1:LVLYS3r3u0MSCxQSDUtLSkporEGi9OAE6hGvayrZNPs= cloud.google.com/go/recommender v1.12.7 h1:asEAoj4a3inPCdH8nbPaZDJWhR/xwfKi4tuSmIlaS2I= cloud.google.com/go/recommender v1.12.7/go.mod h1:lG8DVtczLltWuaCv4IVpNphONZTzaCC9KdxLYeZM5G4= -cloud.google.com/go/redis v1.14.2 h1:QF0maEdVv0Fj/2roU8sX3NpiDBzP9ICYTO+5F32gQNo= cloud.google.com/go/redis v1.16.4 h1:9CO6EcuM9/CpgtcjG6JZV+GFw3oDrRfwLwmvwo/uM1o= cloud.google.com/go/redis v1.16.4/go.mod h1:unCVfLP5eFrVhGLDnb7IaSaWxuZ+7cBgwwBwbdG9m9w= -cloud.google.com/go/resourcemanager v1.9.5 h1:AZWr1vWVDKGwfLsVhcN+vcwOz3xqqYxtmMa0aABCMms= cloud.google.com/go/resourcemanager v1.9.11 h1:N8CmqszjKNOgJnrQVsg+g8VWIEGgcwsD5rPiay9cMC4= cloud.google.com/go/resourcemanager v1.9.11/go.mod h1:SbNAbjVLoi2rt9G74bEYb3aw1iwvyWPOJMnij4SsmHA= -cloud.google.com/go/resourcesettings v1.6.5 h1:BTr5MVykJwClASci/7Og4Qfx70aQ4n3epsNLj94ZYgw= cloud.google.com/go/resourcesettings v1.7.4 h1:1VwLfvJi8QtGrKPwuisGqr6gcgaCSR6A57wIvN+fqkM= cloud.google.com/go/resourcesettings v1.7.4/go.mod h1:seBdLuyeq+ol2u9G2+74GkSjQaxaBWF+vVb6mVzQFG0= -cloud.google.com/go/retail v1.16.0 h1:Fn1GuAua1c6crCGqfJ1qMxG1Xh10Tg/x5EUODEHMqkw= cloud.google.com/go/retail v1.17.4 h1:YJgpBwCarAPqzaJS8ycIhyn2sAQT1RhTJRiTVBjtJAI= cloud.google.com/go/retail v1.17.4/go.mod h1:oPkL1FzW7D+v/hX5alYIx52ro2FY/WPAviwR1kZZTMs= -cloud.google.com/go/run v1.3.4 h1:m9WDA7DzTpczhZggwYlZcBWgCRb+kgSIisWn1sbw2rQ= cloud.google.com/go/run v1.4.0 h1:ai1rnbX92iPqWg9MrbDbebsxlUSAiOK6N9dEDDQeVA0= cloud.google.com/go/run v1.4.0/go.mod h1:4G9iHLjdOC+CQ0CzA0+6nLeR6NezVPmlj+GULmb0zE4= -cloud.google.com/go/scheduler v1.10.6 h1:5U8iXLoQ03qOB+ZXlAecU7fiE33+u3QiM9nh4cd0eTE= cloud.google.com/go/scheduler v1.10.12 h1:8BxDXoHCcsAe2fXsvFrkBbTxgl+5JBrIy1+/HRS0nxY= cloud.google.com/go/scheduler v1.10.12/go.mod h1:6DRtOddMWJ001HJ6MS148rtLSh/S2oqd2hQC3n5n9fQ= -cloud.google.com/go/secretmanager v1.11.5 h1:82fpF5vBBvu9XW4qj0FU2C6qVMtj1RM/XHwKXUEAfYY= cloud.google.com/go/secretmanager v1.13.5/go.mod h1:/OeZ88l5Z6nBVilV0SXgv6XJ243KP2aIhSWRMrbvDCQ= cloud.google.com/go/secretmanager v1.13.6 h1:0ZEl/LuoB4xQsjVfQt3Gi/dZfOv36n4JmdPrMargzYs= cloud.google.com/go/secretmanager v1.13.6/go.mod h1:x2ySyOrqv3WGFRFn2Xk10iHmNmvmcEVSSqc30eb1bhw= -cloud.google.com/go/security v1.15.5 h1:wTKJQ10j8EYgvE8Y+KhovxDRVDk2iv/OsxZ6GrLP3kE= cloud.google.com/go/security v1.17.4 h1:ERhxAa02mnMEIIAXvzje+qJ+yWniP6l5uOX+k9ELCaA= cloud.google.com/go/security v1.17.4/go.mod h1:KMuDJH+sEB3KTODd/tLJ7kZK+u2PQt+Cfu0oAxzIhgo= -cloud.google.com/go/securitycenter v1.24.4 h1:/5jjkZ+uGe8hZ7pvd7pO30VW/a+pT2MrrdgOqjyucKQ= cloud.google.com/go/securitycenter v1.33.1 h1:K+jfFUTum2jl//uWCN+QKkKXRgidxTyGfGTqXPyDvUY= cloud.google.com/go/securitycenter v1.33.1/go.mod h1:jeFisdYUWHr+ig72T4g0dnNCFhRwgwGoQV6GFuEwafw= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= -cloud.google.com/go/servicedirectory v1.11.4 h1:da7HFI1229kyzIyuVEzHXip0cw0d+E0s8mjQby0WN+k= cloud.google.com/go/servicedirectory v1.11.11 h1:8Ky2lY0CWJJIIlsc+rKTn6C3SqOuVEwT3brDC6TJCjk= cloud.google.com/go/servicedirectory v1.11.11/go.mod h1:pnynaftaj9LmRLIc6t3r7r7rdCZZKKxui/HaF/RqYfs= cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= -cloud.google.com/go/shell v1.7.5 h1:3Fq2hzO0ZSyaqBboJrFkwwf/qMufDtqwwA6ep8EZxEI= cloud.google.com/go/shell v1.7.11 h1:RobTXyL33DQITYQh//KJ9GjS4bsdj4fGmm2rkb/ywzM= cloud.google.com/go/shell v1.7.11/go.mod h1:SywZHWac7onifaT9m9MmegYp3GgCLm+tgk+w2lXK8vg= -cloud.google.com/go/spanner v1.57.0 h1:fJq+ZfQUDHE+cy1li0bJA8+sy2oiSGhuGqN5nqVaZdU= cloud.google.com/go/spanner v1.65.0 h1:XK15cs9lFFQo5n4Wh9nfrcPXAxWln6NdodDiQKmoD08= cloud.google.com/go/spanner v1.65.0/go.mod h1:dQGB+w5a67gtyE3qSKPPxzniedrnAmV6tewQeBY7Hxs= -cloud.google.com/go/speech v1.21.1 h1:nuFc+Kj5B8de75nN4FdPyUbI2SiBoHZG6BLurXL56Q0= cloud.google.com/go/speech v1.24.0 h1:3j+WpeBY57C0FDJxg317vpKgOLjL/kNxlcNPGSqXkqE= cloud.google.com/go/speech v1.24.0/go.mod h1:HcVyIh5jRXM5zDMcbFCW+DF2uK/MSGN6Rastt6bj1ic= -cloud.google.com/go/storage v1.41.0/go.mod h1:J1WCa/Z2FcgdEDuPUY8DxT5I+d9mFKsCepp5vR6Sq80= -cloud.google.com/go/storagetransfer v1.10.4 h1:dy4fL3wO0VABvzM05ycMUPFHxTPbJz9Em8ikAJVqSbI= cloud.google.com/go/storagetransfer v1.10.10 h1:GfxaYqX+kwlrSrJAENNmRTCGmSTgvouvS3XhgwKpOT8= cloud.google.com/go/storagetransfer v1.10.10/go.mod h1:8+nX+WgQ2ZJJnK8e+RbK/zCXk8T7HdwyQAJeY7cEcm0= -cloud.google.com/go/talent v1.6.6 h1:JssV0CE3FNujuSWn7SkosOzg7qrMxVnt6txOfGcMSa4= cloud.google.com/go/talent v1.6.12 h1:JN721EjG+UTfHVVaMhyxwKCCJPjUc8PiS0RnW/7kWfE= cloud.google.com/go/talent v1.6.12/go.mod h1:nT9kNVuJhZX2QgqKZS6t6eCWZs5XEBYRBv6bIMnPmo4= -cloud.google.com/go/texttospeech v1.7.5 h1:dxY2Q5mHCbrGa3oPR2O3PCicdnvKa1JmwGQK36EFLOw= cloud.google.com/go/texttospeech v1.7.11 h1:jzko1ahItjLYEWr6n3lTIoBSinD1JzavEuDzYLWZNko= cloud.google.com/go/texttospeech v1.7.11/go.mod h1:Ua125HU+WT2IkIo5MzQtuNpNEk72soShJQVdorZ1SAE= -cloud.google.com/go/tpu v1.6.5 h1:C8YyYda8WtNdBoCgFwwBzZd+S6+EScHOxM/z1h0NNp8= cloud.google.com/go/tpu v1.6.11 h1:uMrwnK05cocNt3OOp+mZ16xlvIKaXUt3QUXkUbG4LdM= cloud.google.com/go/tpu v1.6.11/go.mod h1:W0C4xaSj1Ay3VX/H96FRvLt2HDs0CgdRPVI4e7PoCDk= -cloud.google.com/go/trace v1.10.5 h1:0pr4lIKJ5XZFYD9GtxXEWr0KkVeigc3wlGpZco0X1oA= cloud.google.com/go/trace v1.10.11/go.mod h1:fUr5L3wSXerNfT0f1bBg08W4axS2VbHGgYcfH4KuTXU= cloud.google.com/go/trace v1.10.12 h1:GoGZv1iAXEa73HgSGNjRl2vKqp5/f2AeKqErRFXA2kg= cloud.google.com/go/trace v1.10.12/go.mod h1:tYkAIta/gxgbBZ/PIzFxSH5blajgX4D00RpQqCG/GZs= -cloud.google.com/go/translate v1.10.1 h1:upovZ0wRMdzZvXnu+RPam41B0mRJ+coRXFP2cYFJ7ew= -cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/QEF2aJEUovs= cloud.google.com/go/translate v1.10.7 h1:W16MpZ2Z3TWoHbNHmyHz9As276lGVTSwxRcquv454R0= cloud.google.com/go/translate v1.10.7/go.mod h1:mH/+8tvcItuy1cOWqU+/Y3iFHgkVUObNIQYI/kiFFiY= -cloud.google.com/go/video v1.20.4 h1:TXwotxkShP1OqgKsbd+b8N5hrIHavSyLGvYnLGCZ7xc= cloud.google.com/go/video v1.22.0 h1:+FTZi7NtT4FV2Y1j3zC3zYjaRrlGqKsZpbLweredEWM= cloud.google.com/go/video v1.22.0/go.mod h1:CxPshUNAb1ucnzbtruEHlAal9XY+SPG2cFqC/woJzII= -cloud.google.com/go/videointelligence v1.11.5 h1:mYaWH8uhUCXLJCN3gdXswKzRa2+lK0zN6/KsIubm6pE= cloud.google.com/go/videointelligence v1.11.11 h1:zl8xijOEavernn/t6mZZ4fg0pIVc2yquHH73oj0Leo4= cloud.google.com/go/videointelligence v1.11.11/go.mod h1:dab2Ca3AXT6vNJmt3/6ieuquYRckpsActDekLcsd6dU= cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= -cloud.google.com/go/vision/v2 v2.8.0 h1:W52z1b6LdGI66MVhE70g/NFty9zCYYcjdKuycqmlhtg= cloud.google.com/go/vision/v2 v2.8.6 h1:HyFEUXQa0SvlF0LASCn/x+juNCH4kIXQrUqi6SIcYvE= cloud.google.com/go/vision/v2 v2.8.6/go.mod h1:G3v0uovxCye3u369JfrHGY43H6u/IQ08x9dw5aVH8yY= -cloud.google.com/go/vmmigration v1.7.5 h1:5v9RT2vWyuw3pK2ox0HQpkoftO7Q7/8591dTxxQc79g= cloud.google.com/go/vmmigration v1.7.11 h1:yqwkTPpvSw9dUfnl9/APAVrwO9UW1jJZtgbZpNQ+WdU= cloud.google.com/go/vmmigration v1.7.11/go.mod h1:PmD1fDB0TEHGQR1tDZt9GEXFB9mnKKalLcTVRJKzcQA= -cloud.google.com/go/vmwareengine v1.1.1 h1:EGdDi9QbqThfZq3ILcDK5g+m9jTevc34AY5tACx5v7k= cloud.google.com/go/vmwareengine v1.2.0 h1:9Fjn/RoeOMo8UQt1TbXmmw7rJApC26BqnISAI1AERcc= cloud.google.com/go/vmwareengine v1.2.0/go.mod h1:rPjCHu6hG9N8d6PhkoDWFkqL9xpbFY+ueVW+0pNFbZg= -cloud.google.com/go/vpcaccess v1.7.5 h1:XyL6hTLtEM/eE4F1GEge8xUN9ZCkiVWn44K/YA7z1rQ= cloud.google.com/go/vpcaccess v1.7.11 h1:1XgRP+Q2X6MvE/xnexpQ7ydgav+IO5UcKUIJEbL65J8= cloud.google.com/go/vpcaccess v1.7.11/go.mod h1:a2cuAiSCI4TVK0Dt6/dRjf22qQvfY+podxst2VvAkcI= -cloud.google.com/go/webrisk v1.9.5 h1:251MvGuC8wisNN7+jqu9DDDZAi38KiMXxOpA/EWy4dE= cloud.google.com/go/webrisk v1.9.11 h1:2qwEqnXrToIv2Y4xvsUSxCk7R2Ki+3W2+GNyrytoKTQ= cloud.google.com/go/webrisk v1.9.11/go.mod h1:mK6M8KEO0ZI7VkrjCq3Tjzw4vYq+3c4DzlMUDVaiswE= -cloud.google.com/go/websecurityscanner v1.6.5 h1:YqWZrZYabG88TZt7364XWRJGhxmxhony2ZUyZEYMF2k= cloud.google.com/go/websecurityscanner v1.6.11 h1:r3ePI3YN7ujwX8c9gIkgbVjYVwP4yQA4X2z6P7+HNxI= cloud.google.com/go/websecurityscanner v1.6.11/go.mod h1:vhAZjksELSg58EZfUQ1BMExD+hxqpn0G0DuyCZQjiTg= -cloud.google.com/go/workflows v1.12.4 h1:uHNmUiatTbPQ4H1pabwfzpfEYD4BBnqDHqMm2IesOh4= cloud.google.com/go/workflows v1.12.10 h1:EGJeZmwgE71jxFOI5s9iKST2Bivif3DSzlqVbiXACXQ= cloud.google.com/go/workflows v1.12.10/go.mod h1:RcKqCiOmKs8wFUEf3EwWZPH5eHc7Oq0kamIyOUCk0IE= -code.cloudfoundry.org/clock v1.1.0 h1:XLzC6W3Ah/Y7ht1rmZ6+QfPdt1iGWEAAtIZXgiaj57c= -code.cloudfoundry.org/clock v1.1.0/go.mod h1:yA3fxddT9RINQL2XHS7PS+OXxKCGhfrZmlNUCIM6AKo= -contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9 h1:yxE46rQA0QaqPGqN2UnwXvgCrRqtjR1CsGSWVTRjvv4= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec h1:CSNP8nIEQt4sZEo2sGUiWSmVJ9c5QdyIQvwzZAsn+8Y= contrib.go.opencensus.io/exporter/aws v0.0.0-20230502192102-15967c811cec/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= -contrib.go.opencensus.io/exporter/stackdriver v0.13.10 h1:a9+GZPUe+ONKUwULjlEOucMMG0qfSCCenlji0Nhqbys= contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4= contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc= contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60= @@ -395,38 +262,22 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06 git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e h1:rl2Aq4ZODqTDkeSqQBy+fzpZPamacO1Srp8zq7jf2Sc= -github.com/Azure/azure-amqp-common-go/v3 v3.2.2 h1:CJpxNAGxP7UBhDusRUoaOn0uOorQyAYhQYLnNgkRhlY= github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1 h1:o/Ws6bEqMeKZUfj1RRm3mQ51O8JGU5w+Qdg2AhHib6A= github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.7.1/go.mod h1:6QAMYBAbQeeKX+REFJMZ1nFWu9XLw/PPcjYpuc9RDFs= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+3LoSsYf9YMjkupeAnHMX8O9mmY= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E= -github.com/Azure/azure-service-bus-go v0.11.5 h1:EVMicXGNrSX+rHRCBgm/TRQ4VUZ1m3yAYM/AB2R/SOs= -github.com/Azure/go-amqp v0.16.4 h1:/1oIXrq5zwXLHaoYDliJyiFjJSpJZMWGgtMX9e0/Z30= -github.com/Azure/go-amqp v0.17.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= @@ -437,12 +288,10 @@ github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3 github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= -github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0 h1:YNu23BtH0PKF+fg3ykSorCp6jSTjcEtfnYLzbmcjVRA= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0 h1:kAtNAWwvTt5+iew6baV0kbOrtjYTXPtWNSyOFlcxkBU= github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRBTGfw0Ndv4YqNxlB8zAPJJDxbASE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/IBM/sarama v1.40.1/go.mod h1:+5OFwA5Du9I6QrznhaMHsuwWdWZNMjaBSIxEWEgKOYE= github.com/IBM/sarama v1.43.0 h1:YFFDn8mMI2QL0wOrG0J2sFoVIAFl7hS9JQi2YZsXtJc= github.com/IBM/sarama v1.43.0/go.mod h1:zlE6HEbC/SMQ9mhEYaF7nNLYOUyrs0obySKCckWP9BM= github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= @@ -451,7 +300,6 @@ github.com/KimMachineGun/automemlimit v0.6.0 h1:p/BXkH+K40Hax+PuWWPQ478hPjsp9h1C github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA= github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -464,8 +312,6 @@ github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= -github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= -github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= @@ -492,6 +338,8 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= +github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ= +github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs= github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= @@ -502,24 +350,18 @@ github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhi github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a h1:pv34s756C4pEXnjgPfGYgdhg/ZdajGhyOvzx8k+23nw= github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= github.com/aws/aws-sdk-go v1.44.321/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1 h1:w/fPGB0t5rWwA43mux4e9ozFSH5zF1moQemlA131PWc= -github.com/aws/aws-sdk-go-v2/service/kms v1.16.3 h1:nUP29LA4GZZPihNSo5ZcF4Rl73u+bN5IBRnrQA0jFK4= github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk= github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4 h1:EmIEXOjAdXtxa2OGM1VAajZV/i06Q8qd4kBpJd9/p1k= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4 h1:NgRFYyFpiMD62y4VPXh4DosPFbZd4vdMVBWKk0VmWXc= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4/go.mod h1:TKKN7IQoM7uTnyuFm9bm9cw5P//ZYTl4m3htBWQ1G/c= -github.com/aws/aws-sdk-go-v2/service/sns v1.17.4 h1:7TdmoJJBwLFyakXjfrGztejwY5Ie1JEto7YFfznCmAw= github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 h1:eSTEdxkfle2G98FE+Xl3db/XAXXVTJPNQo9K/Ar8oAI= github.com/aws/aws-sdk-go-v2/service/sns v1.31.3/go.mod h1:1dn0delSO3J69THuty5iwP0US2Glt0mx2qBBlI13pvw= -github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3 h1:uHjK81fESbGy2Y9lspub1+C6VN5W2UXTDo2A/Pm4G0U= github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz7y/WXbN3RgOoVrc= github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI= -github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1 h1:zc1YLcknvxdW/i1MuJKmEnFB2TNkOfguuQaGRvJXPng= github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7M5lu8PqdvOmbRHw= github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -545,7 +387,6 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -561,8 +402,6 @@ github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nC github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= -github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= -github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= @@ -570,11 +409,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMu github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo= @@ -601,8 +437,6 @@ github.com/dave/rebecca v0.9.1 h1:jxVfdOxRirbXL28vXMvUvJ1in3djwkVKXCq339qhBL0= github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= -github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= -github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= @@ -618,10 +452,8 @@ github.com/drone/drone-yaml v1.2.3/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcej github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1 h1:E8hjIYiEyI+1S2XZSLpMkqT9V8+YMljFNBWrFpuVM3A= github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E= github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI= -github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30= github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= -github.com/eapache/go-xerial-snappy v0.0.0-20230111030713-bf00bc1b83b6/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= @@ -629,25 +461,20 @@ github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= -github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/expr-lang/expr v1.16.2 h1:JvMnzUs3LeVHBvGFcXYmXo+Q6DPDmzrlcSBO6Wy3w4s= github.com/expr-lang/expr v1.16.2/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= -github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2 h1:cZqz+yOJ/R64LcKjNQOdARott/jP7BnUQ9Ah7KaZCvw= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8 h1:a9ENSRDFBUPkJ5lCgVZh26+ZbGyoVJG7yb5SSzF5H54= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= @@ -663,14 +490,10 @@ github.com/go-fonts/liberation v0.3.0/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yl github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9 h1:NxXI5pTAtpEaU49bpLpQoDsu1zrteW/vxzTz8Cd2UAs= github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -691,7 +514,6 @@ github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 h1:vF83LI8tAakwEwvWZtr github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= -github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= @@ -700,10 +522,8 @@ github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs0 github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9 h1:OF1IPgv+F4NmqmJ98KTjdN97Vs1JxDPB3vbmYzV2dpk= -github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= -github.com/google/go-replayers/httpreplay v1.1.1 h1:H91sIMlt1NZzN7R+/ASswyouLJfW0WLW7fhyUFvDEkY= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= @@ -712,9 +532,6 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/googleapis/cloud-bigtable-clients-test v0.0.2 h1:S+sCHWAiAc+urcEnvg5JYJUOdlQEm/SEzQ/c/IdAH5M= github.com/googleapis/cloud-bigtable-clients-test v0.0.2/go.mod h1:mk3CrkrouRgtnhID6UZQDK3DrFFa7cYCAJcEmNsHYrY= -github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= -github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= -github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 h1:zC34cGQu69FG7qzJ3WiKW244WfhDC3xxYMeNOX2gtUQ= github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -724,38 +541,18 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grafana/alerting v0.0.0-20240712142914-5558735b4462 h1:MWpvVoPcSej4YfxSIuAllr9vg0UgVEG5CQifD5fK+ps= -github.com/grafana/alerting v0.0.0-20240712142914-5558735b4462/go.mod h1:DLj8frbtCaITljC2jc0L85JQViPF3mPfOSiYhm1osso= -github.com/grafana/authlib v0.0.0-20240611075137-331cbe4e840f/go.mod h1:+MjD5sxxgLOIvw0ox18wJmjBzz8tOECo7quiiZAmgJY= -github.com/grafana/authlib/claims v0.0.0-20240809095826-8eb5495c0b2a/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= -github.com/grafana/authlib/claims v0.0.0-20240814142503-ca29e2d96514/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= -github.com/grafana/authlib/claims v0.0.0-20240827210201-19d5347dd8dd/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/gomemcache v0.0.0-20240229205252-cd6a66d6fb56/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= -github.com/grafana/grafana-plugin-sdk-go v0.235.0/go.mod h1:6n9LbrjGL3xAATntYVNcIi90G9BVHRJjzHKz5FXVfWw= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20240820070818-e7b6b4cf3426/go.mod h1:tN+IWuH3T93r746qR3kOGKoetB0gEd3/e+vR/8oaTf8= -github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b h1:HCbWyVL6vi7gxyO76gQksSPH203oBJ1MJ3JcG1OQlsg= -github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b/go.mod h1:01sXtHoRwI8W324IPAzuxDFOmALqYLCOhvSC2fUHWXc= github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hairyhenderson/go-which v0.2.0 h1:vxoCKdgYc6+MTBzkJYhWegksHjjxuXPNiqo5G2oBM+4= -github.com/hairyhenderson/go-which v0.2.0/go.mod h1:U1BQQRCjxYHfOkXDyCgst7OZVknbqI7KuGKhGnmyIik= github.com/hamba/avro/v2 v2.17.2 h1:6PKpEWzJfNnvBgn7m2/8WYaDOUASxfDU+Jyb4ojDgFY= github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= -github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= -github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek= -github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uoQQ= -github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs= -github.com/hashicorp/consul/api v1.14.0/go.mod h1:bcaw5CSZ7NE9qfOfKCI1xb7ZKjzu/MyvQkCLTfqLqxQ= github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= -github.com/hashicorp/consul/sdk v0.10.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= @@ -763,46 +560,22 @@ github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.4.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/serf v0.10.0/go.mod h1:bXN03oZc5xlH46k/K1qTrpXb9ERKyY1/i/N5mxvgrZw= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hudl/fargo v1.4.0 h1:ZDDILMbB37UlAVLlWcJ2Iz1XuahZZTDZfdCKeclfq2s= -github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= -github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/influxdata/influxdb v1.7.6 h1:8mQ7A/V+3noMGCt/P9pD09ISaiz9XvgCk303UYA3gcs= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= -github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ= -github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= -github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= -github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns= -github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= -github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= -github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1 h1:9Xm8CKtMZIXgcopfdWk/qZ1rt0HjMgfMR9nxxSeK6vk= github.com/jackspirou/syscerts v0.0.0-20160531025014-b68f5469dff1/go.mod h1:zuHl3Hh+e9P6gmBPvcqR1HjkaWHC/csgyskg6IaFKFo= github.com/jaegertracing/jaeger v1.55.0 h1:IJHzKb2B9EYQyKlE7VSoKzNP3emHeqZWnWrKj+kYzzs= github.com/jaegertracing/jaeger v1.55.0/go.mod h1:S884Mz8H+iGI8Ealq6sM9QzSOeU6P+nbFkYw7uww8CI= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= -github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= github.com/jedib0t/go-pretty/v6 v6.2.4 h1:wdaj2KHD2W+mz8JgJ/Q6L/T5dB7kyqEFI16eLq7GEmk= github.com/jedib0t/go-pretty/v6 v6.2.4/go.mod h1:+nE9fyyHGil+PuISTCrp7avEdo6bqoMwqZnuiK2r2a0= github.com/jhump/gopoet v0.1.0 h1:gYjOPnzHd2nzB37xYQZxj4EIQNpBrBskRqQQ3q4ZgSg= @@ -812,8 +585,6 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8 github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jon-whit/go-grpc-prometheus v1.4.0 h1:/wmpGDJcLXuEjXryWhVYEGt9YBRhtLwFEN7T+Flr8sw= github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= @@ -842,11 +613,7 @@ github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3ro github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY= -github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= @@ -857,7 +624,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kshvakov/clickhouse v1.3.5 h1:PDTYk9VYgbjPAWry3AoDREeMgOVUFij6bh6IjlloHL0= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= @@ -883,10 +649,6 @@ github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvls github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= -github.com/microsoft/ApplicationInsights-Go v0.4.4 h1:G4+H9WNs6ygSCe6sUyxRc2U81TI5Es90b2t/MwX5KqY= -github.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U= -github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= -github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= @@ -895,31 +657,24 @@ github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWe github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mithrandie/readline-csvq v1.3.0 h1:VTJEOGouJ8j27jJCD4kBBbNTxM0OdBvE1aY1tMhlqE8= github.com/mithrandie/readline-csvq v1.3.0/go.mod h1:FKyYqDgf/G4SNov7SMFXRWO6LQLXIOeTog/NB97FZl0= -github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= -github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/go-grpc-compression v1.2.2 h1:XaDbnRvt2+1vgr0b/l0qh4mJAfIxE0bKXtz2Znl3GGI= github.com/mostynb/go-grpc-compression v1.2.2/go.mod h1:GOCr2KBxXcblCuczg3YdLQlcin1/NfyDA348ckuCH6w= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= github.com/natessilva/dag v0.0.0-20180124060714-7194b8dcc5c4 h1:dnMxwus89s86tI8rcGVp2HwZzlz7c5o92VOy7dSckBQ= github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI= -github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I= -github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= github.com/nats-io/nats-server/v2 v2.5.0 h1:wsnVaaXH9VRSg+A2MVg5Q727/CqxnmPLGFQ3YZYKTQg= -github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4= -github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= -github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= -github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.97.0 h1:8GH8y3Cq54Ey6He9tyhcVYLfG4TEs/7pp3s6934zNKA= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter v0.97.0/go.mod h1:QBHXt+tHds39B4xGyBkbOx2TST+p8JLWBiXbKKAhNss= @@ -982,13 +737,10 @@ github.com/performancecopilot/speed/v4 v4.0.0 h1:VxEDCmdkfbQYDlcr/GC9YoN9PQ6p8ul github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= -github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= -github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= @@ -996,24 +748,16 @@ github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8 github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM= github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= -github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ= github.com/prometheus/statsd_exporter v0.26.0 h1:SQl3M6suC6NWQYEzOvIv+EF6dAMYEqIuZy+o4H9F5Ig= github.com/prometheus/statsd_exporter v0.26.0/go.mod h1:GXFLADOmBTVDrHc7b04nX8ooq3azG61pnECNqT7O5DM= -github.com/rabbitmq/amqp091-go v1.2.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= -github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= -github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= -github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2 h1:1jfy6i1g66ijpffgfaF/7pIFYZnSZzvo9P9DFkFmRIM= -github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2/go.mod h1:7jOTMgqac46PZcF54q6l2hkLEG8op93fZu61KmxWDV4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/relvacode/iso8601 v1.4.0 h1:GsInVSEJfkYuirYFxa80nMLbH2aydgZpIf52gYZXUJs= github.com/relvacode/iso8601 v1.4.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4 h1:BN/Nyn2nWMoqGRA7G7paDNDqTXE30mXGqzzybrfo05w= -github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= @@ -1033,16 +777,12 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/parquet-cli v0.0.7 h1:rhdZODIbyMS3twr4OM3am8BPPT5pbfMcHLH93whDM5o= github.com/stoewer/parquet-cli v0.0.7/go.mod h1:bskxHdj8q3H1EmfuCqjViFoeO3NEvs5lzZAQvI8Nfjk= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/substrait-io/substrait-go v0.4.2 h1:buDnjsb3qAqTaNbOR7VKmNgXf4lYQxWEcnSGUWBtmN8= github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA= github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= @@ -1110,16 +850,8 @@ github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= -go.einride.tech/aip v0.66.0 h1:XfV+NQX6L7EOYK11yoHHFtndeaWh3KbD9/cN/6iWEt8= go.einride.tech/aip v0.67.1 h1:d/4TW92OxXBngkSOwWS2CH5rez869KpKMaN44mdxkFI= go.einride.tech/aip v0.67.1/go.mod h1:ZGX4/zKw8dcgzdLsrvpOOGxfxI2QSk12SlP7d6c0/XI= -go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= -go.etcd.io/etcd/api/v3 v3.5.13/go.mod h1:gBqlqkcMMZMVTMm4NDZloEVJzxQOQIls8splbqBDa0c= -go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= -go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8= -go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= -go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= -go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.opentelemetry.io/collector v0.97.0 h1:qyOju13byHIKEK/JehmTiGMj4pFLa4kDyrOCtTmjHU0= go.opentelemetry.io/collector v0.97.0/go.mod h1:V6xquYAaO2VHVu4DBK28JYuikRdZajh7DH5Vl/Y8NiA= go.opentelemetry.io/collector/component v0.97.0 h1:vanKhXl5nptN8igRH4PqVYHOILif653vaPIKv6LCZCI= @@ -1184,14 +916,8 @@ go.opentelemetry.io/collector/service v0.95.0 h1:t6RUHV7ByFjkjPKGz5n6n4wIoXZLC8H go.opentelemetry.io/collector/service v0.95.0/go.mod h1:4yappQmDE5UZmLE9wwtj6IPM4W5KGLIYfObEAaejtQc= go.opentelemetry.io/contrib/config v0.4.0 h1:Xb+ncYOqseLroMuBesGNRgVQolXcXOhMj7EhGwJCdHs= go.opentelemetry.io/contrib/config v0.4.0/go.mod h1:drNk2xRqLWW4/amk6Uh1S+sDAJTc7bcEEN1GfJzj418= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0/go.mod h1:SeQhzAEccGVZVEy7aH87Nh0km+utSpo1pTv6eMMop48= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= go.opentelemetry.io/contrib/propagators/b3 v1.23.0 h1:aaIGWc5JdfRGpCafLRxMJbD65MfTa206AwSKkvGS0Hg= go.opentelemetry.io/contrib/propagators/b3 v1.23.0/go.mod h1:Gyz7V7XghvwTq+mIhLFlTgcc03UDroOg8vezs4NLhwU= -go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel/bridge/opencensus v1.26.0 h1:DZzxj9QjznMVoehskOJnFP2gsTCWtDTFBDvFhPAY7nc= go.opentelemetry.io/otel/bridge/opencensus v1.26.0/go.mod h1:rJiX0KrF5m8Tm1XE8jLczpAv5zUaDcvhKecFG0ZoFG4= @@ -1201,129 +927,70 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1 h1:ZqR go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1/go.mod h1:D7ynngPWlGJrqyGSDOdscuv7uqttfCE3jcBvffDv9y4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1 h1:q/Nj5/2TZRIt6PderQ9oU0M00fzoe8UZuINGw6ETGTw= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.23.1/go.mod h1:DTE9yAu6r08jU3xa68GiSeI7oRcSEQ2RpKbbQGO+dWM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/prometheus v0.37.0 h1:NQc0epfL0xItsmGgSXgfbH2C1fq2VLXkZoDFsfRNHpc= -go.opentelemetry.io/otel/exporters/prometheus v0.37.0/go.mod h1:hB8qWjsStK36t50/R0V2ULFb4u95X/Q6zupXLgvjTh8= go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ= go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.23.1 h1:C8r95vDR125t815KD+b1tI0Fbc1pFnwHTBxkbIZ6Szc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.23.1/go.mod h1:Qr0qomr64jentMtOjWMbtYeJMSuMSlsPEjmnRA2sWZ4= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA= -go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= -go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= -go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y= go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= -go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= -google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= -google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw= -google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ= -google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk= -google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag= -google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/genproto v0.0.0-20240528184218-531527333157/go.mod h1:ubQlAQnzejB8uZzszhrTCU2Fyp6Vi7ZE5nn0c3W8+qQ= -google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M= -google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= -google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= -google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= -google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= -google.golang.org/genproto/googleapis/api v0.0.0-20240722135656-d784300faade/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= -google.golang.org/genproto/googleapis/bytestream v0.0.0-20240325203815-454cdb8f5daa h1:wBkzraZsSqhj1M4L/nMrljUU6XasJkgHvUsq8oRGwF0= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf h1:T4tsZBlZYXK3j40sQNP5MBO32I+rn6ypV1PpklsiV8k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:5/MT647Cn/GGhwTpXC7QqcaR5Cnee4v4MKCU1/nwnIQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1331,18 +998,14 @@ gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/code-generator v0.31.0-rc.1 h1:+kAoEeod3OBbeMY8XmD426ZF+AacWthzjRreudcsyL8= -k8s.io/code-generator v0.31.0-rc.1/go.mod h1:HqqhOJxD46ECI95Va1PQPex7+h8SO8CQnErTm9CDNM4= -k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6 h1:WN8Lymy+dCTDHgn4vhUSNIB6U+0sDiv/c9Zdr0UeAnI= -k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6/go.mod h1:l0ukbPS0lwFxOzSq5ZqjutzF+5IL2TLp495PswRPSZk= +k8s.io/code-generator v0.31.0 h1:w607nrMi1KeDKB3/F/J4lIoOgAwc+gV9ZKew4XRfMp8= +k8s.io/code-generator v0.31.0/go.mod h1:84y4w3es8rOJOUUP1rLsIiGlO1JuEaPFXQPA9e/K6U0= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/kube-openapi v0.0.0-20240220201932-37d671a357a5/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= @@ -1355,11 +1018,8 @@ modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= -nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= -nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM= diff --git a/hack/go.mod b/hack/go.mod index 32b2a9e2a43..e85ea1da8b9 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/hack -go 1.23.0 +go 1.23.1 require k8s.io/code-generator v0.31.0 diff --git a/package.json b/package.json index 281fd5bc892..4f8297e540b 100644 --- a/package.json +++ b/package.json @@ -268,7 +268,7 @@ "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/saga-icons": "workspace:*", - "@grafana/scenes": "^5.13.0", + "@grafana/scenes": "5.14.1", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 4b458ca1e3d..c79f0418045 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -201,11 +201,11 @@ export interface FeatureToggles { prometheusAzureOverrideAudience?: boolean; backgroundPluginInstaller?: boolean; dataplaneAggregator?: boolean; - adhocFilterOneOf?: boolean; newFiltersUI?: boolean; lokiSendDashboardPanelNames?: boolean; singleTopNav?: boolean; exploreLogsShardSplitting?: boolean; exploreLogsAggregatedMetrics?: boolean; exploreLogsLimitedTimeRange?: boolean; + appPlatformAccessTokens?: boolean; } diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index 93cb4c8b99a..570437c943d 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -166,9 +166,12 @@ export const availableIconsIndex = { kubernetes: true, 'layer-group': true, 'layers-alt': true, + 'legend-hide': true, + 'legend-show': true, 'library-panel': true, 'line-alt': true, link: true, + 'link-broken': true, 'list-ui-alt': true, 'list-ul': true, 'list-ol': true, diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index b0a8b9710cf..45c453130fa 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -57,7 +57,6 @@ export const Pages = { navV2: 'data-testid Dashboard navigation', publicDashboardTag: 'data-testid public dashboard tag', shareButton: 'data-testid share-button', - settingsButton: 'data-testid settings-button', scrollContainer: 'data-testid Dashboard canvas scroll container', newShareButton: { container: 'data-testid new share button', @@ -240,9 +239,6 @@ export const Pages = { */ dashboards: (title: string) => `Dashboard search item ${title}`, }, - RecentlyDeleted: { - url: '/dashboard/recently-deleted', - }, SaveDashboardAsModal: { newName: 'Save dashboard title field', save: 'Save dashboard button', @@ -379,10 +375,6 @@ export const Pages = { FolderView: { url: '/?search=open&layout=folders', }, - table: { - body: 'data-testid search-table', - row: (name: string) => `data-testid search row ${name}`, - }, }, PublicDashboards: { ListItem: { diff --git a/packages/grafana-icons/svg/asserts.svg b/packages/grafana-icons/svg/asserts.svg index 459045ebb90..31a10a9236b 100644 --- a/packages/grafana-icons/svg/asserts.svg +++ b/packages/grafana-icons/svg/asserts.svg @@ -1,5 +1,14 @@ - - - - + + + + + + + + + + + + + diff --git a/packages/grafana-prometheus/src/result_transformer.test.ts b/packages/grafana-prometheus/src/result_transformer.test.ts index 5dfc9d53ee9..1da1426e73d 100644 --- a/packages/grafana-prometheus/src/result_transformer.test.ts +++ b/packages/grafana-prometheus/src/result_transformer.test.ts @@ -1213,4 +1213,21 @@ describe('Prometheus Result Transformer', () => { expect(transformedTableDataFrames[1].meta?.executedQueryString).toEqual(executedQueryForRefB); }); }); + + it("transforms dataFrame and retains time field's `config.interval`", () => { + const df = createDataFrame({ + refId: 'A', + fields: [ + { name: 'time', type: FieldType.time, values: [1, 2, 3], config: { interval: 1 } }, + { + name: 'value', + type: FieldType.number, + values: [5, 10, 5], + }, + ], + }); + + const tableDf = transformDFToTable([df])[0]; + expect(tableDf.fields[0].config.interval).toEqual(1); + }); }); diff --git a/packages/grafana-prometheus/src/result_transformer.ts b/packages/grafana-prometheus/src/result_transformer.ts index d3bac7fff9c..4916c10e68b 100644 --- a/packages/grafana-prometheus/src/result_transformer.ts +++ b/packages/grafana-prometheus/src/result_transformer.ts @@ -212,6 +212,8 @@ export function transformDFToTable(dfs: DataFrame[]): DataFrame[] { // Fill valueField, timeField and labelFields with values dataFramesByRefId[refId].forEach((df) => { + timeField.config.interval ??= df.fields[0]?.config.interval; + const timeFields = df.fields[0]?.values ?? []; const dataFields = df.fields[1]?.values ?? []; timeFields.forEach((value) => timeField.values.push(value)); diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index c98614764fe..58c2acd67c0 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -84,7 +84,7 @@ "monaco-editor": "0.34.1", "ol": "7.4.0", "prismjs": "1.29.0", - "rc-cascader": "3.27.0", + "rc-cascader": "3.28.1", "rc-drawer": "7.2.0", "rc-slider": "11.1.5", "rc-time-picker": "^3.7.3", diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.internal.story.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx similarity index 82% rename from packages/grafana-ui/src/components/Combobox/Combobox.internal.story.tsx rename to packages/grafana-ui/src/components/Combobox/Combobox.story.tsx index a0bbf86f2ff..20889e6ed1c 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.internal.story.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx @@ -1,8 +1,9 @@ import { action } from '@storybook/addon-actions'; import { Meta, StoryFn, StoryObj } from '@storybook/react'; import { Chance } from 'chance'; -import { ComponentProps, useEffect, useState } from 'react'; +import React, { ComponentProps, useEffect, useState } from 'react'; +import { Alert } from '../Alert/Alert'; import { Field } from '../Forms/Field'; import { Combobox, Option, Value } from './Combobox'; @@ -44,6 +45,7 @@ const meta: Meta = { }, render: (args) => , + decorators: [InDevDecorator], }; const BasicWithState: StoryFn = (args) => { @@ -114,4 +116,24 @@ export const ManyOptions: StoryObj = { render: ManyOptionsStory, }; +export const CustomValue: StoryObj = { + args: { + createCustomValue: true, + }, +}; + export default meta; + +function InDevDecorator(Story: React.ElementType) { + return ( +
                      + + Combobox is still in development and not able to be used externally. +
                      + Within the Grafana repo, it can be used by importing it from{' '} + @grafana/ui/src/unstable +
                      + +
                      + ); +} diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.story.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.story.tsx index f33faef0941..395c3232953 100644 --- a/packages/grafana-ui/src/components/Input/AutoSizeInput.story.tsx +++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.story.tsx @@ -35,6 +35,8 @@ const meta: Meta = { suffixVisible: '', invalid: false, loading: false, + value: '', + defaultValue: '', }, argTypes: { prefixVisible: { @@ -80,6 +82,8 @@ export const Simple: StoryFn = (args) => { type={args.type} placeholder={args.placeholder} minWidth={args.minWidth} + value={args.value} + defaultValue={args.defaultValue} /> ); }; @@ -88,6 +92,8 @@ Simple.args = { before: false, after: false, placeholder: 'Enter your name here...', + value: '', + defaultValue: '', }; export default meta; diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx index 64c34f1ba17..b47fc7da3d9 100644 --- a/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx +++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.test.tsx @@ -1,4 +1,5 @@ import { screen, render, fireEvent, waitFor } from '@testing-library/react'; +import { useEffect, useState } from 'react'; import { measureText } from '../../utils/measureText'; @@ -117,4 +118,30 @@ describe('AutoSizeInput', () => { expect(getComputedStyle(screen.getByTestId('input-wrapper')).width).toBe('32px'); }); + + it('should update the input value if the value prop changes', () => { + // Wrapper component to control the `value` prop + const Wrapper = () => { + const [value, setValue] = useState('Initial'); + + // Simulate prop change after render + useEffect(() => { + setTimeout(() => setValue('Updated'), 100); // Update `value` after 100ms + }, []); + + return ; + }; + + render(); + + const input: HTMLInputElement = screen.getByTestId('autosize-input'); + + // Check initial value + expect(input.value).toBe('Initial'); + + // Wait for the value to update + return waitFor(() => { + expect(input.value).toBe('Updated'); + }); + }); }); diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx index c4fec7c6172..6e5b4ccd837 100644 --- a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx +++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx @@ -15,13 +15,32 @@ export interface Props extends InputProps { } export const AutoSizeInput = React.forwardRef((props, ref) => { - const { defaultValue = '', minWidth = 10, maxWidth, onCommitChange, onKeyDown, onBlur, ...restProps } = props; - const [value, setValue] = React.useState(defaultValue); + const { + defaultValue = '', + minWidth = 10, + maxWidth, + onCommitChange, + onKeyDown, + onBlur, + value: controlledValue, + ...restProps + } = props; + + // Initialize internal state + const [value, setValue] = React.useState(controlledValue ?? defaultValue); const [inputWidth, setInputWidth] = React.useState(minWidth); + // Update internal state when controlled `value` prop changes + useEffect(() => { + if (controlledValue !== undefined) { + setValue(controlledValue); + } + }, [controlledValue]); + + // Update input width when `value`, `minWidth`, or `maxWidth` change useEffect(() => { setInputWidth(getWidthFor(value.toString(), minWidth, maxWidth)); - }, [value, maxWidth, minWidth]); + }, [value, minWidth, maxWidth]); return ( int(query.Pagination.Limit)-1 { + res.Items = res.Items[0 : len(res.Items)-1] + res.Continue = lastID + break + } + } + + if query.UID == "" { + // FIXME: we need to filer for service accounts here.. + res.RV, err = sql.GetResourceVersion(ctx, "user", "updated") + } + + return res, err +} + +type ListServiceAccountTokenQuery struct { + // UID is the service account uid. + UID string + OrgID int64 + Pagination common.Pagination +} + +type ListServiceAccountTokenResult struct { + Items []ServiceAccountToken + Continue int64 + RV int64 +} + +type ServiceAccountToken struct { + ID int64 + Name string + Revoked bool + Expires *int64 + LastUsed *time.Time + Created time.Time + Updated time.Time +} + +var sqlQueryServiceAccountTokensTemplate = mustTemplate("service_account_tokens_query.sql") + +func newListServiceAccountTokens(sql *legacysql.LegacyDatabaseHelper, q *ListServiceAccountTokenQuery) listServiceAccountTokensQuery { + return listServiceAccountTokensQuery{ + SQLTemplate: sqltemplate.New(sql.DialectForDriver()), + UserTable: sql.Table("user"), + OrgUserTable: sql.Table("org_user"), + TokenTable: sql.Table("api_key"), + Query: q, + } +} + +type listServiceAccountTokensQuery struct { + sqltemplate.SQLTemplate + Query *ListServiceAccountTokenQuery + UserTable string + TokenTable string + OrgUserTable string +} + +func (s *legacySQLStore) ListServiceAccountTokens(ctx context.Context, ns claims.NamespaceInfo, query ListServiceAccountTokenQuery) (*ListServiceAccountTokenResult, error) { + // for continue + query.Pagination.Limit += 1 + query.OrgID = ns.OrgID + if ns.OrgID == 0 { + return nil, fmt.Errorf("expected non zero orgID") + } + + sql, err := s.sql(ctx) + if err != nil { + return nil, err + } + + req := newListServiceAccountTokens(sql, &query) + q, err := sqltemplate.Execute(sqlQueryServiceAccountTokensTemplate, req) + if err != nil { + return nil, fmt.Errorf("execute template %q: %w", sqlQueryServiceAccountTokensTemplate.Name(), err) + } + + rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...) + defer func() { + if rows != nil { + _ = rows.Close() + } + }() + + res := &ListServiceAccountTokenResult{} + if err != nil { + return nil, err + } + + var lastID int64 + for rows.Next() { + var t ServiceAccountToken + err := rows.Scan(&t.ID, &t.Name, &t.Revoked, &t.LastUsed, &t.Expires, &t.Created, &t.Updated) + if err != nil { + return res, err + } + + lastID = t.ID + res.Items = append(res.Items, t) + if len(res.Items) > int(query.Pagination.Limit)-1 { + res.Items = res.Items[0 : len(res.Items)-1] + res.Continue = lastID + break + } + } + + return res, err +} diff --git a/pkg/registry/apis/iam/legacy/service_account_tokens_query.sql b/pkg/registry/apis/iam/legacy/service_account_tokens_query.sql new file mode 100644 index 00000000000..021d17d9d5e --- /dev/null +++ b/pkg/registry/apis/iam/legacy/service_account_tokens_query.sql @@ -0,0 +1,19 @@ +SELECT + t.id, + t.name, + t.is_revoked, + t.last_used_at, + t.expires, + t.created, + t.updated + FROM {{ .Ident .TokenTable }} as t + INNER JOIN {{ .Ident .UserTable }} as u ON t.service_account_id = u.id + INNER JOIN {{ .Ident .OrgUserTable }} as o ON u.id = o.user_id +WHERE o.org_id = {{ .Arg .Query.OrgID }} + AND u.is_service_account + AND u.uid = {{ .Arg .Query.UID }} +{{ if .Query.Pagination.Continue }} + AND t.id >= {{ .Arg .Query.Pagination.Continue }} +{{ end }} + ORDER BY t.id asc + LIMIT {{ .Arg .Query.Pagination.Limit }} diff --git a/pkg/registry/apis/iam/legacy/service_accounts_query.sql b/pkg/registry/apis/iam/legacy/service_accounts_query.sql new file mode 100644 index 00000000000..ca700c1c064 --- /dev/null +++ b/pkg/registry/apis/iam/legacy/service_accounts_query.sql @@ -0,0 +1,18 @@ +SELECT + u.id, + u.uid, + u.name, + u.is_disabled, + u.created, + u.updated + FROM {{ .Ident .UserTable }} as u JOIN {{ .Ident .OrgUserTable }} as o ON u.id = o.user_id + WHERE o.org_id = {{ .Arg .Query.OrgID }} + AND u.is_service_account +{{ if .Query.UID }} + AND u.uid = {{ .Arg .Query.UID }} +{{ end }} +{{ if .Query.Pagination.Continue }} + AND u.id >= {{ .Arg .Query.Pagination.Continue }} +{{ end }} + ORDER BY u.id asc + LIMIT {{ .Arg .Query.Pagination.Limit }} diff --git a/pkg/registry/apis/iam/legacy/sql.go b/pkg/registry/apis/iam/legacy/sql.go index 1957f0636c0..554bd114412 100644 --- a/pkg/registry/apis/iam/legacy/sql.go +++ b/pkg/registry/apis/iam/legacy/sql.go @@ -17,6 +17,9 @@ type LegacyIdentityStore interface { ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error) ListUserTeams(ctx context.Context, ns claims.NamespaceInfo, query ListUserTeamsQuery) (*ListUserTeamsResult, error) + ListServiceAccounts(ctx context.Context, ns claims.NamespaceInfo, query ListServiceAccountsQuery) (*ListServiceAccountResult, error) + ListServiceAccountTokens(ctx context.Context, ns claims.NamespaceInfo, query ListServiceAccountTokenQuery) (*ListServiceAccountTokenResult, error) + ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error) ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error) ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error) diff --git a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_1.sql b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_1.sql index d35831279af..4ac1c74d87f 100755 --- a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_1.sql +++ b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_1.sql @@ -2,6 +2,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account ORDER BY u.id asc LIMIT 5 diff --git a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_2.sql b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_2.sql index c32659b6c3d..5bcc865f1d9 100755 --- a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_2.sql +++ b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_page_2.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.id >= 2 ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_uid.sql b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_uid.sql index 5944036d745..2c36c9f9fbb 100755 --- a/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_uid.sql +++ b/pkg/registry/apis/iam/legacy/testdata/mysql--users_query-users_uid.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.uid = 'abc' ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_1.sql b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_1.sql index 5d4f1b771fc..7de4914aa8d 100755 --- a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_1.sql +++ b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_1.sql @@ -2,6 +2,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account ORDER BY u.id asc LIMIT 5 diff --git a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_2.sql b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_2.sql index ed97b2c25d2..e938a14392b 100755 --- a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_2.sql +++ b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_page_2.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.id >= 2 ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_uid.sql b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_uid.sql index bab837475af..5c8f1f2ec66 100755 --- a/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_uid.sql +++ b/pkg/registry/apis/iam/legacy/testdata/postgres--users_query-users_uid.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.uid = 'abc' ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_1.sql b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_1.sql index 5d4f1b771fc..7de4914aa8d 100755 --- a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_1.sql +++ b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_1.sql @@ -2,6 +2,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account ORDER BY u.id asc LIMIT 5 diff --git a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_2.sql b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_2.sql index ed97b2c25d2..e938a14392b 100755 --- a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_2.sql +++ b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_page_2.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.id >= 2 ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_uid.sql b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_uid.sql index bab837475af..5c8f1f2ec66 100755 --- a/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_uid.sql +++ b/pkg/registry/apis/iam/legacy/testdata/sqlite--users_query-users_uid.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id WHERE o.org_id = 0 - AND u.is_service_account = FALSE + AND NOT u.is_service_account AND u.uid = 'abc' ORDER BY u.id asc LIMIT 1 diff --git a/pkg/registry/apis/iam/legacy/user.go b/pkg/registry/apis/iam/legacy/user.go index 7107aae029e..f1f51c43481 100644 --- a/pkg/registry/apis/iam/legacy/user.go +++ b/pkg/registry/apis/iam/legacy/user.go @@ -14,9 +14,8 @@ import ( ) type ListUserQuery struct { - OrgID int64 - UID string - IsServiceAccount bool + OrgID int64 + UID string Pagination common.Pagination } diff --git a/pkg/registry/apis/iam/legacy/users_query.sql b/pkg/registry/apis/iam/legacy/users_query.sql index c7863b0fba5..59d46d39400 100644 --- a/pkg/registry/apis/iam/legacy/users_query.sql +++ b/pkg/registry/apis/iam/legacy/users_query.sql @@ -2,7 +2,7 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name, u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin FROM {{ .Ident .UserTable }} as u JOIN {{ .Ident .OrgUserTable }} as o ON u.id = o.user_id WHERE o.org_id = {{ .Arg .Query.OrgID }} - AND u.is_service_account = {{ .Arg .Query.IsServiceAccount }} + AND NOT u.is_service_account {{ if .Query.UID }} AND u.uid = {{ .Arg .Query.UID }} {{ end }} diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go index 87975306edc..c1536f08aec 100644 --- a/pkg/registry/apis/iam/register.go +++ b/pkg/registry/apis/iam/register.go @@ -4,7 +4,7 @@ import ( "context" "github.com/grafana/grafana/pkg/apimachinery/identity" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" @@ -55,19 +55,19 @@ func RegisterAPIService( } func (b *IdentityAccessManagementAPIBuilder) GetGroupVersion() schema.GroupVersion { - return identityv0.SchemeGroupVersion + return iamv0.SchemeGroupVersion } func (b *IdentityAccessManagementAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { - identityv0.AddKnownTypes(scheme, identityv0.VERSION) + iamv0.AddKnownTypes(scheme, iamv0.VERSION) // Link this version to the internal representation. // This is used for server-side-apply (PATCH), and avoids the error: // "no kind is registered for the type" - identityv0.AddKnownTypes(scheme, runtime.APIVersionInternal) + iamv0.AddKnownTypes(scheme, runtime.APIVersionInternal) - metav1.AddToGroupVersion(scheme, identityv0.SchemeGroupVersion) - return scheme.SetVersionPriority(identityv0.SchemeGroupVersion) + metav1.AddToGroupVersion(scheme, iamv0.SchemeGroupVersion) + return scheme.SetVersionPriority(iamv0.SchemeGroupVersion) } func (b *IdentityAccessManagementAPIBuilder) GetAPIGroupInfo( @@ -76,37 +76,38 @@ func (b *IdentityAccessManagementAPIBuilder) GetAPIGroupInfo( optsGetter generic.RESTOptionsGetter, dualWriteBuilder grafanarest.DualWriteBuilder, ) (*genericapiserver.APIGroupInfo, error) { - apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(identityv0.GROUP, scheme, metav1.ParameterCodec, codecs) + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(iamv0.GROUP, scheme, metav1.ParameterCodec, codecs) storage := map[string]rest.Storage{} - teamResource := identityv0.TeamResourceInfo + teamResource := iamv0.TeamResourceInfo storage[teamResource.StoragePath()] = team.NewLegacyStore(b.Store) storage[teamResource.StoragePath("members")] = team.NewLegacyTeamMemberREST(b.Store) - teamBindingResource := identityv0.TeamBindingResourceInfo + teamBindingResource := iamv0.TeamBindingResourceInfo storage[teamBindingResource.StoragePath()] = team.NewLegacyBindingStore(b.Store) - userResource := identityv0.UserResourceInfo + userResource := iamv0.UserResourceInfo storage[userResource.StoragePath()] = user.NewLegacyStore(b.Store) storage[userResource.StoragePath("teams")] = user.NewLegacyTeamMemberREST(b.Store) - serviceaccountResource := identityv0.ServiceAccountResourceInfo + serviceaccountResource := iamv0.ServiceAccountResourceInfo storage[serviceaccountResource.StoragePath()] = serviceaccount.NewLegacyStore(b.Store) + storage[serviceaccountResource.StoragePath("tokens")] = serviceaccount.NewLegacyTokenREST(b.Store) if b.SSOService != nil { - ssoResource := identityv0.SSOSettingResourceInfo + ssoResource := iamv0.SSOSettingResourceInfo storage[ssoResource.StoragePath()] = sso.NewLegacyStore(b.SSOService) } // The display endpoint -- NOTE, this uses a rewrite hack to allow requests without a name parameter storage["display"] = user.NewLegacyDisplayREST(b.Store) - apiGroupInfo.VersionedResourcesStorageMap[identityv0.VERSION] = storage + apiGroupInfo.VersionedResourcesStorageMap[iamv0.VERSION] = storage return &apiGroupInfo, nil } func (b *IdentityAccessManagementAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { - return identityv0.GetOpenAPIDefinitions + return iamv0.GetOpenAPIDefinitions } func (b *IdentityAccessManagementAPIBuilder) GetAPIRoutes() *builder.APIRoutes { diff --git a/pkg/registry/apis/iam/serviceaccount/rest_token.go b/pkg/registry/apis/iam/serviceaccount/rest_token.go new file mode 100644 index 00000000000..b66f790f381 --- /dev/null +++ b/pkg/registry/apis/iam/serviceaccount/rest_token.go @@ -0,0 +1,115 @@ +package serviceaccount + +import ( + "context" + "net/http" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/rest" + + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + "github.com/grafana/grafana/pkg/registry/apis/iam/common" + "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" +) + +var ( + _ rest.Storage = (*LegacyTokenRest)(nil) + _ rest.Scoper = (*LegacyTokenRest)(nil) + _ rest.StorageMetadata = (*LegacyTokenRest)(nil) + _ rest.Connecter = (*LegacyTokenRest)(nil) +) + +func NewLegacyTokenREST(store legacy.LegacyIdentityStore) *LegacyTokenRest { + return &LegacyTokenRest{store} +} + +type LegacyTokenRest struct { + store legacy.LegacyIdentityStore +} + +// New implements rest.Storage. +func (s *LegacyTokenRest) New() runtime.Object { + return &iamv0.UserTeamList{} +} + +// Destroy implements rest.Storage. +func (s *LegacyTokenRest) Destroy() {} + +// NamespaceScoped implements rest.Scoper. +func (s *LegacyTokenRest) NamespaceScoped() bool { + return true +} + +// ProducesMIMETypes implements rest.StorageMetadata. +func (s *LegacyTokenRest) ProducesMIMETypes(verb string) []string { + return []string{"application/json"} +} + +// ProducesObject implements rest.StorageMetadata. +func (s *LegacyTokenRest) ProducesObject(verb string) interface{} { + return s.New() +} + +// Connect implements rest.Connecter. +func (s *LegacyTokenRest) Connect(ctx context.Context, name string, options runtime.Object, responder rest.Responder) (http.Handler, error) { + ns, err := request.NamespaceInfoFrom(ctx, true) + if err != nil { + return nil, err + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + res, err := s.store.ListServiceAccountTokens(ctx, ns, legacy.ListServiceAccountTokenQuery{ + UID: name, + Pagination: common.PaginationFromListQuery(r.URL.Query()), + }) + if err != nil { + responder.Error(err) + return + } + + list := &iamv0.ServiceAccountTokenList{Items: make([]iamv0.ServiceAccountToken, 0, len(res.Items))} + + for _, t := range res.Items { + list.Items = append(list.Items, mapToToken(t)) + } + + list.ListMeta.Continue = common.OptionalFormatInt(res.Continue) + + responder.Object(http.StatusOK, list) + }), nil +} + +// NewConnectOptions implements rest.Connecter. +func (s *LegacyTokenRest) NewConnectOptions() (runtime.Object, bool, string) { + return nil, false, "" +} + +// ConnectMethods implements rest.Connecter. +func (s *LegacyTokenRest) ConnectMethods() []string { + return []string{http.MethodGet} +} + +func mapToToken(t legacy.ServiceAccountToken) iamv0.ServiceAccountToken { + var expires, lastUsed *metav1.Time + + if t.Expires != nil { + ts := metav1.NewTime(time.Unix(*t.Expires, 0)) + expires = &ts + } + + if t.LastUsed != nil { + ts := metav1.NewTime(*t.LastUsed) + lastUsed = &ts + } + + return iamv0.ServiceAccountToken{ + Name: t.Name, + Expires: expires, + LastUsed: lastUsed, + Revoked: t.Revoked, + Created: metav1.NewTime(t.Created), + } +} diff --git a/pkg/registry/apis/iam/serviceaccount/store.go b/pkg/registry/apis/iam/serviceaccount/store.go index eb5b9521964..618f2089793 100644 --- a/pkg/registry/apis/iam/serviceaccount/store.go +++ b/pkg/registry/apis/iam/serviceaccount/store.go @@ -11,11 +11,10 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/grafana/pkg/apimachinery/utils" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" - "github.com/grafana/grafana/pkg/services/user" ) var ( @@ -26,7 +25,7 @@ var ( _ rest.Storage = (*LegacyStore)(nil) ) -var resource = identityv0.ServiceAccountResourceInfo +var resource = iamv0.ServiceAccountResourceInfo func NewLegacyStore(store legacy.LegacyIdentityStore) *LegacyStore { return &LegacyStore{store} @@ -63,20 +62,18 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt if err != nil { return nil, err } - query := legacy.ListUserQuery{ - OrgID: ns.OrgID, - IsServiceAccount: true, - Pagination: common.PaginationFromListOptions(options), - } - found, err := s.store.ListUsers(ctx, ns, query) + found, err := s.store.ListServiceAccounts(ctx, ns, legacy.ListServiceAccountsQuery{ + OrgID: ns.OrgID, + Pagination: common.PaginationFromListOptions(options), + }) if err != nil { return nil, err } - list := &identityv0.ServiceAccountList{} - for _, item := range found.Users { - list.Items = append(list.Items, *toSAItem(&item, ns.Value)) + list := &iamv0.ServiceAccountList{} + for _, item := range found.Items { + list.Items = append(list.Items, toSAItem(item, ns.Value)) } list.ListMeta.Continue = common.OptionalFormatInt(found.Continue) @@ -85,26 +82,24 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt return list, err } -func toSAItem(u *user.User, ns string) *identityv0.ServiceAccount { - item := &identityv0.ServiceAccount{ +func toSAItem(sa legacy.ServiceAccount, ns string) iamv0.ServiceAccount { + item := iamv0.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: u.UID, + Name: sa.UID, Namespace: ns, - ResourceVersion: fmt.Sprintf("%d", u.Updated.UnixMilli()), - CreationTimestamp: metav1.NewTime(u.Created), + ResourceVersion: fmt.Sprintf("%d", sa.Updated.UnixMilli()), + CreationTimestamp: metav1.NewTime(sa.Created), }, - Spec: identityv0.ServiceAccountSpec{ - Name: u.Name, - Email: u.Email, - EmailVerified: u.EmailVerified, - Disabled: u.IsDisabled, + Spec: iamv0.ServiceAccountSpec{ + Title: sa.Name, + Disabled: sa.Disabled, }, } - obj, _ := utils.MetaAccessor(item) - obj.SetUpdatedTimestamp(&u.Updated) + obj, _ := utils.MetaAccessor(&item) + obj.SetUpdatedTimestamp(&sa.Updated) obj.SetOriginInfo(&utils.ResourceOriginInfo{ Name: "SQL", - Path: strconv.FormatInt(u.ID, 10), + Path: strconv.FormatInt(sa.ID, 10), }) return item } @@ -114,18 +109,19 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO if err != nil { return nil, err } - query := legacy.ListUserQuery{ - OrgID: ns.OrgID, - IsServiceAccount: true, - Pagination: common.Pagination{Limit: 1}, - } - found, err := s.store.ListUsers(ctx, ns, query) + found, err := s.store.ListServiceAccounts(ctx, ns, legacy.ListServiceAccountsQuery{ + UID: name, + OrgID: ns.OrgID, + Pagination: common.Pagination{Limit: 1}, + }) if found == nil || err != nil { return nil, resource.NewNotFound(name) } - if len(found.Users) < 1 { + if len(found.Items) < 1 { return nil, resource.NewNotFound(name) } - return toSAItem(&found.Users[0], ns.Value), nil + + res := toSAItem(found.Items[0], ns.Value) + return &res, nil } diff --git a/pkg/registry/apis/iam/sso/store.go b/pkg/registry/apis/iam/sso/store.go index 699be8b9dfe..c9965adc152 100644 --- a/pkg/registry/apis/iam/sso/store.go +++ b/pkg/registry/apis/iam/sso/store.go @@ -14,7 +14,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/registry/rest" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/ssosettings" ssomodels "github.com/grafana/grafana/pkg/services/ssosettings/models" @@ -30,7 +30,7 @@ var ( _ rest.GracefulDeleter = (*LegacyStore)(nil) ) -var resource = identityv0.SSOSettingResourceInfo +var resource = iamv0.SSOSettingResourceInfo func NewLegacyStore(service ssosettings.Service) *LegacyStore { return &LegacyStore{service} @@ -78,7 +78,7 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt return nil, fmt.Errorf("failed to list sso settings: %w", err) } - list := &identityv0.SSOSettingList{} + list := &iamv0.SSOSettingList{} for _, s := range settings { list.Items = append(list.Items, mapToObject(ns.Value, s)) } @@ -128,7 +128,7 @@ func (s *LegacyStore) Update( return old, created, err } - setting, ok := obj.(*identityv0.SSOSetting) + setting, ok := obj.(*iamv0.SSOSetting) if !ok { return old, created, errors.New("expected ssosetting after update") } @@ -153,7 +153,7 @@ func (s *LegacyStore) Delete( return obj, false, err } - old, ok := obj.(*identityv0.SSOSetting) + old, ok := obj.(*iamv0.SSOSetting) if !ok { return obj, false, errors.New("expected ssosetting") } @@ -182,10 +182,10 @@ func (s *LegacyStore) Delete( return afterDelete, false, err } -func mapToObject(ns string, s *ssomodels.SSOSettings) identityv0.SSOSetting { - source := identityv0.SourceDB +func mapToObject(ns string, s *ssomodels.SSOSettings) iamv0.SSOSetting { + source := iamv0.SourceDB if s.Source == ssomodels.System { - source = identityv0.SourceSystem + source = iamv0.SourceSystem } version := "0" @@ -193,7 +193,7 @@ func mapToObject(ns string, s *ssomodels.SSOSettings) identityv0.SSOSetting { version = fmt.Sprintf("%d", s.Updated.UnixMilli()) } - object := identityv0.SSOSetting{ + object := iamv0.SSOSetting{ ObjectMeta: metav1.ObjectMeta{ Name: s.Provider, Namespace: ns, @@ -201,7 +201,7 @@ func mapToObject(ns string, s *ssomodels.SSOSettings) identityv0.SSOSetting { ResourceVersion: version, CreationTimestamp: metav1.NewTime(s.Updated), }, - Spec: identityv0.SSOSettingSpec{ + Spec: iamv0.SSOSettingSpec{ Source: source, Settings: commonv1.Unstructured{Object: s.Settings}, }, @@ -210,7 +210,7 @@ func mapToObject(ns string, s *ssomodels.SSOSettings) identityv0.SSOSetting { return object } -func mapToModel(obj *identityv0.SSOSetting) *ssomodels.SSOSettings { +func mapToModel(obj *iamv0.SSOSetting) *ssomodels.SSOSettings { return &ssomodels.SSOSettings{ Provider: obj.Name, Settings: obj.Spec.Settings.Object, diff --git a/pkg/registry/apis/iam/team/rest_members.go b/pkg/registry/apis/iam/team/rest_members.go index 13efb6783c1..a3c11e76011 100644 --- a/pkg/registry/apis/iam/team/rest_members.go +++ b/pkg/registry/apis/iam/team/rest_members.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" @@ -33,7 +33,7 @@ type LegacyTeamMemberREST struct { // New implements rest.Storage. func (s *LegacyTeamMemberREST) New() runtime.Object { - return &identityv0.TeamMemberList{} + return &iamv0.TeamMemberList{} } // Destroy implements rest.Storage. @@ -71,7 +71,7 @@ func (s *LegacyTeamMemberREST) Connect(ctx context.Context, name string, options return } - list := &identityv0.TeamMemberList{Items: make([]identityv0.TeamMember, 0, len(res.Members))} + list := &iamv0.TeamMemberList{Items: make([]iamv0.TeamMember, 0, len(res.Members))} for _, m := range res.Members { list.Items = append(list.Items, mapToTeamMember(m)) @@ -95,14 +95,16 @@ func (s *LegacyTeamMemberREST) ConnectMethods() []string { var cfg = &setting.Cfg{} -func mapToTeamMember(m legacy.TeamMember) identityv0.TeamMember { - return identityv0.TeamMember{ - IdentityDisplay: identityv0.IdentityDisplay{ - IdentityType: claims.TypeUser, - UID: m.UserUID, - Display: m.Name, - AvatarURL: dtos.GetGravatarUrlWithDefault(cfg, m.Email, m.Name), - InternalID: m.UserID, +func mapToTeamMember(m legacy.TeamMember) iamv0.TeamMember { + return iamv0.TeamMember{ + IdentityDisplay: iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: claims.TypeUser, + Name: m.UserUID, + }, + DisplayName: m.Name, + AvatarURL: dtos.GetGravatarUrlWithDefault(cfg, m.Email, m.Name), + InternalID: m.UserID, }, External: m.External, Permission: mapPermisson(m.Permission), diff --git a/pkg/registry/apis/iam/team/store.go b/pkg/registry/apis/iam/team/store.go index 5f70e46ac34..ae6f78291fd 100644 --- a/pkg/registry/apis/iam/team/store.go +++ b/pkg/registry/apis/iam/team/store.go @@ -11,7 +11,7 @@ import ( "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/utils" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" @@ -25,7 +25,7 @@ var ( _ rest.Storage = (*LegacyStore)(nil) ) -var resource = identityv0.TeamResourceInfo +var resource = iamv0.TeamResourceInfo func NewLegacyStore(store legacy.LegacyIdentityStore) *LegacyStore { return &LegacyStore{store} @@ -58,25 +58,25 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object, return resource.TableConverter().ConvertToTable(ctx, object, tableOptions) } -func (s *LegacyStore) doList(ctx context.Context, ns claims.NamespaceInfo, query legacy.ListTeamQuery) (*identityv0.TeamList, error) { +func (s *LegacyStore) doList(ctx context.Context, ns claims.NamespaceInfo, query legacy.ListTeamQuery) (*iamv0.TeamList, error) { rsp, err := s.store.ListTeams(ctx, ns, query) if err != nil { return nil, err } - list := &identityv0.TeamList{ + list := &iamv0.TeamList{ ListMeta: metav1.ListMeta{ ResourceVersion: strconv.FormatInt(rsp.RV, 10), }, } for _, team := range rsp.Teams { - item := identityv0.Team{ + item := iamv0.Team{ ObjectMeta: metav1.ObjectMeta{ Name: team.UID, Namespace: ns.Value, CreationTimestamp: metav1.NewTime(team.Created), ResourceVersion: strconv.FormatInt(team.Updated.UnixMilli(), 10), }, - Spec: identityv0.TeamSpec{ + Spec: iamv0.TeamSpec{ Title: team.Name, Email: team.Email, }, diff --git a/pkg/registry/apis/iam/team/store_binding.go b/pkg/registry/apis/iam/team/store_binding.go index b97ecf74a00..1dc1576f5d8 100644 --- a/pkg/registry/apis/iam/team/store_binding.go +++ b/pkg/registry/apis/iam/team/store_binding.go @@ -11,14 +11,14 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/authlib/claims" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/team" ) -var bindingResource = identityv0.TeamBindingResourceInfo +var bindingResource = iamv0.TeamBindingResourceInfo var ( _ rest.Storage = (*LegacyBindingStore)(nil) @@ -102,8 +102,8 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion. return nil, err } - list := identityv0.TeamBindingList{ - Items: make([]identityv0.TeamBinding, 0, len(res.Bindings)), + list := iamv0.TeamBindingList{ + Items: make([]iamv0.TeamBinding, 0, len(res.Bindings)), } for _, b := range res.Bindings { @@ -116,7 +116,7 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion. return &list, nil } -func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) identityv0.TeamBinding { +func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) iamv0.TeamBinding { rv := time.Time{} ct := time.Now() @@ -129,15 +129,15 @@ func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) identityv } } - return identityv0.TeamBinding{ + return iamv0.TeamBinding{ ObjectMeta: metav1.ObjectMeta{ Name: b.TeamUID, Namespace: ns.Value, ResourceVersion: strconv.FormatInt(rv.UnixMilli(), 10), CreationTimestamp: metav1.NewTime(ct), }, - Spec: identityv0.TeamBindingSpec{ - TeamRef: identityv0.TeamRef{ + Spec: iamv0.TeamBindingSpec{ + Team: iamv0.TeamRef{ Name: b.TeamUID, }, Subjects: mapToSubjects(b.Members), @@ -145,21 +145,24 @@ func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) identityv } } -func mapToSubjects(members []legacy.TeamMember) []identityv0.TeamSubject { - out := make([]identityv0.TeamSubject, 0, len(members)) +func mapToSubjects(members []legacy.TeamMember) []iamv0.TeamSubject { + out := make([]iamv0.TeamSubject, 0, len(members)) for _, m := range members { - out = append(out, identityv0.TeamSubject{ - Name: m.MemberID(), + out = append(out, iamv0.TeamSubject{ + Identity: iamv0.IdentityRef{ + Type: claims.TypeUser, + Name: m.UserUID, + }, Permission: common.MapTeamPermission(m.Permission), }) } return out } -func mapPermisson(p team.PermissionType) identityv0.TeamPermission { +func mapPermisson(p team.PermissionType) iamv0.TeamPermission { if p == team.PermissionTypeAdmin { - return identityv0.TeamPermissionAdmin + return iamv0.TeamPermissionAdmin } else { - return identityv0.TeamPermissionMember + return iamv0.TeamPermissionMember } } diff --git a/pkg/registry/apis/iam/user/rest_display.go b/pkg/registry/apis/iam/user/rest_display.go index 480b331114c..4a9940a1e95 100644 --- a/pkg/registry/apis/iam/user/rest_display.go +++ b/pkg/registry/apis/iam/user/rest_display.go @@ -8,7 +8,7 @@ import ( "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" - identity "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/setting" @@ -35,7 +35,7 @@ func NewLegacyDisplayREST(store legacy.LegacyIdentityStore) *LegacyDisplayREST { } func (r *LegacyDisplayREST) New() runtime.Object { - return &identity.IdentityDisplayResults{} + return &iamv0.IdentityDisplayResults{} } func (r *LegacyDisplayREST) Destroy() {} @@ -54,7 +54,7 @@ func (r *LegacyDisplayREST) ProducesMIMETypes(verb string) []string { } func (r *LegacyDisplayREST) ProducesObject(verb string) any { - return &identity.IdentityDisplayResults{} + return &iamv0.IdentityDisplayResults{} } func (r *LegacyDisplayREST) ConnectMethods() []string { @@ -91,21 +91,24 @@ func (r *LegacyDisplayREST) Connect(ctx context.Context, name string, _ runtime. return } - rsp := &identity.IdentityDisplayResults{ + rsp := &iamv0.IdentityDisplayResults{ Keys: keys.keys, InvalidKeys: keys.invalid, - Display: make([]identity.IdentityDisplay, 0, len(users.Users)+len(keys.disp)+1), + Display: make([]iamv0.IdentityDisplay, 0, len(users.Users)+len(keys.disp)+1), } for _, user := range users.Users { - disp := identity.IdentityDisplay{ - IdentityType: claims.TypeUser, - Display: user.NameOrFallback(), - UID: user.UID, + disp := iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: claims.TypeUser, + Name: user.UID, + }, + DisplayName: user.NameOrFallback(), + InternalID: user.ID, } if user.IsServiceAccount { - disp.IdentityType = claims.TypeServiceAccount + disp.Identity.Type = claims.TypeServiceAccount } - disp.AvatarURL = dtos.GetGravatarUrlWithDefault(fakeCfgForGravatar, user.Email, disp.Display) + disp.AvatarURL = dtos.GetGravatarUrlWithDefault(fakeCfgForGravatar, user.Email, disp.DisplayName) rsp.Display = append(rsp.Display, disp) } @@ -124,7 +127,7 @@ type dispKeys struct { invalid []string // For terminal keys, this is a constant - disp []identity.IdentityDisplay + disp []iamv0.IdentityDisplay } func parseKeys(req []string) dispKeys { @@ -145,26 +148,31 @@ func parseKeys(req []string) dispKeys { switch t { case claims.TypeAnonymous: - keys.disp = append(keys.disp, identity.IdentityDisplay{ - IdentityType: t, - Display: "Anonymous", - AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), + keys.disp = append(keys.disp, iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: t, + }, + DisplayName: "Anonymous", + AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), }) continue case claims.TypeAPIKey: - keys.disp = append(keys.disp, identity.IdentityDisplay{ - IdentityType: t, - UID: key, - Display: "API Key", - AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), + keys.disp = append(keys.disp, iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: t, + Name: key, + }, + DisplayName: "API Key", + AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), }) continue case claims.TypeProvisioning: - keys.disp = append(keys.disp, identity.IdentityDisplay{ - IdentityType: t, - UID: "Provisioning", - Display: "Provisioning", - AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), + keys.disp = append(keys.disp, iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: t, + }, + DisplayName: "Provisioning", + AvatarURL: dtos.GetGravatarUrl(fakeCfgForGravatar, string(t)), }) continue default: @@ -176,10 +184,12 @@ func parseKeys(req []string) dispKeys { id, err := strconv.ParseInt(key, 10, 64) if err == nil { if id == 0 { - keys.disp = append(keys.disp, identity.IdentityDisplay{ - IdentityType: claims.TypeUser, - UID: key, - Display: "System admin", + keys.disp = append(keys.disp, iamv0.IdentityDisplay{ + Identity: iamv0.IdentityRef{ + Type: claims.TypeUser, + Name: key, + }, + DisplayName: "System admin", }) continue } diff --git a/pkg/registry/apis/iam/user/rest_user_team.go b/pkg/registry/apis/iam/user/rest_user_team.go index ce086ea3db2..d9d0c22d2ca 100644 --- a/pkg/registry/apis/iam/user/rest_user_team.go +++ b/pkg/registry/apis/iam/user/rest_user_team.go @@ -7,7 +7,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" @@ -30,7 +30,7 @@ type LegacyUserTeamREST struct { // New implements rest.Storage. func (s *LegacyUserTeamREST) New() runtime.Object { - return &identityv0.UserTeamList{} + return &iamv0.UserTeamList{} } // Destroy implements rest.Storage. @@ -68,7 +68,7 @@ func (s *LegacyUserTeamREST) Connect(ctx context.Context, name string, options r return } - list := &identityv0.UserTeamList{Items: make([]identityv0.UserTeam, 0, len(res.Items))} + list := &iamv0.UserTeamList{Items: make([]iamv0.UserTeam, 0, len(res.Items))} for _, m := range res.Items { list.Items = append(list.Items, mapToUserTeam(m)) @@ -90,10 +90,10 @@ func (s *LegacyUserTeamREST) ConnectMethods() []string { return []string{http.MethodGet} } -func mapToUserTeam(t legacy.UserTeam) identityv0.UserTeam { - return identityv0.UserTeam{ +func mapToUserTeam(t legacy.UserTeam) iamv0.UserTeam { + return iamv0.UserTeam{ Title: t.Name, - TeamRef: identityv0.TeamRef{ + TeamRef: iamv0.TeamRef{ Name: t.UID, }, Permission: common.MapTeamPermission(t.Permission), diff --git a/pkg/registry/apis/iam/user/store.go b/pkg/registry/apis/iam/user/store.go index 5e955a978c7..b67ce7cf22a 100644 --- a/pkg/registry/apis/iam/user/store.go +++ b/pkg/registry/apis/iam/user/store.go @@ -11,7 +11,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/grafana/pkg/apimachinery/utils" - identityv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" + iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/iam/common" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" @@ -26,7 +26,7 @@ var ( _ rest.Storage = (*LegacyStore)(nil) ) -var resource = identityv0.UserResourceInfo +var resource = iamv0.UserResourceInfo func NewLegacyStore(store legacy.LegacyIdentityStore) *LegacyStore { return &LegacyStore{store} @@ -65,15 +65,14 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt } found, err := s.store.ListUsers(ctx, ns, legacy.ListUserQuery{ - OrgID: ns.OrgID, - IsServiceAccount: false, - Pagination: common.PaginationFromListOptions(options), + OrgID: ns.OrgID, + Pagination: common.PaginationFromListOptions(options), }) if err != nil { return nil, err } - list := &identityv0.UserList{} + list := &iamv0.UserList{} for _, item := range found.Users { list.Items = append(list.Items, *toUserItem(&item, ns.Value)) } @@ -90,9 +89,8 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO return nil, err } query := legacy.ListUserQuery{ - OrgID: ns.OrgID, - IsServiceAccount: false, - Pagination: common.Pagination{Limit: 1}, + OrgID: ns.OrgID, + Pagination: common.Pagination{Limit: 1}, } found, err := s.store.ListUsers(ctx, ns, query) @@ -105,15 +103,15 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO return toUserItem(&found.Users[0], ns.Value), nil } -func toUserItem(u *user.User, ns string) *identityv0.User { - item := &identityv0.User{ +func toUserItem(u *user.User, ns string) *iamv0.User { + item := &iamv0.User{ ObjectMeta: metav1.ObjectMeta{ Name: u.UID, Namespace: ns, ResourceVersion: fmt.Sprintf("%d", u.Updated.UnixMilli()), CreationTimestamp: metav1.NewTime(u.Created), }, - Spec: identityv0.UserSpec{ + Spec: iamv0.UserSpec{ Name: u.Name, Login: u.Login, Email: u.Email, diff --git a/pkg/semconv/go.mod b/pkg/semconv/go.mod index 81429a73acc..a8a19c15a1c 100644 --- a/pkg/semconv/go.mod +++ b/pkg/semconv/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/pkg/semconv -go 1.23.0 +go 1.23.1 require go.opentelemetry.io/otel v1.29.0 diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index 94e39d71300..7bc0ae81aa6 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -497,6 +497,8 @@ var ( return Scope("settings", "auth."+provider, "*") } + ScopeSettingsLDAP = Scope("settings", "auth.ldap", "*") + // Annotation scopes ScopeAnnotationsRoot = "annotations" ScopeAnnotationsProvider = NewScopeProvider(ScopeAnnotationsRoot) diff --git a/pkg/services/accesscontrol/roles.go b/pkg/services/accesscontrol/roles.go index df358b03246..a113cfd26f4 100644 --- a/pkg/services/accesscontrol/roles.go +++ b/pkg/services/accesscontrol/roles.go @@ -265,6 +265,14 @@ var ( Action: ActionSettingsWrite, Scope: ScopeSettingsOAuth("generic_oauth"), }, + { + Action: ActionSettingsRead, + Scope: ScopeSettingsOAuth("ldap"), + }, + { + Action: ActionSettingsWrite, + Scope: ScopeSettingsOAuth("ldap"), + }, }, } diff --git a/pkg/services/apiserver/builder/helper.go b/pkg/services/apiserver/builder/helper.go index 7b14570c538..1b71e753ad3 100644 --- a/pkg/services/apiserver/builder/helper.go +++ b/pkg/services/apiserver/builder/helper.go @@ -23,16 +23,16 @@ import ( k8stracing "k8s.io/component-base/tracing" "k8s.io/kube-openapi/pkg/common" - "github.com/grafana/grafana/pkg/web" - "github.com/grafana/grafana/pkg/apiserver/endpoints/filters" grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/apiserver/options" ) -// TODO: this is a temporary hack to make rest.Connecter work with resource level routes -var pathRewriters = []filters.PathRewriter{ +type BuildHandlerChainFunc = func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler + +// PathRewriters is a temporary hack to make rest.Connecter work with resource level routes (TODO) +var PathRewriters = []filters.PathRewriter{ { Pattern: regexp.MustCompile(`(/apis/scope.grafana.app/v0alpha1/namespaces/.*/)find/(.*)$`), ReplaceFunc: func(matches []string) string { @@ -53,6 +53,37 @@ var pathRewriters = []filters.PathRewriter{ }, } +func getDefaultBuildHandlerChainFunc(builders []APIGroupBuilder) BuildHandlerChainFunc { + return func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler { + requestHandler, err := GetCustomRoutesHandler( + delegateHandler, + c.LoopbackClientConfig, + builders) + if err != nil { + panic(fmt.Sprintf("could not build the request handler for specified API builders: %s", err.Error())) + } + + // Needs to run last in request chain to function as expected, hence we register it first. + handler := filters.WithTracingHTTPLoggingAttributes(requestHandler) + + // filters.WithRequester needs to be after the K8s chain because it depends on the K8s user in context + handler = filters.WithRequester(handler) + + // Call DefaultBuildHandlerChain on the main entrypoint http.Handler + // See https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/server/config.go#L906 + // DefaultBuildHandlerChain provides many things, notably CORS, HSTS, cache-control, authz and latency tracking + handler = genericapiserver.DefaultBuildHandlerChain(handler, c) + + handler = filters.WithAcceptHeader(handler) + handler = filters.WithPathRewriters(handler, PathRewriters) + handler = k8stracing.WithTracing(handler, c.TracerProvider, "KubernetesAPI") + // Configure filters.WithPanicRecovery to not crash on panic + utilruntime.ReallyCrash = false + + return handler + } +} + func SetupConfig( scheme *runtime.Scheme, serverConfig *genericapiserver.RecommendedConfig, @@ -61,7 +92,7 @@ func SetupConfig( buildVersion string, buildCommit string, buildBranch string, - optionalMiddlewares ...web.Middleware, + buildHandlerChainFunc func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler, ) error { defsGetter := GetOpenAPIDefinitions(builders) serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig( @@ -91,38 +122,10 @@ func SetupConfig( serverConfig.OpenAPIV3Config.Info.Version = buildVersion serverConfig.SkipOpenAPIInstallation = false - serverConfig.BuildHandlerChainFunc = func(delegateHandler http.Handler, c *genericapiserver.Config) http.Handler { - // Call DefaultBuildHandlerChain on the main entrypoint http.Handler - // See https://github.com/kubernetes/apiserver/blob/v0.28.0/pkg/server/config.go#L906 - // DefaultBuildHandlerChain provides many things, notably CORS, HSTS, cache-control, authz and latency tracking - requestHandler, err := getAPIHandler( - delegateHandler, - c.LoopbackClientConfig, - builders) - if err != nil { - panic(fmt.Sprintf("could not build handler chain func: %s", err.Error())) - } + serverConfig.BuildHandlerChainFunc = getDefaultBuildHandlerChainFunc(builders) - // Needs to run last in request chain to function as expected, hence we register it first. - handler := filters.WithTracingHTTPLoggingAttributes(requestHandler) - // filters.WithRequester needs to be after the K8s chain because it depends on the K8s user in context - handler = filters.WithRequester(handler) - handler = genericapiserver.DefaultBuildHandlerChain(handler, c) - - // If optional middlewares include auth function, they need to happen before DefaultBuildHandlerChain - if len(optionalMiddlewares) > 0 { - for _, m := range optionalMiddlewares { - handler = m(handler) - } - } - - handler = filters.WithAcceptHeader(handler) - handler = filters.WithPathRewriters(handler, pathRewriters) - handler = k8stracing.WithTracing(handler, serverConfig.TracerProvider, "KubernetesAPI") - // Configure filters.WithPanicRecovery to not crash on panic - utilruntime.ReallyCrash = false - - return handler + if buildHandlerChainFunc != nil { + serverConfig.BuildHandlerChainFunc = buildHandlerChainFunc } serverConfig.EffectiveVersion = utilversion.DefaultKubeEffectiveVersion() diff --git a/pkg/services/apiserver/builder/request_handler.go b/pkg/services/apiserver/builder/request_handler.go index 38385d7b778..41e5e3e7e0d 100644 --- a/pkg/services/apiserver/builder/request_handler.go +++ b/pkg/services/apiserver/builder/request_handler.go @@ -13,7 +13,7 @@ type requestHandler struct { router *mux.Router } -func getAPIHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder) (http.Handler, error) { +func GetCustomRoutesHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder) (http.Handler, error) { useful := false // only true if any routes exist anywhere router := mux.NewRouter() diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 17867919647..d5fa8746e79 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -298,7 +298,7 @@ func (s *service) start(ctx context.Context) error { if err != nil { return err } - client := resource.NewLocalResourceStoreClient(server) + client := resource.NewLocalResourceClient(server) serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(client, o.RecommendedOptions.Etcd.StorageConfig) @@ -339,6 +339,7 @@ func (s *service) start(ctx context.Context) error { s.cfg.BuildVersion, s.cfg.BuildCommit, s.cfg.BuildBranch, + nil, ) if err != nil { return err @@ -544,9 +545,9 @@ func (s *service) running(ctx context.Context) error { func newResourceStoreClient(conn *grpc.ClientConn, cfg *setting.Cfg) (resource.ResourceStoreClient, error) { if cfg.StackID != "" { - return resource.NewResourceStoreClientCloud(conn, cfg) + return resource.NewResourceClientCloud(conn, cfg) } - return resource.NewResourceStoreClientGRPC(conn) + return resource.NewResourceClientGRPC(conn) } func ensureKubeConfig(restConfig *clientrest.Config, dir string) error { diff --git a/pkg/services/apiserver/standalone/factory.go b/pkg/services/apiserver/standalone/factory.go index 0e5d58fea5e..76d0a68a1da 100644 --- a/pkg/services/apiserver/standalone/factory.go +++ b/pkg/services/apiserver/standalone/factory.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/web" "github.com/prometheus/client_golang/prometheus" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,8 +31,8 @@ type APIServerFactory interface { // Given the flags, what can we produce GetEnabled(runtime []RuntimeConfig) ([]schema.GroupVersion, error) - // Any optional middlewares this factory wants configured via apiserver's BuildHandlerChain facility - GetOptionalMiddlewares(tracer tracing.Tracer) []web.Middleware + // Optional override for apiserver's BuildHandlerChainFunc, return nil if you want to use the grafana's default chain func defined in pkg/services/apiserver/builder/helper.go + GetBuildHandlerChainFunc(tracer tracing.Tracer, builders []builder.APIGroupBuilder) builder.BuildHandlerChainFunc // Make an API server for a given group+version MakeAPIServer(ctx context.Context, tracer tracing.Tracer, gv schema.GroupVersion) (builder.APIGroupBuilder, error) @@ -52,8 +51,8 @@ func (p *DummyAPIFactory) GetOptions() options.OptionsProvider { return nil } -func (p *DummyAPIFactory) GetOptionalMiddlewares(_ tracing.Tracer) []web.Middleware { - return []web.Middleware{} +func (p *DummyAPIFactory) GetBuildHandlerChainFunc(_ tracing.Tracer, builders []builder.APIGroupBuilder) builder.BuildHandlerChainFunc { + return nil } func (p *DummyAPIFactory) GetEnabled(runtime []RuntimeConfig) ([]schema.GroupVersion, error) { diff --git a/pkg/services/cloudmigration/gmsclient/gms_client.go b/pkg/services/cloudmigration/gmsclient/gms_client.go index a831d58aa00..d9804d4e157 100644 --- a/pkg/services/cloudmigration/gmsclient/gms_client.go +++ b/pkg/services/cloudmigration/gmsclient/gms_client.go @@ -65,7 +65,7 @@ func (c *gmsClientImpl) ValidateKey(ctx context.Context, cm cloudmigration.Cloud if resp.StatusCode != 200 { body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("token validation failure: %v", body) + return fmt.Errorf("token validation failure: %v", string(body)) } return nil diff --git a/pkg/services/cloudmigration/objectstorage/s3.go b/pkg/services/cloudmigration/objectstorage/s3.go index dc84553cde1..32bfecfd4f5 100644 --- a/pkg/services/cloudmigration/objectstorage/s3.go +++ b/pkg/services/cloudmigration/objectstorage/s3.go @@ -86,7 +86,7 @@ func (s3 *S3) PresignedURLUpload(ctx context.Context, presignedURL, key string, if response.StatusCode >= 400 { body, _ := io.ReadAll(response.Body) - return fmt.Errorf("unexpected response: status=%d body=%s", response.StatusCode, body) + return fmt.Errorf("unexpected response: status=%d body=%s", response.StatusCode, string(body)) } return nil diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index dc6d06ee8bc..a7f645e3aa2 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1386,12 +1386,6 @@ var ( Owner: grafanaAppPlatformSquad, RequiresRestart: true, }, - { - Name: "adhocFilterOneOf", - Description: "Exposes a new 'one of' operator for ad-hoc filters. This operator allows users to filter by multiple values in a single filter.", - Stage: FeatureStageExperimental, - Owner: grafanaDashboardsSquad, - }, { Name: "newFiltersUI", Description: "Enables new combobox style UI for the Ad hoc filters variable in scenes architecture", @@ -1432,6 +1426,14 @@ var ( FrontendOnly: true, Owner: grafanaObservabilityLogsSquad, }, + { + Name: "appPlatformAccessTokens", + Description: "Enables the use of access tokens for the App Platform", + Stage: FeatureStageExperimental, + Owner: identityAccessTeam, + HideFromDocs: true, + HideFromAdminPage: true, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 407c96eb20b..0ad5a6f19a1 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -182,10 +182,10 @@ cloudwatchMetricInsightsCrossAccount,preview,@grafana/aws-datasources,false,fals prometheusAzureOverrideAudience,deprecated,@grafana/partner-datasources,false,false,false backgroundPluginInstaller,experimental,@grafana/plugins-platform-backend,false,true,false dataplaneAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false -adhocFilterOneOf,experimental,@grafana/dashboards-squad,false,false,false newFiltersUI,experimental,@grafana/dashboards-squad,false,false,false lokiSendDashboardPanelNames,experimental,@grafana/observability-logs,false,false,false singleTopNav,experimental,@grafana/grafana-frontend-platform,false,false,true exploreLogsShardSplitting,experimental,@grafana/observability-logs,false,false,true exploreLogsAggregatedMetrics,experimental,@grafana/observability-logs,false,false,true exploreLogsLimitedTimeRange,experimental,@grafana/observability-logs,false,false,true +appPlatformAccessTokens,experimental,@grafana/identity-access-team,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 09a5c150cc6..544f2d40fd7 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -739,10 +739,6 @@ const ( // Enable grafana dataplane aggregator FlagDataplaneAggregator = "dataplaneAggregator" - // FlagAdhocFilterOneOf - // Exposes a new 'one of' operator for ad-hoc filters. This operator allows users to filter by multiple values in a single filter. - FlagAdhocFilterOneOf = "adhocFilterOneOf" - // FlagNewFiltersUI // Enables new combobox style UI for the Ad hoc filters variable in scenes architecture FlagNewFiltersUI = "newFiltersUI" @@ -766,4 +762,8 @@ const ( // FlagExploreLogsLimitedTimeRange // Used in Explore Logs to limit the time range FlagExploreLogsLimitedTimeRange = "exploreLogsLimitedTimeRange" + + // FlagAppPlatformAccessTokens + // Enables the use of access tokens for the App Platform + FlagAppPlatformAccessTokens = "appPlatformAccessTokens" ) diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 43b2e7a8039..46e9189f4c6 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -52,7 +52,8 @@ "metadata": { "name": "adhocFilterOneOf", "resourceVersion": "1723119716623", - "creationTimestamp": "2024-08-08T12:21:56Z" + "creationTimestamp": "2024-08-08T12:21:56Z", + "deletionTimestamp": "2024-09-05T12:30:12Z" }, "spec": { "description": "Exposes a new 'one of' operator for ad-hoc filters. This operator allows users to filter by multiple values in a single filter.", @@ -335,6 +336,20 @@ "expression": "true" } }, + { + "metadata": { + "name": "appPlatformAccessTokens", + "resourceVersion": "1725549369316", + "creationTimestamp": "2024-09-05T15:16:09Z" + }, + "spec": { + "description": "Enables the use of access tokens for the App Platform", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, { "metadata": { "name": "authAPIAccessTokenAuth", diff --git a/pkg/services/folder/model.go b/pkg/services/folder/model.go index 55cb98dac3e..309c4cc15d4 100644 --- a/pkg/services/folder/model.go +++ b/pkg/services/folder/model.go @@ -54,7 +54,7 @@ type Folder struct { } var GeneralFolder = Folder{ID: 0, Title: "General"} -var RootFolder = &Folder{ID: 0, Title: "Root", UID: GeneralFolderUID, ParentUID: ""} +var RootFolder = &Folder{ID: 0, Title: "Dashbboards", UID: GeneralFolderUID, ParentUID: ""} var SharedWithMeFolder = Folder{ Title: "Shared with me", Description: "Dashboards and folders shared with me", diff --git a/pkg/services/live/live.go b/pkg/services/live/live.go index 5ea854ac625..56eedf5e1ea 100644 --- a/pkg/services/live/live.go +++ b/pkg/services/live/live.go @@ -105,9 +105,10 @@ func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, r // things. For example Node allows to publish messages to channels from server // side with its Publish method. node, err := centrifuge.New(centrifuge.Config{ - LogHandler: handleLog, - LogLevel: centrifuge.LogLevelError, - MetricsNamespace: "grafana_live", + LogHandler: handleLog, + LogLevel: centrifuge.LogLevelError, + MetricsNamespace: "grafana_live", + ClientQueueMaxSize: 4194304, // 4MB // Use reasonably large expiration interval for stream meta key, // much bigger than maximum HistoryLifetime value in Node config. // This way stream meta data will expire, in some cases you may want diff --git a/pkg/services/ngalert/metrics/alertmanager.go b/pkg/services/ngalert/metrics/alertmanager.go index 2ae5bae5210..597694c9785 100644 --- a/pkg/services/ngalert/metrics/alertmanager.go +++ b/pkg/services/ngalert/metrics/alertmanager.go @@ -24,11 +24,12 @@ func NewAlertmanagerMetrics(r prometheus.Registerer) *Alertmanager { } type AlertmanagerConfigMetrics struct { - ConfigHash *prometheus.GaugeVec - Matchers prometheus.Gauge - MatchRE prometheus.Gauge - Match prometheus.Gauge - ObjectMatchers prometheus.Gauge + ConfigHash *prometheus.GaugeVec + ConfigSizeBytes *prometheus.GaugeVec + Matchers prometheus.Gauge + MatchRE prometheus.Gauge + Match prometheus.Gauge + ObjectMatchers prometheus.Gauge } func NewAlertmanagerConfigMetrics(r prometheus.Registerer) *AlertmanagerConfigMetrics { @@ -37,6 +38,10 @@ func NewAlertmanagerConfigMetrics(r prometheus.Registerer) *AlertmanagerConfigMe Name: "alertmanager_config_hash", Help: "The hash of the Alertmanager configuration.", }, []string{"org"}), + ConfigSizeBytes: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "alertmanager_config_size_bytes", + Help: "The size of the grafana alertmanager configuration in bytes", + }, []string{"org"}), Matchers: prometheus.NewGauge(prometheus.GaugeOpts{ Name: "alertmanager_config_matchers", Help: "The total number of matchers", @@ -55,7 +60,7 @@ func NewAlertmanagerConfigMetrics(r prometheus.Registerer) *AlertmanagerConfigMe }), } if r != nil { - r.MustRegister(m.ConfigHash, m.Matchers, m.MatchRE, m.Match, m.ObjectMatchers) + r.MustRegister(m.ConfigHash, m.ConfigSizeBytes, m.Matchers, m.MatchRE, m.Match, m.ObjectMatchers) } return m } diff --git a/pkg/services/ngalert/metrics/multi_org_alertmanager.go b/pkg/services/ngalert/metrics/multi_org_alertmanager.go index 7ed927c901f..3a9cf21f798 100644 --- a/pkg/services/ngalert/metrics/multi_org_alertmanager.go +++ b/pkg/services/ngalert/metrics/multi_org_alertmanager.go @@ -22,7 +22,7 @@ type MultiOrgAlertmanager struct { } func NewMultiOrgAlertmanagerMetrics(r prometheus.Registerer) *MultiOrgAlertmanager { - registries := metrics.NewTenantRegistries(log.New("ngalert.multiorg.alertmanager.metrics")) //TODO: Should this be here? Probably not. + registries := metrics.NewTenantRegistries(log.New("ngalert.multiorg.alertmanager.metrics")) // TODO: Should this be here? Probably not. moa := &MultiOrgAlertmanager{ Registerer: r, registries: registries, @@ -120,6 +120,7 @@ type AlertmanagerAggregatedMetrics struct { objectMatchers *prometheus.Desc configHash *prometheus.Desc + configSize *prometheus.Desc } func NewAlertmanagerAggregatedMetrics(registries *metrics.TenantRegistries) *AlertmanagerAggregatedMetrics { @@ -265,6 +266,11 @@ func NewAlertmanagerAggregatedMetrics(registries *metrics.TenantRegistries) *Ale fmt.Sprintf("%s_%s_alertmanager_config_hash", Namespace, Subsystem), "The hash of the Alertmanager configuration.", []string{"org"}, nil), + + configSize: prometheus.NewDesc( + fmt.Sprintf("%s_%s_alertmanager_config_size_bytes", Namespace, Subsystem), + "The size of the grafana alertmanager configuration in bytes", + []string{"org"}, nil), } return aggregatedMetrics @@ -311,6 +317,7 @@ func (a *AlertmanagerAggregatedMetrics) Describe(out chan<- *prometheus.Desc) { out <- a.objectMatchers out <- a.configHash + out <- a.configSize } func (a *AlertmanagerAggregatedMetrics) Collect(out chan<- prometheus.Metric) { @@ -356,4 +363,5 @@ func (a *AlertmanagerAggregatedMetrics) Collect(out chan<- prometheus.Metric) { data.SendSumOfGauges(out, a.objectMatchers, "alertmanager_config_object_matchers") data.SendMaxOfGaugesPerTenant(out, a.configHash, "alertmanager_config_hash") + data.SendMaxOfGaugesPerTenant(out, a.configSize, "alertmanager_config_size_bytes") } diff --git a/pkg/services/ngalert/notifier/alertmanager.go b/pkg/services/ngalert/notifier/alertmanager.go index 51fecf85b21..ad9e74cd811 100644 --- a/pkg/services/ngalert/notifier/alertmanager.go +++ b/pkg/services/ngalert/notifier/alertmanager.go @@ -91,7 +91,8 @@ func (m maintenanceOptions) MaintenanceFunc(state alertingNotify.State) (int64, func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store AlertingStore, stateStore stateStore, peer alertingNotify.ClusterPeer, decryptFn alertingNotify.GetDecryptedValueFn, ns notifications.Service, - m *metrics.Alertmanager, withAutogen bool) (*alertmanager, error) { + m *metrics.Alertmanager, withAutogen bool, +) (*alertmanager, error) { nflog, err := stateStore.GetNotificationLog(ctx) if err != nil { return nil, err @@ -283,7 +284,7 @@ type AggregateMatchersUsage struct { ObjectMatchers int } -func (am *alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig) { +func (am *alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig, cfgSize int) { var amu AggregateMatchersUsage am.aggregateRouteMatchers(cfg.AlertmanagerConfig.Route, &amu) am.aggregateInhibitMatchers(cfg.AlertmanagerConfig.InhibitRules, &amu) @@ -295,6 +296,10 @@ func (am *alertmanager) updateConfigMetrics(cfg *apimodels.PostableUserConfig) { am.ConfigMetrics.ConfigHash. WithLabelValues(strconv.FormatInt(am.orgID, 10)). Set(hashAsMetricValue(am.Base.ConfigHash())) + + am.ConfigMetrics.ConfigSizeBytes. + WithLabelValues(strconv.FormatInt(am.orgID, 10)). + Set(float64(cfgSize)) } func (am *alertmanager) aggregateRouteMatchers(r *apimodels.Route, amu *AggregateMatchersUsage) { @@ -352,7 +357,7 @@ func (am *alertmanager) applyConfig(cfg *apimodels.PostableUserConfig) (bool, er return false, err } - am.updateConfigMetrics(cfg) + am.updateConfigMetrics(cfg, len(rawConfig)) return true, nil } diff --git a/pkg/services/notifications/smtp_test.go b/pkg/services/notifications/smtp_test.go index e55bcd50639..78e9d76e454 100644 --- a/pkg/services/notifications/smtp_test.go +++ b/pkg/services/notifications/smtp_test.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/textproto" + "sort" "strings" "testing" "time" @@ -301,6 +302,11 @@ func TestSmtpSend(t *testing.T) { messages := srv.MessagesAndPurge() assert.Len(t, messages, 3) + // sort for test consistency + sort.Slice(messages, func(i, j int) bool { + return messages[i].RcpttoRequestResponse()[0][0] < messages[j].RcpttoRequestResponse()[0][0] + }) + for i, sentMsg := range messages { rcpts := sentMsg.RcpttoRequestResponse() assert.EqualValues(t, [][]string{ diff --git a/pkg/services/serviceaccounts/manager/service_test.go b/pkg/services/serviceaccounts/manager/service_test.go index c7175f480c8..9345235039e 100644 --- a/pkg/services/serviceaccounts/manager/service_test.go +++ b/pkg/services/serviceaccounts/manager/service_test.go @@ -14,7 +14,7 @@ import ( ) type FakeServiceAccountStore struct { - ExpectedServiceAccountID *serviceaccounts.ServiceAccount + ExpectedServiceAccountID int64 ExpectedServiceAccountDTO *serviceaccounts.ServiceAccountDTO ExpectedServiceAccountProfileDTO *serviceaccounts.ServiceAccountProfileDTO ExpectedSearchServiceAccountQueryResult *serviceaccounts.SearchOrgServiceAccountsResult @@ -39,7 +39,7 @@ func (f *FakeServiceAccountStore) RetrieveServiceAccount(ctx context.Context, or // RetrieveServiceAccountIdByName is a fake retrieving a service account id by name. func (f *FakeServiceAccountStore) RetrieveServiceAccountIdByName(ctx context.Context, orgID int64, name string) (int64, error) { - return f.ExpectedServiceAccountID.Id, f.ExpectedError + return f.ExpectedServiceAccountID, f.ExpectedError } // CreateServiceAccount is a fake creating a service account. diff --git a/pkg/services/serviceaccounts/models.go b/pkg/services/serviceaccounts/models.go index 777d9de6170..7756e0657f5 100644 --- a/pkg/services/serviceaccounts/models.go +++ b/pkg/services/serviceaccounts/models.go @@ -52,10 +52,6 @@ type MigrationResult struct { FailedDetails []string `json:"failedDetails"` } -type ServiceAccount struct { - Id int64 -} - // swagger:model type CreateServiceAccountForm struct { // example: grafana diff --git a/pkg/services/store/kind/dashboard/dashboard.go b/pkg/services/store/kind/dashboard/dashboard.go index b325acd8c57..6c50b68bf42 100644 --- a/pkg/services/store/kind/dashboard/dashboard.go +++ b/pkg/services/store/kind/dashboard/dashboard.go @@ -396,9 +396,13 @@ func readpanelInfo(iter *jsoniter.Iterator, lookup DatasourceLookup) panelInfo { panel.PluginVersion = iter.ReadString() // since 7x (the saved version for the plugin model) case "libraryPanel": - var v map[string]string + var v map[string]interface{} iter.ReadVal(&v) - panel.LibraryPanel = v["uid"] + if uid, ok := v["uid"]; ok { + if u, isString := uid.(string); isString { + panel.LibraryPanel = u + } + } case "datasource": targets.addDatasource(iter) diff --git a/pkg/services/store/kind/dashboard/dashboard_test.go b/pkg/services/store/kind/dashboard/dashboard_test.go index 94287ad69e0..5be34abbbca 100644 --- a/pkg/services/store/kind/dashboard/dashboard_test.go +++ b/pkg/services/store/kind/dashboard/dashboard_test.go @@ -70,6 +70,7 @@ func TestReadDashboard(t *testing.T) { "mixed-datasource-with-variable", "special-datasource-types", "panels-without-datasources", + "panel-with-library-panel-field", } devdash := "../../../../../devenv/dev-dashboards/" diff --git a/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field-info.json b/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field-info.json new file mode 100644 index 00000000000..1ffaecb605b --- /dev/null +++ b/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field-info.json @@ -0,0 +1,30 @@ +{ + "id": 345, + "title": "The libraryPanel field should get read properly despite the version being an integer", + "tags": null, + "datasource": [ + { + "uid": "default.uid", + "type": "default.type" + } + ], + "panels": [ + { + "id": 454, + "title": "Example title", + "type": "timeseries", + "libraryPanel": "dfkljg98345dkf", + "datasource": [ + { + "uid": "default.uid", + "type": "default.type" + } + ] + } + ], + "schemaVersion": 39, + "linkCount": 0, + "timeFrom": "now-6h", + "timeTo": "now", + "timezone": "" +} \ No newline at end of file diff --git a/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field.json b/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field.json new file mode 100644 index 00000000000..06e9195c90d --- /dev/null +++ b/pkg/services/store/kind/dashboard/testdata/panel-with-library-panel-field.json @@ -0,0 +1,83 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "The libraryPanel field should get read properly despite the version being an integer", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 345, + "links": [], + "panels": [ + { + "description": "", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 15 + }, + "id": 454, + "libraryPanel": { + "name": "example name", + "uid": "dfkljg98345dkf", + "version": 2001 + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "datasource": { + "type": "graphite", + "uid": "000000001" + }, + "refId": "A" + } + ], + "title": "Example title", + "transparent": true, + "type": "timeseries" + } + ], + "preload": false, + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "The libraryPanel field should get read properly despite the version being an integer", + "uid": "sdjf03945834sdkf", + "version": 33, + "weekStart": "" +} diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 26dc5486ec7..82a6ea30b81 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -1,9 +1,9 @@ module github.com/grafana/grafana/pkg/storage/unified/apistore -go 1.23.0 +go 1.23.1 require ( - github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e + github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d @@ -33,7 +33,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.4 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -56,9 +56,10 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/gomega v1.34.1 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.0 // indirect + github.com/prometheus/client_golang v1.20.2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index 2cc871b48f1..7e43c070bc5 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -100,8 +100,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 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/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= -github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= 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-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -163,8 +163,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= -github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e h1:mwDijVnw0/7L/34B/5nypzT7nXuxJt71TrTbZRyESBQ= -github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da/go.mod h1:p09fvU5ujNL/Ig8HB7g4f+S0zyYbQq3x/f0jA4ujVOM= github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da h1:xQMb8cRZYu7D0IO9q/lB7qFQpLGAoPUnCase1CGHrXY= @@ -220,8 +220,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -230,8 +230,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN 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/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= -github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -323,6 +323,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -402,8 +404,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= diff --git a/pkg/storage/unified/apistore/restoptions.go b/pkg/storage/unified/apistore/restoptions.go index 2c8e80c24d4..6c1a4dbc039 100644 --- a/pkg/storage/unified/apistore/restoptions.go +++ b/pkg/storage/unified/apistore/restoptions.go @@ -50,7 +50,7 @@ func NewRESTOptionsGetterMemory(originalStorageConfig storagebackend.Config) (*R return nil, err } return NewRESTOptionsGetterForClient( - resource.NewLocalResourceStoreClient(server), + resource.NewLocalResourceClient(server), originalStorageConfig, ), nil } @@ -84,7 +84,7 @@ func NewRESTOptionsGetterForFile(path string, return nil, err } return NewRESTOptionsGetterForClient( - resource.NewLocalResourceStoreClient(server), + resource.NewLocalResourceClient(server), originalStorageConfig, ), nil } diff --git a/pkg/storage/unified/apistore/watcher_test.go b/pkg/storage/unified/apistore/watcher_test.go index 38510a088b3..fb4deb11811 100644 --- a/pkg/storage/unified/apistore/watcher_test.go +++ b/pkg/storage/unified/apistore/watcher_test.go @@ -97,7 +97,7 @@ func testSetup(t testing.TB, opts ...setupOption) (context.Context, storage.Inte Backend: backend, }) require.NoError(t, err) - client := resource.NewLocalResourceStoreClient(server) + client := resource.NewLocalResourceClient(server) config := storagebackend.NewDefaultConfig(setupOpts.prefix, setupOpts.codec) store, destroyFunc, err := NewStorage( diff --git a/pkg/storage/unified/resource/client_wrapper.go b/pkg/storage/unified/resource/client.go similarity index 93% rename from pkg/storage/unified/resource/client_wrapper.go rename to pkg/storage/unified/resource/client.go index eb0b1b09153..466a31cf994 100644 --- a/pkg/storage/unified/resource/client_wrapper.go +++ b/pkg/storage/unified/resource/client.go @@ -22,7 +22,7 @@ import ( // TODO(drclau): decide on the audience for the resource store const resourceStoreAudience = "resourceStore" -func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient { +func NewLocalResourceClient(server ResourceStoreServer) ResourceStoreClient { // scenario: local in-proc channel := &inprocgrpc.Channel{} @@ -44,7 +44,7 @@ func NewLocalResourceStoreClient(server ResourceStoreServer) ResourceStoreClient return NewResourceStoreClient(grpchan.InterceptClientConn(channel, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)) } -func NewResourceStoreClientGRPC(conn *grpc.ClientConn) (ResourceStoreClient, error) { +func NewResourceClientGRPC(conn *grpc.ClientConn) (ResourceStoreClient, error) { // scenario: remote on-prem clientInt, err := authnlib.NewGrpcClientInterceptor( &authnlib.GrpcClientConfig{}, @@ -59,7 +59,7 @@ func NewResourceStoreClientGRPC(conn *grpc.ClientConn) (ResourceStoreClient, err return NewResourceStoreClient(grpchan.InterceptClientConn(conn, clientInt.UnaryClientInterceptor, clientInt.StreamClientInterceptor)), nil } -func NewResourceStoreClientCloud(conn *grpc.ClientConn, cfg *setting.Cfg) (ResourceStoreClient, error) { +func NewResourceClientCloud(conn *grpc.ClientConn, cfg *setting.Cfg) (ResourceStoreClient, error) { // scenario: remote cloud grpcClientConfig := clientCfgMapping(grpcutils.ReadGrpcClientConfig(cfg)) diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 144e157d1c6..f12630c30b8 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -1,14 +1,14 @@ module github.com/grafana/grafana/pkg/storage/unified/resource -go 1.23.0 +go 1.23.1 require ( github.com/fullstorydev/grpchan v1.1.1 github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e - github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e + github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 - github.com/prometheus/client_golang v1.20.0 + github.com/prometheus/client_golang v1.20.2 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/otel/trace v1.29.0 gocloud.dev v0.39.0 @@ -25,14 +25,11 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/jhump/protoreflect v1.15.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -45,9 +42,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/x448/float16 v0.8.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 67bd51e39da..1246b0a8b1c 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -114,7 +114,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -130,12 +129,13 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksP github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e h1:FhuMCsOqm6wCzlikbmjSGJJ6pDrcC3FeFTQBCqvOfyk= -github.com/grafana/authlib/claims v0.0.0-20240830142353-b79220d2bc2e h1:mwDijVnw0/7L/34B/5nypzT7nXuxJt71TrTbZRyESBQ= +github.com/grafana/authlib v0.0.0-20240903121118-16441568af1e/go.mod h1:PFzXbCrn0GIpN4KwT6NP1l5Z1CPLfmKHnYx8rZzQcyY= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e h1:ng5SopWamGS0MHaCj2e5huWYxAfMeCrj1l/dbJnfiow= +github.com/grafana/authlib/claims v0.0.0-20240903121118-16441568af1e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e h1:3vNpomyzv714Hgls5vn+fC0vgv8wUOSHepUl7PB5nUs= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e/go.mod h1:ORVFiW/KNRY52lNjkGwnFWCxNVfE97bJG2jr2fetq0I= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= @@ -169,8 +169,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI= -github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= +github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= @@ -185,7 +185,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An 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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/pkg/storage/unified/resource/resource.pb.go b/pkg/storage/unified/resource/resource.pb.go index 3ca099325ab..7fa31ba23b1 100644 --- a/pkg/storage/unified/resource/resource.pb.go +++ b/pkg/storage/unified/resource/resource.pb.go @@ -1951,7 +1951,7 @@ type OriginResponse struct { // ResourceVersion of the list response ResourceVersion int64 `protobuf:"varint,3,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` // Error details - Error *ErrorResult `protobuf:"bytes,8,opt,name=error,proto3" json:"error,omitempty"` + Error *ErrorResult `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` } func (x *OriginResponse) Reset() { @@ -2403,7 +2403,7 @@ var file_resource_proto_rawDesc = []byte{ 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x2e, 0x0a, 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, @@ -2446,29 +2446,25 @@ var file_resource_proto_rawDesc = []byte{ 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, - 0x32, 0xc3, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, + 0x32, 0x8c, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x3e, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x57, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, - 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, + 0x57, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48, + 0x0a, 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, + 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2554,22 +2550,20 @@ var file_resource_proto_depIdxs = []int32{ 13, // 32: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest 19, // 33: resource.ResourceStore.List:input_type -> resource.ListRequest 21, // 34: resource.ResourceStore.Watch:input_type -> resource.WatchRequest - 15, // 35: resource.ResourceIndex.Read:input_type -> resource.ReadRequest - 23, // 36: resource.ResourceIndex.History:input_type -> resource.HistoryRequest - 25, // 37: resource.ResourceIndex.Origin:input_type -> resource.OriginRequest - 28, // 38: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest - 16, // 39: resource.ResourceStore.Read:output_type -> resource.ReadResponse - 10, // 40: resource.ResourceStore.Create:output_type -> resource.CreateResponse - 12, // 41: resource.ResourceStore.Update:output_type -> resource.UpdateResponse - 14, // 42: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse - 20, // 43: resource.ResourceStore.List:output_type -> resource.ListResponse - 22, // 44: resource.ResourceStore.Watch:output_type -> resource.WatchEvent - 16, // 45: resource.ResourceIndex.Read:output_type -> resource.ReadResponse - 24, // 46: resource.ResourceIndex.History:output_type -> resource.HistoryResponse - 27, // 47: resource.ResourceIndex.Origin:output_type -> resource.OriginResponse - 29, // 48: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse - 39, // [39:49] is the sub-list for method output_type - 29, // [29:39] is the sub-list for method input_type + 23, // 35: resource.ResourceIndex.History:input_type -> resource.HistoryRequest + 25, // 36: resource.ResourceIndex.Origin:input_type -> resource.OriginRequest + 28, // 37: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest + 16, // 38: resource.ResourceStore.Read:output_type -> resource.ReadResponse + 10, // 39: resource.ResourceStore.Create:output_type -> resource.CreateResponse + 12, // 40: resource.ResourceStore.Update:output_type -> resource.UpdateResponse + 14, // 41: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse + 20, // 42: resource.ResourceStore.List:output_type -> resource.ListResponse + 22, // 43: resource.ResourceStore.Watch:output_type -> resource.WatchEvent + 24, // 44: resource.ResourceIndex.History:output_type -> resource.HistoryResponse + 27, // 45: resource.ResourceIndex.Origin:output_type -> resource.OriginResponse + 29, // 46: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse + 38, // [38:47] is the sub-list for method output_type + 29, // [29:38] is the sub-list for method input_type 29, // [29:29] is the sub-list for extension type_name 29, // [29:29] is the sub-list for extension extendee 0, // [0:29] is the sub-list for field type_name diff --git a/pkg/storage/unified/resource/resource.proto b/pkg/storage/unified/resource/resource.proto index a8d95fca989..e298ff77aa9 100644 --- a/pkg/storage/unified/resource/resource.proto +++ b/pkg/storage/unified/resource/resource.proto @@ -442,8 +442,6 @@ service ResourceStore { service ResourceIndex { // TODO: rpc Search(...) ... eventually a typed response - rpc Read(ReadRequest) returns (ReadResponse); // Duplicated -- for client read only usage - // Show resource history (and trash) rpc History(HistoryRequest) returns (HistoryResponse); diff --git a/pkg/storage/unified/resource/resource_grpc.pb.go b/pkg/storage/unified/resource/resource_grpc.pb.go index bfd2fa2d52a..9e1db6eb635 100644 --- a/pkg/storage/unified/resource/resource_grpc.pb.go +++ b/pkg/storage/unified/resource/resource_grpc.pb.go @@ -348,7 +348,6 @@ var ResourceStore_ServiceDesc = grpc.ServiceDesc{ } const ( - ResourceIndex_Read_FullMethodName = "/resource.ResourceIndex/Read" ResourceIndex_History_FullMethodName = "/resource.ResourceIndex/History" ResourceIndex_Origin_FullMethodName = "/resource.ResourceIndex/Origin" ) @@ -360,7 +359,6 @@ const ( // Unlike the ResourceStore, this service can be exposed to clients directly // It should be implemented with efficient indexes and does not need read-after-write semantics type ResourceIndexClient interface { - Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) // Show resource history (and trash) History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error) // Used for efficient provisioning @@ -375,16 +373,6 @@ func NewResourceIndexClient(cc grpc.ClientConnInterface) ResourceIndexClient { return &resourceIndexClient{cc} } -func (c *resourceIndexClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReadResponse) - err := c.cc.Invoke(ctx, ResourceIndex_Read_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *resourceIndexClient) History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HistoryResponse) @@ -412,7 +400,6 @@ func (c *resourceIndexClient) Origin(ctx context.Context, in *OriginRequest, opt // Unlike the ResourceStore, this service can be exposed to clients directly // It should be implemented with efficient indexes and does not need read-after-write semantics type ResourceIndexServer interface { - Read(context.Context, *ReadRequest) (*ReadResponse, error) // Show resource history (and trash) History(context.Context, *HistoryRequest) (*HistoryResponse, error) // Used for efficient provisioning @@ -423,9 +410,6 @@ type ResourceIndexServer interface { type UnimplementedResourceIndexServer struct { } -func (UnimplementedResourceIndexServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Read not implemented") -} func (UnimplementedResourceIndexServer) History(context.Context, *HistoryRequest) (*HistoryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method History not implemented") } @@ -444,24 +428,6 @@ func RegisterResourceIndexServer(s grpc.ServiceRegistrar, srv ResourceIndexServe s.RegisterService(&ResourceIndex_ServiceDesc, srv) } -func _ResourceIndex_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReadRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ResourceIndexServer).Read(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ResourceIndex_Read_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ResourceIndexServer).Read(ctx, req.(*ReadRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _ResourceIndex_History_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HistoryRequest) if err := dec(in); err != nil { @@ -505,10 +471,6 @@ var ResourceIndex_ServiceDesc = grpc.ServiceDesc{ ServiceName: "resource.ResourceIndex", HandlerType: (*ResourceIndexServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "Read", - Handler: _ResourceIndex_Read_Handler, - }, { MethodName: "History", Handler: _ResourceIndex_History_Handler, diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index 3b401bb1b5f..a31ca6fe37b 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -25,7 +25,6 @@ type ResourceServer interface { ResourceStoreServer ResourceIndexServer DiagnosticsServer - LifecycleHooks } type ListIterator interface { diff --git a/pkg/storage/unified/sql/test/integration_test.go b/pkg/storage/unified/sql/test/integration_test.go index 72cc7df39cb..d27c8d91b57 100644 --- a/pkg/storage/unified/sql/test/integration_test.go +++ b/pkg/storage/unified/sql/test/integration_test.go @@ -357,7 +357,7 @@ func TestClientServer(t *testing.T) { t.Run("Create a client", func(t *testing.T) { conn, err := grpc.NewClient(svc.GetAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) - client, err = resource.NewResourceStoreClientGRPC(conn) + client, err = resource.NewResourceClientGRPC(conn) require.NoError(t, err) }) diff --git a/pkg/tests/apis/playlist/playlist_test.go b/pkg/tests/apis/playlist/playlist_test.go index a362e99433e..d9153363f04 100644 --- a/pkg/tests/apis/playlist/playlist_test.go +++ b/pkg/tests/apis/playlist/playlist_test.go @@ -94,7 +94,7 @@ func TestIntegrationPlaylist(t *testing.T) { DisableAnonymous: true, APIServerStorageType: "file", // write the files to disk UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode0, }, }, @@ -110,7 +110,7 @@ func TestIntegrationPlaylist(t *testing.T) { DisableAnonymous: true, APIServerStorageType: "file", // write the files to disk UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode1, }, }, @@ -126,7 +126,7 @@ func TestIntegrationPlaylist(t *testing.T) { DisableAnonymous: true, APIServerStorageType: "file", // write the files to disk UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode2, }, }, @@ -142,7 +142,7 @@ func TestIntegrationPlaylist(t *testing.T) { DisableAnonymous: true, APIServerStorageType: "file", // write the files to disk UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode3, }, }, @@ -161,7 +161,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode0, }, }, @@ -186,7 +186,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode2, }, }, @@ -202,7 +202,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode3, }, }, @@ -218,7 +218,7 @@ func TestIntegrationPlaylist(t *testing.T) { DisableAnonymous: true, APIServerStorageType: "etcd", // requires etcd running on localhost:2379 UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode0, }, }, @@ -250,7 +250,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode1, }, }, @@ -279,7 +279,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode2, }, }, @@ -308,7 +308,7 @@ func TestIntegrationPlaylist(t *testing.T) { featuremgmt.FlagKubernetesPlaylists, // Required so that legacy calls are also written }, UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{ - playlistv0alpha1.GROUPRESOURCE: { + playlistv0alpha1.RESOURCEGROUP: { DualWriterMode: grafanarest.Mode3, }, }, diff --git a/pkg/web/macaron.go b/pkg/web/macaron.go index 17afc06657a..b7222ca6233 100644 --- a/pkg/web/macaron.go +++ b/pkg/web/macaron.go @@ -142,7 +142,7 @@ func mwFromHandler(handler Handler) Middleware { // a convenience function that is provided for users of contexthandler package (standalone apiservers) // who have an implicit dependency on Macron in context but don't want to take a dependency on // router additionally -func EmptyMacronMiddleware(next http.Handler) http.Handler { +func EmptyMacaronMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { m := New() c := m.createContext(writer, request) diff --git a/public/api-enterprise-spec.json b/public/api-enterprise-spec.json index 06396ffc779..cfcba3e9b58 100644 --- a/public/api-enterprise-spec.json +++ b/public/api-enterprise-spec.json @@ -9439,6 +9439,12 @@ } } }, + "goneError": { + "description": "GoneError is returned when the requested endpoint was removed.", + "schema": { + "$ref": "#/definitions/ErrorResponseBody" + } + }, "helpFlagResponse": { "description": "", "schema": { @@ -9623,12 +9629,6 @@ "$ref": "#/definitions/SuccessResponseBody" } }, - "postAPIkeyResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/NewApiKeyResult" - } - }, "postAnnotationResponse": { "description": "", "schema": { diff --git a/public/api-merged.json b/public/api-merged.json index 8d6476bd4d8..2c9c7078edd 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -2226,8 +2226,8 @@ "operationId": "addAPIkey", "deprecated": true, "responses": { - "301": { - "$ref": "#/responses/statusMovedPermanently" + "410": { + "$ref": "#/responses/goneError" } } } @@ -23806,6 +23806,12 @@ } } }, + "goneError": { + "description": "GoneError is returned when the requested endpoint was removed.", + "schema": { + "$ref": "#/definitions/ErrorResponseBody" + } + }, "helpFlagResponse": { "description": "(empty)", "schema": { @@ -23990,12 +23996,6 @@ "$ref": "#/definitions/SuccessResponseBody" } }, - "postAPIkeyResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/NewApiKeyResult" - } - }, "postAnnotationResponse": { "description": "(empty)", "schema": { diff --git a/public/app/core/components/RolePicker/RolePicker.tsx b/public/app/core/components/RolePicker/RolePicker.tsx index 3df906a381c..e94fb1009d9 100644 --- a/public/app/core/components/RolePicker/RolePicker.tsx +++ b/public/app/core/components/RolePicker/RolePicker.tsx @@ -5,7 +5,12 @@ import { Role, OrgRole } from 'app/types'; import { RolePickerInput } from './RolePickerInput'; import { RolePickerMenu } from './RolePickerMenu'; -import { MENU_MAX_HEIGHT, ROLE_PICKER_MAX_MENU_WIDTH, ROLE_PICKER_WIDTH } from './constants'; +import { + MENU_MAX_HEIGHT, + ROLE_PICKER_MAX_MENU_WIDTH, + ROLE_PICKER_MENU_MAX_WIDTH, + ROLE_PICKER_WIDTH, +} from './constants'; export interface Props { basicRole?: OrgRole; @@ -79,39 +84,38 @@ export const RolePicker = ({ return {}; } const { bottom, top, left, right } = dimensions; + + const spaceBelow = window.innerHeight - bottom; + const spaceAbove = top; + const spaceRight = window.innerWidth - right; + const spaceLeft = left; + let horizontal = left; + let vertical = bottom; let menuToLeft = false; + let menuToTop = false; - const distance = window.innerHeight - bottom; - let vertical = bottom + 5; - let ToTheSide = false; - if (distance < MENU_MAX_HEIGHT + 20) { - // move the menu above the input if there is not enough space below - vertical = top - MENU_MAX_HEIGHT - 50; + // Check vertical space + if (spaceBelow < MENU_MAX_HEIGHT && spaceAbove > spaceBelow) { + vertical = top - MENU_MAX_HEIGHT; + menuToTop = true; } - // check if there is enough space above the input field - if (top < MENU_MAX_HEIGHT + 50) { - // if not, reset the vertical position - vertical = top; - // move the menu to the right edge of the input field - horizontal = right + 5; - // flag to align the menu to the right or left edge of the input field - ToTheSide = true; - } - - /* - * This expression calculates whether there is enough place - * on the right of the RolePicker input to show/fit the role picker menu and its sub menu AND - * whether there is enough place under the RolePicker input to show/fit - * both (the role picker menu and its sub menu) aligned to the left edge of the input. - * Otherwise, it aligns the role picker menu to the right. - */ - if (horizontal + ROLE_PICKER_MAX_MENU_WIDTH > window.innerWidth) { - horizontal = window.innerWidth - (ToTheSide ? left : right); + // Check horizontal space + if (spaceRight < ROLE_PICKER_MENU_MAX_WIDTH && spaceLeft < ROLE_PICKER_MENU_MAX_WIDTH) { + horizontal = right - ROLE_PICKER_MENU_MAX_WIDTH; menuToLeft = true; + } else { + horizontal = Math.max(0, left + (dimensions.width - ROLE_PICKER_MENU_MAX_WIDTH) / 2); } + // Ensure the menu stays within the viewport + horizontal = Math.max(0, Math.min(horizontal, window.innerWidth - ROLE_PICKER_MAX_MENU_WIDTH)); + vertical = Math.max(0, Math.min(vertical, window.innerHeight - MENU_MAX_HEIGHT)); + if (menuToTop) { + // Adjust vertical position to align with the input + vertical -= 48; + } return { horizontal, vertical, menuToLeft }; }; diff --git a/public/app/features/admin/ldap/LdapDrawer.tsx b/public/app/features/admin/ldap/LdapDrawer.tsx index 53e1eb6a96f..e267abf1c15 100644 --- a/public/app/features/admin/ldap/LdapDrawer.tsx +++ b/public/app/features/admin/ldap/LdapDrawer.tsx @@ -5,6 +5,7 @@ import { useFormContext } from 'react-hook-form'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { useStyles2, + Button, CollapsableSection, Divider, Drawer, @@ -25,6 +26,8 @@ import { import { t, Trans } from 'app/core/internationalization'; import { LdapPayload, MapKeyCertConfigured } from 'app/types'; +import { GroupMappingComponent } from './LdapGroupMapping'; + interface Props { onClose: () => void; mapKeyCertConfigured: MapKeyCertConfigured; @@ -90,6 +93,23 @@ export const LdapDrawerComponent = ({ ); + const onAddGroupMapping = () => { + setValue(`${serverConfig}.group_mappings`, [ + ...getValues(`${serverConfig}.group_mappings`), + { + group_dn: '', + org_id: 1, + org_role: 'Viewer', + grafana_admin: false, + }, + ]); + }; + + const onRemoveGroupMapping = (index: number) => { + const groupMappings = getValues(`${serverConfig}.group_mappings`); + setValue(`${serverConfig}.group_mappings`, [...groupMappings.slice(0, index), ...groupMappings.slice(index + 1)]); + }; + return ( @@ -204,7 +224,13 @@ export const LdapDrawerComponent = ({ {...register(`${serverConfig}.group_search_filter_user_attribute`)} /> + {watch('settings.config.servers.0.group_mappings')?.map((_, i) => { + return onRemoveGroupMapping(i)} />; + })} + > = Object.keys(OrgRole).map((key) => { + return { label: key, value: key }; +}); + +interface GroupMappingProps { + onRemove: () => void; + groupMappingIndex: number; +} + +export const GroupMappingComponent = ({ groupMappingIndex, onRemove }: GroupMappingProps) => { + const { getValues, register, setValue } = useFormContext(); + return ( + + + + + + setValue(`settings.config.servers.0.group_mappings.${groupMappingIndex}.org_role`, v)} + /> + + + + + {contextSrv.isGrafanaAdmin && ( + + + + )} + + + ); +}; diff --git a/public/app/features/admin/ldap/LdapSettingsPage.tsx b/public/app/features/admin/ldap/LdapSettingsPage.tsx index 5d5e4885714..cc0cf54015c 100644 --- a/public/app/features/admin/ldap/LdapSettingsPage.tsx +++ b/public/app/features/admin/ldap/LdapSettingsPage.tsx @@ -218,9 +218,19 @@ export const LdapSettingsPage = () => { ); + const disabledFormAlert = ( + + + Your LDAP configuration is not working because the basic login form is currently disabled. Please enable the + login form to use LDAP authentication. You can enable it on the Authentication page under “Auth settings”. + + + ); + return ( + {config.disableLoginForm && disabledFormAlert}
                      {isLoading && } diff --git a/public/app/features/dashboard-scene/inspect/HelpWizard/utils.ts b/public/app/features/dashboard-scene/inspect/HelpWizard/utils.ts index b4ad0c41b37..d3cab702aba 100644 --- a/public/app/features/dashboard-scene/inspect/HelpWizard/utils.ts +++ b/public/app/features/dashboard-scene/inspect/HelpWizard/utils.ts @@ -16,9 +16,8 @@ import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; import { DashboardGridItem } from '../../scene/DashboardGridItem'; import { DashboardScene } from '../../scene/DashboardScene'; -import { LibraryVizPanel } from '../../scene/LibraryVizPanel'; import { gridItemToPanel, vizPanelToPanel } from '../../serialization/transformSceneToSaveModel'; -import { getQueryRunnerFor } from '../../utils/utils'; +import { getQueryRunnerFor, isLibraryPanel } from '../../utils/utils'; import { Randomize, randomizeData } from './randomizer'; @@ -64,11 +63,10 @@ export function getGithubMarkdown(panel: VizPanel, snapshot: string): string { export async function getDebugDashboard(panel: VizPanel, rand: Randomize, timeRange: TimeRange) { let saveModel: ReturnType = { type: '' }; - const isLibraryPanel = panel.parent instanceof LibraryVizPanel; - const gridItem = (isLibraryPanel ? panel.parent.parent : panel.parent) as DashboardGridItem; + const gridItem = panel.parent as DashboardGridItem; const scene = panel.getRoot() as DashboardScene; - if (isLibraryPanel) { + if (isLibraryPanel(panel)) { saveModel = { ...gridItemToPanel(gridItem), ...vizPanelToPanel(panel), @@ -78,7 +76,7 @@ export async function getDebugDashboard(panel: VizPanel, rand: Randomize, timeRa // we want the debug dashboard to include the panel with any changes that were made while // in panel edit mode. const sourcePanel = scene.state.editPanel.state.vizManager.state.sourcePanel.resolve(); - const dashGridItem = sourcePanel.parent instanceof LibraryVizPanel ? sourcePanel.parent.parent : sourcePanel.parent; + const dashGridItem = sourcePanel.parent; if (dashGridItem instanceof DashboardGridItem) { saveModel = { ...gridItemToPanel(dashGridItem), diff --git a/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx b/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx index a357890c034..84e7df9aa7c 100644 --- a/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx +++ b/public/app/features/dashboard-scene/inspect/InspectJsonTab.test.tsx @@ -5,6 +5,8 @@ import { getDefaultTimeRange, LoadingState, PanelData, + PanelPlugin, + PluginType, standardTransformersRegistry, toDataFrame, } from '@grafana/data'; @@ -16,7 +18,7 @@ import { getStandardTransformers } from 'app/features/transformers/standardTrans import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; import { activateFullSceneTree } from '../utils/test-utils'; @@ -25,10 +27,33 @@ import { findVizPanelByKey } from '../utils/utils'; import { InspectJsonTab } from './InspectJsonTab'; standardTransformersRegistry.setInit(getStandardTransformers); +const panelPlugin: PanelPlugin = new PanelPlugin(() => null); +panelPlugin.meta = { + id: 'table', + name: 'Table', + sort: 1, + type: PluginType.panel, + info: { + author: { + name: 'name', + }, + description: '', + links: [], + logos: { + large: '', + small: '', + }, + screenshots: [], + updated: '', + version: '1.0.', + }, + module: '', + baseUrl: '', +}; setPluginImportUtils({ importPanelPlugin: (id: string) => Promise.resolve(getPanelPlugin({})), - getPanelPluginFromCache: (id: string) => undefined, + getPanelPluginFromCache: (id: string) => panelPlugin, }); jest.mock('@grafana/runtime', () => ({ @@ -204,7 +229,29 @@ async function buildTestScene() { } async function buildTestSceneWithLibraryPanel() { - const panel = vizPanelToPanel(buildTestPanel()); + const libraryPanel = new VizPanel({ + title: 'Panel A', + pluginId: 'table', + key: 'panel-12', + $behaviors: [new LibraryPanelBehavior({ title: 'LibraryPanel A title', name: 'LibraryPanel A', uid: '111' })], + titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })], + $data: new SceneDataTransformer({ + transformations: [ + { + id: 'reduce', + options: { + reducers: ['last'], + }, + }, + ], + $data: new SceneQueryRunner({ + datasource: { uid: 'abcdef' }, + queries: [{ refId: 'A' }], + }), + }), + }); + + const panel = vizPanelToPanel(libraryPanel.clone({ $behaviors: undefined })); const libraryPanelState = { name: 'LibraryPanel A', @@ -212,11 +259,20 @@ async function buildTestSceneWithLibraryPanel() { uid: '111', panelKey: 'panel-22', model: panel, + type: 'table', version: 1, }; jest.spyOn(libpanels, 'getLibraryPanel').mockResolvedValue({ ...libraryPanelState, ...panel }); - const libraryPanel = new LibraryVizPanel(libraryPanelState); + + const gridItem = new DashboardGridItem({ + key: 'griditem-1', + x: 0, + y: 0, + width: 10, + height: 12, + body: libraryPanel, + }); const scene = new DashboardScene({ title: 'hello', @@ -225,16 +281,7 @@ async function buildTestSceneWithLibraryPanel() { canEdit: true, }, body: new SceneGridLayout({ - children: [ - new DashboardGridItem({ - key: 'griditem-1', - x: 0, - y: 0, - width: 10, - height: 12, - body: libraryPanel, - }), - ], + children: [gridItem], }), }); @@ -243,7 +290,7 @@ async function buildTestSceneWithLibraryPanel() { await new Promise((r) => setTimeout(r, 1)); const tab = new InspectJsonTab({ - panelRef: libraryPanel.state.panel!.getRef(), + panelRef: libraryPanel.getRef(), onClose: jest.fn(), }); diff --git a/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx b/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx index 8091daf5d8a..c1424bfeeb3 100644 --- a/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx +++ b/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx @@ -27,10 +27,15 @@ import { reportPanelInspectInteraction } from 'app/features/search/page/reportin import { VizPanelManager } from '../panel-edit/VizPanelManager'; import { DashboardGridItem } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { buildGridItemForPanel } from '../serialization/transformSaveModelToScene'; import { gridItemToPanel, vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; -import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; +import { + getDashboardSceneFor, + getLibraryPanelBehavior, + getPanelIdForVizPanel, + getQueryRunnerFor, + isLibraryPanel, +} from '../utils/utils'; export type ShowContent = 'panel-json' | 'panel-data' | 'data-frames'; @@ -142,7 +147,7 @@ export class InspectJsonTab extends SceneObjectBase { const panel = this.state.panelRef.resolve(); // Library panels are not editable from the inspect - if (panel.parent instanceof LibraryVizPanel) { + if (isLibraryPanel(panel)) { return false; } @@ -206,11 +211,11 @@ function getJsonText(show: ShowContent, panel: VizPanel): string { case 'panel-json': { reportPanelInspectInteraction(InspectTab.JSON, 'panelData'); - const isInspectingLibraryPanel = panel.parent instanceof LibraryVizPanel; - const gridItem = isInspectingLibraryPanel ? panel.parent.parent : panel.parent; + const isInspectingLibraryPanel = isLibraryPanel(panel); + const gridItem = panel.parent; if (isInspectingLibraryPanel) { - objToStringify = libraryPanelChildToLegacyRepresentation(panel); + objToStringify = libraryPanelToLegacyRepresentation(panel); break; } @@ -256,19 +261,20 @@ function getJsonText(show: ShowContent, panel: VizPanel): string { /** * - * @param panel Must be child of a LibraryVizPanel that is in turn the child of a DashboardGridItem + * @param panel Must hold a LibraryPanel behavior * @returns object representation of the legacy library panel structure. */ -function libraryPanelChildToLegacyRepresentation(panel: VizPanel<{}, {}>) { - if (!(panel.parent instanceof LibraryVizPanel)) { - throw 'Panel not child of LibraryVizPanel'; +function libraryPanelToLegacyRepresentation(panel: VizPanel<{}, {}>) { + if (!isLibraryPanel(panel)) { + throw 'Panel not a library panel'; } - if (!(panel.parent.parent instanceof DashboardGridItem)) { + const gridItem = panel.parent; + + if (!(gridItem instanceof DashboardGridItem)) { throw 'LibraryPanel not child of DashboardGridItem'; } - const gridItem = panel.parent.parent; const gridPos = { x: gridItem.state.x || 0, y: gridItem.state.y || 0, @@ -276,19 +282,26 @@ function libraryPanelChildToLegacyRepresentation(panel: VizPanel<{}, {}>) { w: gridItem.state.width || 0, }; const libraryPanelObj = vizPanelToLibraryPanel(panel); - const panelObj = vizPanelToPanel(panel, gridPos, false, gridItem); + const panelObj = vizPanelToPanel(panel.clone({ $behaviors: undefined }), gridPos, false, gridItem); return { libraryPanel: { ...libraryPanelObj }, ...panelObj }; } function vizPanelToLibraryPanel(panel: VizPanel): LibraryPanel { - if (!(panel.parent instanceof LibraryVizPanel)) { - throw new Error('Panel not a child of LibraryVizPanel'); + if (!isLibraryPanel(panel)) { + throw new Error('Panel not a Library panel'); } - if (!panel.parent.state._loadedPanel) { + + const libraryPanel = getLibraryPanelBehavior(panel); + + if (!libraryPanel) { + throw new Error('Library panel behavior not found'); + } + + if (!libraryPanel.state._loadedPanel) { throw new Error('Library panel not loaded'); } - return panel.parent.state._loadedPanel; + return libraryPanel.state._loadedPanel; } function hasGridPosChanged(a: SceneGridItemStateLike, b: SceneGridItemStateLike) { diff --git a/public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx b/public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx index 3e850af79b6..dcddc75965f 100644 --- a/public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx +++ b/public/app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo.tsx @@ -3,10 +3,10 @@ import { css } from '@emotion/css'; import { GrafanaTheme2, dateTimeFormat } from '@grafana/data'; import { useStyles2 } from '@grafana/ui'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; interface Props { - libraryPanel: LibraryVizPanel; + libraryPanel: LibraryPanelBehavior; } export const LibraryVizPanelInfo = ({ libraryPanel }: Props) => { diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts b/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts index abca0835fc4..1c18e13fa0d 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditor.test.ts @@ -4,7 +4,7 @@ import * as libAPI from 'app/features/library-panels/state/api'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; import { activateFullSceneTree } from '../utils/test-utils'; @@ -135,20 +135,19 @@ describe('PanelEditor', () => { version: 1, }; - const libraryPanel = new LibraryVizPanel({ + const libPanelBehavior = new LibraryPanelBehavior({ isLoaded: true, title: libraryPanelModel.title, uid: libraryPanelModel.uid, name: libraryPanelModel.name, - panelKey: panel.state.key!, - panel: panel, _loadedPanel: libraryPanelModel, }); - const gridItem = new DashboardGridItem({ body: libraryPanel }); - const apiCall = jest - .spyOn(libAPI, 'updateLibraryVizPanel') - .mockResolvedValue({ type: 'panel', ...libAPI.libraryVizPanelToSaveModel(libraryPanel), version: 2 }); + panel.setState({ + $behaviors: [libPanelBehavior], + }); + + const gridItem = new DashboardGridItem({ body: panel }); const editScene = buildPanelEditScene(panel); const scene = new DashboardScene({ @@ -162,17 +161,23 @@ describe('PanelEditor', () => { activateFullSceneTree(scene); editScene.state.vizManager.state.panel.setState({ title: 'changed title' }); - (editScene.state.vizManager.state.sourcePanel.resolve().parent as LibraryVizPanel).setState({ + (editScene.state.vizManager.state.panel.state.$behaviors![0] as LibraryPanelBehavior).setState({ name: 'changed name', }); + + jest.spyOn(libAPI, 'saveLibPanel').mockImplementation(async (panel) => { + const updatedPanel = { ...libAPI.libraryVizPanelToSaveModel(panel), version: 2 }; + + libPanelBehavior.setPanelFromLibPanel(updatedPanel); + }); + editScene.state.vizManager.commitChanges(); - const calledWith = apiCall.mock.calls[0][0].state; - expect(calledWith.panel?.state.title).toBe('changed title'); - expect(calledWith.name).toBe('changed name'); - await new Promise(process.nextTick); // Wait for mock api to return and update the library panel - expect((gridItem.state.body as LibraryVizPanel).state._loadedPanel?.version).toBe(2); + expect(libPanelBehavior.state._loadedPanel?.version).toBe(2); + expect(libPanelBehavior.state.name).toBe('changed name'); + expect(libPanelBehavior.state.title).toBe('changed title'); + expect((gridItem.state.body as VizPanel).state.title).toBe('changed title'); }); }); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx index 616acfc8238..40546061ae5 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx @@ -5,7 +5,6 @@ import { config, locationService } from '@grafana/runtime'; import { SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes'; import { DashboardGridItem } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { getDashboardSceneFor, getPanelIdForVizPanel } from '../utils/utils'; import { PanelDataPane } from './PanelDataPane/PanelDataPane'; @@ -93,6 +92,7 @@ export class PanelEditor extends SceneObjectBase { } public onDiscard = () => { + this.state.vizManager.setState({ isDirty: false }); this._discardChanges = true; locationService.partial({ editPanel: null }); }; @@ -106,15 +106,7 @@ export class PanelEditor extends SceneObjectBase { const panelManager = this.state.vizManager; const sourcePanel = panelManager.state.sourcePanel.resolve(); - const sourcePanelParent = sourcePanel!.parent; - const isLibraryPanel = sourcePanelParent instanceof LibraryVizPanel; - - const gridItem = isLibraryPanel ? sourcePanelParent.parent : sourcePanelParent; - - if (isLibraryPanel) { - // Library panels handled separately - return; - } + const gridItem = sourcePanel!.parent; if (!(gridItem instanceof DashboardGridItem)) { console.error('Unsupported scene object type'); @@ -155,6 +147,7 @@ export class PanelEditor extends SceneObjectBase { public onConfirmSaveLibraryPanel = () => { this.state.vizManager.commitChanges(); + this.state.vizManager.setState({ isDirty: false }); locationService.partial({ editPanel: null }); }; diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx index b40100ab903..1ac75e7e274 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditorRenderer.tsx @@ -8,7 +8,7 @@ import { Button, ToolbarButton, useStyles2 } from '@grafana/ui'; import { NavToolbarActions } from '../scene/NavToolbarActions'; import { UnlinkModal } from '../scene/UnlinkModal'; -import { getDashboardSceneFor, getLibraryPanel } from '../utils/utils'; +import { getDashboardSceneFor, getLibraryPanelBehavior } from '../utils/utils'; import { PanelEditor } from './PanelEditor'; import { SaveLibraryVizPanelModal } from './SaveLibraryVizPanelModal'; @@ -68,7 +68,7 @@ function VizAndDataPane({ model }: SceneComponentProps) { const dashboard = getDashboardSceneFor(model); const { vizManager, dataPane, showLibraryPanelSaveModal, showLibraryPanelUnlinkModal } = model.useState(); const { sourcePanel } = vizManager.useState(); - const libraryPanel = getLibraryPanel(sourcePanel.resolve()); + const libraryPanel = getLibraryPanelBehavior(sourcePanel.resolve()); const { controls } = dashboard.useState(); const styles = useStyles2(getStyles); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelOptions.test.tsx b/public/app/features/dashboard-scene/panel-edit/PanelOptions.test.tsx index fbecf91ece8..619183a9dd2 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelOptions.test.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelOptions.test.tsx @@ -12,7 +12,7 @@ import { overrideRuleTooltipDescription } from 'app/features/dashboard/component import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; import { activateFullSceneTree } from '../utils/test-utils'; import * as utils from '../utils/utils'; @@ -171,17 +171,19 @@ describe('PanelOptions', () => { version: 1, }; - const libraryPanel = new LibraryVizPanel({ + const libraryPanel = new LibraryPanelBehavior({ isLoaded: true, title: libraryPanelModel.title, uid: libraryPanelModel.uid, name: libraryPanelModel.name, - panelKey: panel.state.key!, - panel: panel, _loadedPanel: libraryPanelModel, }); - new DashboardGridItem({ body: libraryPanel }); + panel.setState({ + $behaviors: [libraryPanel], + }); + + new DashboardGridItem({ body: panel }); const { renderResult, vizManager } = setup({ panel: panel }); @@ -191,7 +193,7 @@ describe('PanelOptions', () => { fireEvent.blur(input, { target: { value: 'new library panel name' } }); }); - expect((vizManager.state.sourcePanel.resolve().parent as LibraryVizPanel).state.name).toBe( + expect((vizManager.state.panel.state.$behaviors![0] as LibraryPanelBehavior).state.name).toBe( 'new library panel name' ); }); diff --git a/public/app/features/dashboard-scene/panel-edit/PanelOptions.tsx b/public/app/features/dashboard-scene/panel-edit/PanelOptions.tsx index 3778f882a2c..d342f9e3ad3 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelOptions.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelOptions.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; import * as React from 'react'; import { PanelData } from '@grafana/data'; +import { VizPanel } from '@grafana/scenes'; import { OptionFilter, renderSearchHits } from 'app/features/dashboard/components/PanelEditor/OptionsPaneOptions'; import { getFieldOverrideCategories } from 'app/features/dashboard/components/PanelEditor/getFieldOverrideElements'; import { @@ -9,7 +10,8 @@ import { getVisualizationOptions2, } from 'app/features/dashboard/components/PanelEditor/getVisualizationOptions'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; +import { getLibraryPanelBehavior, isLibraryPanel } from '../utils/utils'; import { VizPanelManager } from './VizPanelManager'; import { getPanelFrameCategory2 } from './getPanelFrameOptions'; @@ -22,8 +24,7 @@ interface Props { } export const PanelOptions = React.memo(({ vizManager, searchQuery, listMode, data }) => { - const { panel, sourcePanel, repeat } = vizManager.useState(); - const parent = sourcePanel.resolve().parent; + const { panel, repeat } = vizManager.useState(); const { options, fieldConfig, _pluginInstanceState } = panel.useState(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -49,11 +50,17 @@ export const PanelOptions = React.memo(({ vizManager, searchQuery, listMo }, [panel, options, fieldConfig, _pluginInstanceState]); const libraryPanelOptions = useMemo(() => { - if (parent instanceof LibraryVizPanel) { - return getLibraryVizPanelOptionsCategory(parent); + if (panel instanceof VizPanel && isLibraryPanel(panel)) { + const behavior = getLibraryPanelBehavior(panel); + + if (!(behavior instanceof LibraryPanelBehavior)) { + return; + } + + return getLibraryVizPanelOptionsCategory(behavior); } return; - }, [parent]); + }, [panel]); const justOverrides = useMemo( () => diff --git a/public/app/features/dashboard-scene/panel-edit/SaveLibraryVizPanelModal.tsx b/public/app/features/dashboard-scene/panel-edit/SaveLibraryVizPanelModal.tsx index c9538e7bdc7..6fc675762e3 100644 --- a/public/app/features/dashboard-scene/panel-edit/SaveLibraryVizPanelModal.tsx +++ b/public/app/features/dashboard-scene/panel-edit/SaveLibraryVizPanelModal.tsx @@ -5,10 +5,10 @@ import { Button, Icon, Input, Modal, useStyles2 } from '@grafana/ui'; import { getConnectedDashboards } from 'app/features/library-panels/state/api'; import { getModalStyles } from 'app/features/library-panels/styles'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; interface Props { - libraryPanel: LibraryVizPanel; + libraryPanel: LibraryPanelBehavior; isUnsavedPrompt?: boolean; onConfirm: () => void; onDismiss: () => void; diff --git a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx index 7c79f5798cc..887c876bf70 100644 --- a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx +++ b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.test.tsx @@ -20,7 +20,7 @@ import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard/types'; import { DashboardGridItem } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { PanelTimeRange, PanelTimeRangeState } from '../scene/PanelTimeRange'; import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; @@ -286,28 +286,28 @@ describe('VizPanelManager', () => { version: 1, }; - const libraryPanel = new LibraryVizPanel({ + const libPanelBehavior = new LibraryPanelBehavior({ isLoaded: true, title: libraryPanelModel.title, uid: libraryPanelModel.uid, name: libraryPanelModel.name, - panelKey: panel.state.key!, - panel: panel, _loadedPanel: libraryPanelModel, }); - new DashboardGridItem({ body: libraryPanel }); + panel.setState({ + $behaviors: [libPanelBehavior], + }); + + new DashboardGridItem({ body: panel }); const panelManager = VizPanelManager.createFor(panel); - const apiCall = jest - .spyOn(libAPI, 'updateLibraryVizPanel') - .mockResolvedValue({ type: 'panel', ...libAPI.libraryVizPanelToSaveModel(libraryPanel) }); + const apiCall = jest.spyOn(libAPI, 'saveLibPanel'); panelManager.state.panel.setState({ title: 'new title' }); panelManager.commitChanges(); - expect(apiCall.mock.calls[0][0].state.panel?.state.title).toBe('new title'); + expect(apiCall.mock.calls[0][0].state.title).toBe('new title'); }); it('unlinks library panel', () => { @@ -325,23 +325,25 @@ describe('VizPanelManager', () => { version: 1, }; - const libraryPanel = new LibraryVizPanel({ + const libPanelBehavior = new LibraryPanelBehavior({ isLoaded: true, title: libraryPanelModel.title, uid: libraryPanelModel.uid, name: libraryPanelModel.name, - panelKey: panel.state.key!, - panel: panel, _loadedPanel: libraryPanelModel, }); - const gridItem = new DashboardGridItem({ body: libraryPanel }); + panel.setState({ + $behaviors: [libPanelBehavior], + }); + + new DashboardGridItem({ body: panel }); const panelManager = VizPanelManager.createFor(panel); panelManager.unlinkLibraryPanel(); const sourcePanel = panelManager.state.sourcePanel.resolve(); - expect(sourcePanel.parent?.state.key).toBe(gridItem.state.key); + expect(sourcePanel.state.$behaviors).toBe(undefined); }); }); diff --git a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx index 6878fd5e569..c27ed98146c 100644 --- a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx +++ b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx @@ -34,7 +34,7 @@ import { DataQuery, DataTransformerConfig, Panel } from '@grafana/schema'; import { useStyles2 } from '@grafana/ui'; import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard'; import { storeLastUsedDataSourceInLocalStorage } from 'app/features/datasources/components/picker/utils'; -import { updateLibraryVizPanel } from 'app/features/library-panels/state/api'; +import { saveLibPanel } from 'app/features/library-panels/state/api'; import { updateQueries } from 'app/features/query/state/updateQueries'; import { GrafanaQuery } from 'app/plugins/datasource/grafana/types'; import { QueryGroupOptions } from 'app/types'; @@ -42,10 +42,15 @@ import { QueryGroupOptions } from 'app/types'; import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker'; import { getPanelChanges } from '../saving/getDashboardChanges'; import { DashboardGridItem, RepeatDirection } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { PanelTimeRange, PanelTimeRangeState } from '../scene/PanelTimeRange'; import { gridItemToPanel, vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; -import { getDashboardSceneFor, getMultiVariableValues, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; +import { + getDashboardSceneFor, + getMultiVariableValues, + getPanelIdForVizPanel, + getQueryRunnerFor, + isLibraryPanel, +} from '../utils/utils'; export interface VizPanelManagerState extends SceneObjectState { panel: VizPanel; @@ -85,7 +90,7 @@ export class VizPanelManager extends SceneObjectBase { public static createFor(sourcePanel: VizPanel) { let repeatOptions: Pick = {}; - const gridItem = sourcePanel.parent instanceof LibraryVizPanel ? sourcePanel.parent.parent : sourcePanel.parent; + const gridItem = sourcePanel.parent; if (!(gridItem instanceof DashboardGridItem)) { console.error('VizPanel is not a child of a dashboard grid item'); @@ -143,8 +148,8 @@ export class VizPanelManager extends SceneObjectBase { private _detectPanelModelChanges = debounce(() => { const { hasChanges } = getPanelChanges( - vizPanelToPanel(this.state.sourcePanel.resolve()), - vizPanelToPanel(this.state.panel) + vizPanelToPanel(this.state.sourcePanel.resolve().clone({ $behaviors: undefined })), + vizPanelToPanel(this.state.panel.clone({ $behaviors: undefined })) ); this.setState({ isDirty: hasChanges }); }, 250); @@ -263,10 +268,7 @@ export class VizPanelManager extends SceneObjectBase { }); } - this.setState({ - pluginId, - }); - + this.setState({ pluginId }); this.state.panel.changePluginType(pluginId, cachedOptions, newFieldConfig); this.loadDataSource(); @@ -405,21 +407,22 @@ export class VizPanelManager extends SceneObjectBase { public unlinkLibraryPanel() { const sourcePanel = this.state.sourcePanel.resolve(); - if (!(sourcePanel.parent instanceof LibraryVizPanel)) { - throw new Error('VizPanel is not a child of a library panel'); + if (!isLibraryPanel(sourcePanel)) { + throw new Error('VizPanel is not a library panel'); } - const gridItem = sourcePanel.parent.parent; + const gridItem = sourcePanel.parent; if (!(gridItem instanceof DashboardGridItem)) { throw new Error('Library panel not a child of a grid item'); } - const newSourcePanel = this.state.panel.clone({ $data: this.state.$data?.clone() }); + const newSourcePanel = this.state.panel.clone({ $data: sourcePanel.state.$data?.clone(), $behaviors: undefined }); gridItem.setState({ body: newSourcePanel, }); + this.state.panel.setState({ $behaviors: undefined }); this.setState({ sourcePanel: newSourcePanel.getRef() }); } @@ -435,30 +438,17 @@ export class VizPanelManager extends SceneObjectBase { maxPerRow: this.state.maxPerRow, }; + const vizPanel = this.state.panel.clone(); + if (sourcePanel.parent instanceof DashboardGridItem) { sourcePanel.parent.setState({ ...repeatUpdate, - body: this.state.panel.clone(), + body: vizPanel, }); } - if (sourcePanel.parent instanceof LibraryVizPanel) { - if (sourcePanel.parent.parent instanceof DashboardGridItem) { - const newLibPanel = sourcePanel.parent.clone({ - panel: this.state.panel.clone(), - }); - - sourcePanel.parent.parent.setState({ - body: newLibPanel, - ...repeatUpdate, - }); - - updateLibraryVizPanel(newLibPanel!).then((p) => { - if (sourcePanel.parent instanceof LibraryVizPanel) { - newLibPanel.setPanelFromLibPanel(p); - } - }); - } + if (isLibraryPanel(vizPanel)) { + saveLibPanel(vizPanel); } } @@ -467,9 +457,7 @@ export class VizPanelManager extends SceneObjectBase { */ public getPanelSaveModel(): Panel | object { const sourcePanel = this.state.sourcePanel.resolve(); - - const isLibraryPanel = sourcePanel.parent instanceof LibraryVizPanel; - const gridItem = isLibraryPanel ? sourcePanel.parent.parent : sourcePanel.parent; + const gridItem = sourcePanel.parent; if (!(gridItem instanceof DashboardGridItem)) { return { error: 'Unsupported panel parent' }; diff --git a/public/app/features/dashboard-scene/saving/DashboardPrompt.tsx b/public/app/features/dashboard-scene/saving/DashboardPrompt.tsx index f7e737d3996..e841f7a47ba 100644 --- a/public/app/features/dashboard-scene/saving/DashboardPrompt.tsx +++ b/public/app/features/dashboard-scene/saving/DashboardPrompt.tsx @@ -8,7 +8,9 @@ import { Dashboard } from '@grafana/schema/dist/esm/index.gen'; import { ModalsContext, Modal, Button, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; +import { SaveLibraryVizPanelModal } from '../panel-edit/SaveLibraryVizPanelModal'; import { DashboardScene } from '../scene/DashboardScene'; +import { getLibraryPanelBehavior, isLibraryPanel } from '../utils/utils'; interface DashboardPromptProps { dashboard: DashboardScene; @@ -38,29 +40,39 @@ export const DashboardPrompt = memo(({ dashboard }: DashboardPromptProps) => { }, [dashboard]); const onHistoryBlock = (location: H.Location) => { - // const panelInEdit = dashboard.state.editPanel; - // const search = new URLSearchParams(location.search); + const panelInEdit = dashboard.state.editPanel; + const vizPanelManager = panelInEdit?.state.vizManager; + const vizPanel = vizPanelManager?.state.panel; + const search = new URLSearchParams(location.search); - // TODO: Are we leaving panel edit & library panel? + // Are we leaving panel edit & library panel? + if ( + panelInEdit && + vizPanel && + isLibraryPanel(vizPanel) && + vizPanelManager.state.isDirty && + !search.has('editPanel') + ) { + const libPanelBehavior = getLibraryPanelBehavior(vizPanel); - // if (panelInEdit && panelInEdit.libraryPanel && panelInEdit.hasChanged && !search.has('editPanel')) { - // showModal(SaveLibraryPanelModal, { - // isUnsavedPrompt: true, - // panel: dashboard.panelInEdit as PanelModelWithLibraryPanel, - // folderUid: dashboard.meta.folderUid ?? '', - // onConfirm: () => { - // hideModal(); - // moveToBlockedLocationAfterReactStateUpdate(location); - // }, - // onDiscard: () => { - // dispatch(discardPanelChanges()); - // moveToBlockedLocationAfterReactStateUpdate(location); - // hideModal(); - // }, - // onDismiss: hideModal, - // }); - // return false; - // } + showModal(SaveLibraryVizPanelModal, { + dashboard, + isUnsavedPrompt: true, + libraryPanel: libPanelBehavior!, + onConfirm: () => { + panelInEdit.onConfirmSaveLibraryPanel(); + hideModal(); + moveToBlockedLocationAfterReactStateUpdate(location); + }, + onDiscard: () => { + panelInEdit.onDiscard(); + hideModal(); + moveToBlockedLocationAfterReactStateUpdate(location); + }, + onDismiss: hideModal, + }); + return false; + } // Are we still on the same dashboard? if (originalPath === location.pathname) { diff --git a/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.test.ts b/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.test.ts index 413035cf22b..474907a347f 100644 --- a/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.test.ts +++ b/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.test.ts @@ -1,7 +1,23 @@ +import { SceneObjectStateChangedEvent } from '@grafana/scenes'; +import { Dashboard } from '@grafana/schema'; +import { CorsWorker } from 'app/core/utils/CorsWorker'; import * as createDetectChangesWorker from 'app/features/dashboard-scene/saving/createDetectChangesWorker'; +import { DashboardScene } from '../scene/DashboardScene'; + import { DashboardSceneChangeTracker } from './DashboardSceneChangeTracker'; +jest.mock('../serialization/transformSceneToSaveModel', () => { + return { + transformSceneToSaveModel: () => { + return { + title: 'updated dashboard', + invalidProp: () => 'function', + }; + }, + }; +}); + describe('DashboardSceneChangeTracker', () => { it('should set _changesWorker to undefined when terminate is called', () => { const terminate = jest.fn(); @@ -20,4 +36,32 @@ describe('DashboardSceneChangeTracker', () => { changeTracker.terminate(); expect(changeTracker['_changesWorker']).toBeUndefined(); }); + + it('should remove non clonable properties before sending to worker', () => { + const scene = new DashboardScene({}); + const postMessage = jest.fn(); + + jest.spyOn(createDetectChangesWorker, 'createWorker').mockImplementation(() => { + return { + postMessage, + } as unknown as CorsWorker; + }); + jest.spyOn(DashboardSceneChangeTracker, 'isUpdatingPersistedState').mockImplementation(() => { + return true; + }); + jest.spyOn(scene, 'getInitialSaveModel').mockReturnValue({ + title: 'initial dashboard', + invalidProp: () => 'function', + } as unknown as Dashboard); + + const changeTracker = new DashboardSceneChangeTracker(scene); + changeTracker.startTrackingChanges(); + + scene.publishEvent({ type: SceneObjectStateChangedEvent.type, payload: { a: 1 } }); + + expect(postMessage).toHaveBeenCalledWith({ + initial: { title: 'initial dashboard' }, + changed: { title: 'updated dashboard' }, + }); + }); }); diff --git a/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.ts b/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.ts index bf30ddd9e99..d99c4132687 100644 --- a/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.ts +++ b/public/app/features/dashboard-scene/saving/DashboardSceneChangeTracker.ts @@ -19,7 +19,7 @@ import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsData import { DashboardControls } from '../scene/DashboardControls'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene, PERSISTED_PROPS } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks } from '../scene/PanelLinks'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { transformSceneToSaveModel } from '../serialization/transformSceneToSaveModel'; @@ -77,11 +77,6 @@ export class DashboardSceneChangeTracker { if (payload.changedObject instanceof VizPanelLinks) { return true; } - if (payload.changedObject instanceof LibraryVizPanel) { - if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'name')) { - return true; - } - } if (payload.changedObject instanceof SceneRefreshPicker) { if ( Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'intervals') || @@ -90,6 +85,11 @@ export class DashboardSceneChangeTracker { return true; } } + if (payload.changedObject instanceof LibraryPanelBehavior) { + if (Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'name')) { + return true; + } + } if (payload.changedObject instanceof behaviors.CursorSync) { return true; } @@ -139,10 +139,16 @@ export class DashboardSceneChangeTracker { } private detectSaveModelChanges() { - this._changesWorker?.postMessage({ - changed: transformSceneToSaveModel(this._dashboard), - initial: this._dashboard.getInitialSaveModel(), - }); + const changedDashboard = transformSceneToSaveModel(this._dashboard); + const initialDashboard = this._dashboard.getInitialSaveModel(); + + // Objects must be stringify to ensure they are clonable, so they don't contain functions + const changed = + typeof changedDashboard === 'object' ? JSON.parse(JSON.stringify(changedDashboard)) : changedDashboard; + const initial = + typeof initialDashboard === 'object' ? JSON.parse(JSON.stringify(initialDashboard)) : initialDashboard; + + this._changesWorker?.postMessage({ initial, changed }); } private hasMetadataChanges() { diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx index 98cd9aa1993..90116dec207 100644 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.test.tsx @@ -1,4 +1,4 @@ -import { SceneGridLayout, SceneGridRow, SceneTimeRange } from '@grafana/scenes'; +import { SceneGridLayout, SceneGridRow, SceneTimeRange, VizPanel } from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; import { activateFullSceneTree } from '../utils/test-utils'; @@ -6,7 +6,17 @@ import { activateFullSceneTree } from '../utils/test-utils'; import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene } from './DashboardScene'; -import { LibraryVizPanel } from './LibraryVizPanel'; +import { LibraryPanelBehavior } from './LibraryPanelBehavior'; + +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getDataSourceSrv: () => { + return { + get: jest.fn().mockResolvedValue({}), + getInstanceSettings: jest.fn().mockResolvedValue({ uid: 'ds1' }), + }; + }, +})); describe('AddLibraryPanelWidget', () => { let dashboard: DashboardScene; @@ -35,8 +45,55 @@ describe('AddLibraryPanelWidget', () => { const gridItem = layout.state.children[0] as DashboardGridItem; expect(layout.state.children.length).toBe(1); - expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); - expect((gridItem.state.body! as LibraryVizPanel).state.panelKey).toBe('panel-1'); + expect(gridItem.state.body!).toBeInstanceOf(VizPanel); + expect(gridItem.state.body.state.$behaviors![0]).toBeInstanceOf(LibraryPanelBehavior); + expect(gridItem.state.body.state.key).toBe('panel-1'); + }); + + it('should add library panel from menu and enter edit mode in a dashboard that is not already in edit mode', async () => { + const drawer = new AddLibraryPanelDrawer({}); + const dashboard = new DashboardScene({ + $timeRange: new SceneTimeRange({}), + title: 'hello', + uid: 'dash-1', + version: 4, + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [], + }), + overlay: drawer, + }); + + activateFullSceneTree(dashboard); + + await new Promise((r) => setTimeout(r, 1)); + + const panelInfo: LibraryPanel = { + uid: 'uid', + model: { + type: 'timeseries', + }, + name: 'name', + version: 1, + type: 'timeseries', + }; + + // if we are in a saved dashboard with no panels, adding a lib panel through + // the CTA should enter edit mode + expect(dashboard.state.isEditing).toBe(undefined); + + drawer.onAddLibraryPanel(panelInfo); + + const layout = dashboard.state.body as SceneGridLayout; + const gridItem = layout.state.children[0] as DashboardGridItem; + + expect(layout.state.children.length).toBe(1); + expect(gridItem.state.body!).toBeInstanceOf(VizPanel); + expect(gridItem.state.body.state.$behaviors![0]).toBeInstanceOf(LibraryPanelBehavior); + expect(gridItem.state.body.state.key).toBe('panel-1'); + expect(dashboard.state.isEditing).toBe(true); }); it('should throw error if adding lib panel in a layout that is not SceneGridLayout', () => { @@ -48,11 +105,11 @@ describe('AddLibraryPanelWidget', () => { }); it('should replace grid item when grid item state is passed', async () => { - const libPanel = new LibraryVizPanel({ + const libPanel = new VizPanel({ title: 'Panel Title', - uid: 'uid', - name: 'name', - panelKey: 'panel-1', + pluginId: 'table', + key: 'panel-1', + $behaviors: [new LibraryPanelBehavior({ title: 'LibraryPanel A title', name: 'LibraryPanel A', uid: 'uid' })], }); let gridItem = new DashboardGridItem({ @@ -88,20 +145,22 @@ describe('AddLibraryPanelWidget', () => { const layout = dashboard.state.body as SceneGridLayout; gridItem = layout.state.children[0] as DashboardGridItem; + const behavior = gridItem.state.body!.state.$behaviors![0] as LibraryPanelBehavior; expect(layout.state.children.length).toBe(1); - expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(gridItem.state.body!).toBeInstanceOf(VizPanel); + expect(behavior).toBeInstanceOf(LibraryPanelBehavior); expect(gridItem.state.key).toBe('grid-item-1'); - expect((gridItem.state.body! as LibraryVizPanel).state.uid).toBe('new_uid'); - expect((gridItem.state.body! as LibraryVizPanel).state.name).toBe('new_name'); + expect(behavior.state.uid).toBe('new_uid'); + expect(behavior.state.name).toBe('new_name'); }); it('should replace grid item in row when grid item state is passed', async () => { - const libPanel = new LibraryVizPanel({ + const libPanel = new VizPanel({ title: 'Panel Title', - uid: 'uid', - name: 'name', - panelKey: 'panel-1', + pluginId: 'table', + key: 'panel-1', + $behaviors: [new LibraryPanelBehavior({ title: 'LibraryPanel A title', name: 'LibraryPanel A', uid: 'uid' })], }); let gridItem = new DashboardGridItem({ @@ -142,13 +201,15 @@ describe('AddLibraryPanelWidget', () => { const layout = dashboard.state.body as SceneGridLayout; const gridRow = layout.state.children[0] as SceneGridRow; gridItem = gridRow.state.children[0] as DashboardGridItem; + const behavior = gridItem.state.body!.state.$behaviors![0] as LibraryPanelBehavior; expect(layout.state.children.length).toBe(1); expect(gridRow.state.children.length).toBe(1); - expect(gridItem.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(gridItem.state.body!).toBeInstanceOf(VizPanel); + expect(behavior).toBeInstanceOf(LibraryPanelBehavior); expect(gridItem.state.key).toBe('grid-item-1'); - expect((gridItem.state.body! as LibraryVizPanel).state.uid).toBe('new_uid'); - expect((gridItem.state.body! as LibraryVizPanel).state.name).toBe('new_name'); + expect(behavior.state.uid).toBe('new_uid'); + expect(behavior.state.name).toBe('new_name'); }); }); diff --git a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx index 30bc359ca6c..54f7679c87a 100644 --- a/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx +++ b/public/app/features/dashboard-scene/scene/AddLibraryPanelDrawer.tsx @@ -4,6 +4,7 @@ import { SceneObjectBase, SceneObjectRef, SceneObjectState, + VizPanel, } from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema'; import { Drawer } from '@grafana/ui'; @@ -14,13 +15,13 @@ import { } from 'app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; -import { NEW_PANEL_HEIGHT, NEW_PANEL_WIDTH, getDashboardSceneFor, getVizPanelKeyForPanelId } from '../utils/utils'; +import { NEW_PANEL_HEIGHT, NEW_PANEL_WIDTH, getDashboardSceneFor, getDefaultVizPanel } from '../utils/utils'; import { DashboardGridItem } from './DashboardGridItem'; -import { LibraryVizPanel } from './LibraryVizPanel'; +import { LibraryPanelBehavior } from './LibraryPanelBehavior'; export interface AddLibraryPanelDrawerState extends SceneObjectState { - panelToReplaceRef?: SceneObjectRef; + panelToReplaceRef?: SceneObjectRef; } export class AddLibraryPanelDrawer extends SceneObjectBase { @@ -38,11 +39,9 @@ export class AddLibraryPanelDrawer extends SceneObjectBase { }); describe('Library panels', () => { - it('should wait for library panel to be loaded', async () => { - const sourcePanel = new LibraryVizPanel({ - name: 'My Library Panel', + it('should re-run queries when library panel re-runs query', async () => { + const libPanelBehavior = new LibraryPanelBehavior({ + isLoaded: false, title: 'Panel title', uid: 'fdcvggvfy2qdca', - panelKey: 'lib-panel', - panel: new VizPanel({ - key: 'panel-1', - title: 'Panel A', - pluginId: 'table', + name: 'My Library Panel', + _loadedPanel: undefined, + }); + + const sourcePanel = new VizPanel({ + key: 'panel-1', + title: 'Panel A', + pluginId: 'table', + $behaviors: [libPanelBehavior], + $data: new SceneQueryRunner({ + datasource: { uid: 'grafana' }, + queries: [{ refId: 'A', queryType: 'randomWalk' }], }), }); @@ -544,29 +551,22 @@ describe('DashboardDatasourceBehaviour', () => { }), }); - activateFullSceneTree(scene); + const sceneDeactivate = activateFullSceneTree(scene); // spy on runQueries const spy = jest.spyOn(dashboardDSPanel.state.$data as SceneQueryRunner, 'runQueries'); + // deactivate scene to mimic going into panel edit + sceneDeactivate(); + + // run source panel queries and update request ID + (sourcePanel.state.$data as SceneQueryRunner).runQueries(); + + // // activate scene to mimic coming back from panel edit + activateFullSceneTree(scene); + await new Promise((r) => setTimeout(r, 1)); - expect(spy).not.toHaveBeenCalled(); - - // Simulate library panel being loaded - sourcePanel.setState({ - isLoaded: true, - panel: new VizPanel({ - title: 'Panel A', - pluginId: 'table', - key: 'panel-1', - $data: new SceneQueryRunner({ - datasource: { uid: 'grafana' }, - queries: [{ refId: 'A', queryType: 'randomWalk' }], - }), - }), - }); - expect(spy).toHaveBeenCalledTimes(1); }); }); diff --git a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx index d8746198e00..05880cecc19 100644 --- a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx @@ -1,18 +1,9 @@ -import { Unsubscribable } from 'rxjs'; - -import { - CancelActivationHandler, - SceneObjectBase, - SceneObjectState, - SceneQueryRunner, - VizPanel, -} from '@grafana/scenes'; +import { SceneObjectBase, SceneObjectState, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { findVizPanelByKey, getDashboardSceneFor, getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; -import { LibraryVizPanel, LibraryVizPanelState } from './LibraryVizPanel'; interface DashboardDatasourceBehaviourState extends SceneObjectState {} @@ -27,8 +18,6 @@ export class DashboardDatasourceBehaviour extends SceneObjectBase { - this.handleLibPanelStateUpdates(n, p, queryRunner); - }); - } - } else { - if (this.prevRequestId && this.prevRequestId !== sourcePanelQueryRunner.state.data?.request?.requestId) { - queryRunner.runQueries(); - } + throw new Error('Could not find SceneQueryRunner for panel'); + } + + if (this.prevRequestId && this.prevRequestId !== sourcePanelQueryRunner.state.data?.request?.requestId) { + queryRunner.runQueries(); } return () => { this.prevRequestId = sourcePanelQueryRunner?.state.data?.request?.requestId; - if (libraryPanelSub) { - libraryPanelSub.unsubscribe(); - } - if (parentLibPanelCleanUp) { - parentLibPanelCleanUp(); - } }; } - - private handleLibPanelStateUpdates(n: LibraryVizPanelState, p: LibraryVizPanelState, queryRunner: SceneQueryRunner) { - if (n.panel && n.panel !== p.panel) { - const libPanelQueryRunner = getQueryRunnerFor(n.panel); - - if (!(libPanelQueryRunner instanceof SceneQueryRunner)) { - throw new Error('Could not find SceneQueryRunner for panel'); - } - - queryRunner.runQueries(); - } - } } diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx index cfdbaea407c..97e44cef66b 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import { isEqual } from 'lodash'; import { useMemo } from 'react'; -import { Unsubscribable } from 'rxjs'; import { config } from '@grafana/runtime'; import { @@ -26,13 +25,11 @@ import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; import { getMultiVariableValues, getQueryRunnerFor } from '../utils/utils'; -import { AddLibraryPanelDrawer } from './AddLibraryPanelDrawer'; -import { LibraryVizPanel } from './LibraryVizPanel'; import { repeatPanelMenuBehavior } from './PanelMenuBehavior'; import { DashboardRepeatsProcessedEvent } from './types'; export interface DashboardGridItemState extends SceneGridItemStateLike { - body: VizPanel | LibraryVizPanel | AddLibraryPanelDrawer; + body: VizPanel; repeatedPanels?: VizPanel[]; variableName?: string; itemHeight?: number; @@ -43,9 +40,8 @@ export interface DashboardGridItemState extends SceneGridItemStateLike { export type RepeatDirection = 'v' | 'h'; export class DashboardGridItem extends SceneObjectBase implements SceneGridItemLike { - private _libPanelSubscription: Unsubscribable | undefined; private _prevRepeatValues?: VariableValueSingle[]; - private _oldBody?: VizPanel | LibraryVizPanel | AddLibraryPanelDrawer; + private _oldBody?: VizPanel; protected _variableDependency = new DashboardGridItemVariableDependencyHandler(this); @@ -62,48 +58,8 @@ export class DashboardGridItem extends SceneObjectBase i this._prevRepeatValues = undefined; } - this._oldBody = this.state.body; this.performRepeat(); } - - // Subscriptions that handles body updates, i.e. VizPanel -> LibraryVizPanel, AddLibPanelWidget -> LibraryVizPanel - this._subs.add( - this.subscribeToState((newState, prevState) => { - if (newState.body !== prevState.body) { - if (newState.body instanceof LibraryVizPanel) { - this.setupLibraryPanelChangeSubscription(newState.body); - } - } - }) - ); - - // Initial setup of the lbrary panel subscription. Lib panels are lazy laded, so only then we can subscribe to the repeat config changes - if (this.state.body instanceof LibraryVizPanel) { - this.setupLibraryPanelChangeSubscription(this.state.body); - } - - return () => { - this._libPanelSubscription?.unsubscribe(); - this._libPanelSubscription = undefined; - }; - } - - private setupLibraryPanelChangeSubscription(panel: LibraryVizPanel) { - if (this._libPanelSubscription) { - this._libPanelSubscription.unsubscribe(); - this._libPanelSubscription = undefined; - } - - this._libPanelSubscription = panel.subscribeToState((newState) => { - if (newState._loadedPanel?.model.repeat) { - this.setState({ - variableName: newState._loadedPanel.model.repeat, - repeatDirection: newState._loadedPanel.model.repeatDirection, - maxPerRow: newState._loadedPanel.model.maxPerRow, - }); - this.performRepeat(); - } - }); } /** @@ -132,10 +88,6 @@ export class DashboardGridItem extends SceneObjectBase i } public performRepeat() { - if (this.state.body instanceof AddLibraryPanelDrawer) { - return; - } - if (!this.state.variableName || sceneGraph.hasVariableDependencyInLoadingState(this)) { return; } @@ -164,8 +116,10 @@ export class DashboardGridItem extends SceneObjectBase i return; } + this._oldBody = this.state.body; this._prevRepeatValues = values; - const panelToRepeat = this.state.body instanceof LibraryVizPanel ? this.state.body.state.panel! : this.state.body; + + const panelToRepeat = this.state.body; const repeatedPanels: VizPanel[] = []; // when variable has no options (due to error or similar) it will not render any panels at all @@ -262,14 +216,6 @@ export class DashboardGridItem extends SceneObjectBase i if (body instanceof VizPanel) { return ; } - - if (body instanceof LibraryVizPanel) { - return ; - } - - if (body instanceof AddLibraryPanelDrawer) { - return ; - } } if (!repeatedPanels) { diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx index 10fb8788a03..c2a2ba61966 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.test.tsx @@ -20,11 +20,7 @@ import { VariablesChanged } from 'app/features/variables/types'; import { PanelEditor, buildPanelEditScene } from '../panel-edit/PanelEditor'; import { createWorker } from '../saving/createDetectChangesWorker'; -import { - buildGridItemForLibPanel, - buildGridItemForPanel, - transformSaveModelToScene, -} from '../serialization/transformSaveModelToScene'; +import { buildGridItemForPanel, transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; import { DecoratedRevisionModel } from '../settings/VersionsEditView'; import { historySrv } from '../settings/version-history/HistorySrv'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; @@ -34,7 +30,7 @@ import { findVizPanelByKey } from '../utils/utils'; import { DashboardControls } from './DashboardControls'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene, DashboardSceneState } from './DashboardScene'; -import { LibraryVizPanel } from './LibraryVizPanel'; +import { LibraryPanelBehavior } from './LibraryPanelBehavior'; import { PanelTimeRange } from './PanelTimeRange'; import { RowActions } from './row-actions/RowActions'; @@ -348,20 +344,21 @@ describe('DashboardScene', () => { expect(restoredDashboardGridItem.state.variableName).toBe(prevValue); }); - it('A change to any LibraryVizPanel name should set isDirty true', () => { - const libraryVizPanel = sceneGraph.findObject(scene, (p) => p instanceof LibraryVizPanel) as LibraryVizPanel; - const prevValue = libraryVizPanel.state.name; + it('A change to any library panel name should set isDirty true', () => { + const libraryVizPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem).state + .body; + const behavior = libraryVizPanel.state.$behaviors![0] as LibraryPanelBehavior; + const prevValue = behavior.state.name; - libraryVizPanel.setState({ name: 'new name' }); + behavior.setState({ name: 'new name' }); expect(scene.state.isDirty).toBe(true); scene.exitEditMode({ skipConfirm: true }); - const restoredLibraryVizPanel = sceneGraph.findObject( - scene, - (p) => p instanceof LibraryVizPanel - ) as LibraryVizPanel; - expect(restoredLibraryVizPanel.state.name).toBe(prevValue); + const restoredLibraryVizPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem) + .state.body; + const restoredBehavior = restoredLibraryVizPanel.state.$behaviors![0] as LibraryPanelBehavior; + expect(restoredBehavior.state.name).toBe(prevValue); }); it('A change to any PanelTimeRange state should set isDirty true', () => { @@ -625,19 +622,14 @@ describe('DashboardScene', () => { }); it('Should fail to copy a library panel if it does not have a grid item parent', () => { - const libVizPanel = new LibraryVizPanel({ - uid: 'uid', - name: 'libraryPanel', - panelKey: 'panel-4', + const libVizPanel = new VizPanel({ title: 'Library Panel', - panel: new VizPanel({ - title: 'Library Panel', - key: 'panel-4', - pluginId: 'table', - }), + pluginId: 'table', + key: 'panel-4', + $behaviors: [new LibraryPanelBehavior({ title: 'Library Panel', name: 'libraryPanel', uid: 'uid' })], }); - scene.copyPanel(libVizPanel.state.panel as VizPanel); + scene.copyPanel(libVizPanel); expect(store.exists(LS_PANEL_COPY_KEY)).toBe(false); }); @@ -650,10 +642,11 @@ describe('DashboardScene', () => { }); it('Should copy a library viz panel', () => { - const libVizPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem).state - .body as LibraryVizPanel; + const libVizPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem).state.body; - scene.copyPanel(libVizPanel.state.panel as VizPanel); + expect(libVizPanel.state.$behaviors![0]).toBeInstanceOf(LibraryPanelBehavior); + + scene.copyPanel(libVizPanel); expect(store.exists(LS_PANEL_COPY_KEY)).toBe(true); }); @@ -687,13 +680,13 @@ describe('DashboardScene', () => { it('Should paste a library viz panel', () => { store.set(LS_PANEL_COPY_KEY, JSON.stringify({ key: 'panel-7' })); jest.spyOn(JSON, 'parse').mockReturnValue({ libraryPanel: { uid: 'uid', name: 'libraryPanel' } }); - jest.mocked(buildGridItemForLibPanel).mockReturnValue( + jest.mocked(buildGridItemForPanel).mockReturnValue( new DashboardGridItem({ - body: new LibraryVizPanel({ + body: new VizPanel({ title: 'Library Panel', - uid: 'uid', - name: 'libraryPanel', - panelKey: 'panel-4', + pluginId: 'table', + key: 'panel-4', + $behaviors: [new LibraryPanelBehavior({ title: 'Library Panel', name: 'libraryPanel', uid: 'uid' })], }), }) ); @@ -703,12 +696,11 @@ describe('DashboardScene', () => { const body = scene.state.body as SceneGridLayout; const gridItem = body.state.children[0] as DashboardGridItem; - const libVizPanel = gridItem.state.body as LibraryVizPanel; + const libVizPanel = gridItem.state.body; - expect(buildGridItemForLibPanel).toHaveBeenCalledTimes(1); + expect(buildGridItemForPanel).toHaveBeenCalledTimes(1); expect(body.state.children.length).toBe(6); - expect(libVizPanel.state.panelKey).toBe('panel-7'); - expect(libVizPanel.state.panel?.state.key).toBe('panel-7'); + expect(libVizPanel.state.key).toBe('panel-7'); expect(gridItem.state.y).toBe(0); expect(store.exists(LS_PANEL_COPY_KEY)).toBe(false); }); @@ -736,8 +728,7 @@ describe('DashboardScene', () => { it('Should remove a library panel', () => { const libraryPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem).state.body; - const vizPanel = (libraryPanel as LibraryVizPanel).state.panel; - scene.removePanel(vizPanel as VizPanel); + scene.removePanel(libraryPanel); const body = scene.state.body as SceneGridLayout; expect(body.state.children.length).toBe(4); @@ -748,9 +739,8 @@ describe('DashboardScene', () => { ((scene.state.body as SceneGridLayout).state.children[2] as SceneGridRow).state .children[1] as DashboardGridItem ).state.body; - const vizPanel = (libraryPanel as LibraryVizPanel).state.panel; - scene.removePanel(vizPanel as VizPanel); + scene.removePanel(libraryPanel); const body = scene.state.body as SceneGridLayout; const gridRow = body.state.children[2] as SceneGridRow; @@ -784,17 +774,31 @@ describe('DashboardScene', () => { it('Should duplicate a library panel', () => { const libraryPanel = ((scene.state.body as SceneGridLayout).state.children[4] as DashboardGridItem).state.body; - const vizPanel = (libraryPanel as LibraryVizPanel).state.panel; - scene.duplicatePanel(vizPanel as VizPanel); + scene.duplicatePanel(libraryPanel); const body = scene.state.body as SceneGridLayout; const gridItem = body.state.children[5] as DashboardGridItem; - const libVizPanel = gridItem.state.body as LibraryVizPanel; + const libVizPanel = gridItem.state.body; expect(body.state.children.length).toBe(6); - expect(libVizPanel.state.panelKey).toBe('panel-7'); - expect(libVizPanel.state.panel?.state.key).toBe('panel-7'); + expect(libVizPanel.state.key).toBe('panel-7'); + }); + + it('Should deep clone data provider when duplicating a panel', () => { + const vizPanel = ((scene.state.body as SceneGridLayout).state.children[0] as DashboardGridItem).state.body; + scene.duplicatePanel(vizPanel as VizPanel); + + const panelQueries = ( + ((scene.state.body as SceneGridLayout).state.children[0] as DashboardGridItem).state.body.state.$data?.state + .$data as SceneQueryRunner + ).state.queries; + const duplicatedPanelQueries = ( + ((scene.state.body as SceneGridLayout).state.children[5] as DashboardGridItem).state.body.state.$data?.state + .$data as SceneQueryRunner + ).state.queries; + + expect(panelQueries[0]).not.toBe(duplicatedPanelQueries[0]); }); it('Should duplicate a repeated panel', () => { @@ -855,19 +859,17 @@ describe('DashboardScene', () => { ((scene.state.body as SceneGridLayout).state.children[2] as SceneGridRow).state .children[1] as DashboardGridItem ).state.body; - const vizPanel = (libraryPanel as LibraryVizPanel).state.panel; - scene.duplicatePanel(vizPanel as VizPanel); + scene.duplicatePanel(libraryPanel); const body = scene.state.body as SceneGridLayout; const gridRow = body.state.children[2] as SceneGridRow; const gridItem = gridRow.state.children[2] as DashboardGridItem; - const libVizPanel = gridItem.state.body as LibraryVizPanel; + const libVizPanel = gridItem.state.body; expect(gridRow.state.children.length).toBe(3); - expect(libVizPanel.state.panelKey).toBe('panel-7'); - expect(libVizPanel.state.panel?.state.key).toBe('panel-7'); + expect(libVizPanel.state.key).toBe('panel-7'); }); it('Should fail to duplicate a panel if it does not have a grid item parent', () => { @@ -886,16 +888,10 @@ describe('DashboardScene', () => { }); it('Should unlink a library panel', () => { - const libPanel = new LibraryVizPanel({ - title: 'title', - uid: 'abc', - name: 'lib panel', - panelKey: 'panel-1', - isLoaded: true, - panel: new VizPanel({ - title: 'Panel B', - pluginId: 'table', - }), + const libPanel = new VizPanel({ + title: 'Panel B', + pluginId: 'table', + $behaviors: [new LibraryPanelBehavior({ title: 'title', name: 'lib panel', uid: 'abc', isLoaded: true })], }); const scene = buildTestScene({ @@ -916,6 +912,7 @@ describe('DashboardScene', () => { expect(body.state.children.length).toBe(1); expect(gridItem.state.body).toBeInstanceOf(VizPanel); + expect(gridItem.state.$behaviors).toBeUndefined(); }); it('Should create a library panel', () => { @@ -945,11 +942,12 @@ describe('DashboardScene', () => { const layout = scene.state.body as SceneGridLayout; const newGridItem = layout.state.children[0] as DashboardGridItem; + const behavior = newGridItem.state.body.state.$behaviors![0] as LibraryPanelBehavior; expect(layout.state.children.length).toBe(1); - expect(newGridItem.state.body).toBeInstanceOf(LibraryVizPanel); - expect((newGridItem.state.body as LibraryVizPanel).state.uid).toBe('uid'); - expect((newGridItem.state.body as LibraryVizPanel).state.name).toBe('name'); + expect(newGridItem.state.body).toBeInstanceOf(VizPanel); + expect(behavior.state.uid).toBe('uid'); + expect(behavior.state.name).toBe('name'); }); it('Should create a library panel under a row', () => { @@ -984,12 +982,13 @@ describe('DashboardScene', () => { const layout = scene.state.body as SceneGridLayout; const newGridItem = (layout.state.children[0] as SceneGridRow).state.children[0] as DashboardGridItem; + const behavior = newGridItem.state.body.state.$behaviors![0] as LibraryPanelBehavior; expect(layout.state.children.length).toBe(1); expect((layout.state.children[0] as SceneGridRow).state.children.length).toBe(1); - expect(newGridItem.state.body).toBeInstanceOf(LibraryVizPanel); - expect((newGridItem.state.body as LibraryVizPanel).state.uid).toBe('uid'); - expect((newGridItem.state.body as LibraryVizPanel).state.name).toBe('name'); + expect(newGridItem.state.body).toBeInstanceOf(VizPanel); + expect(behavior.state.uid).toBe('uid'); + expect(behavior.state.name).toBe('name'); }); }); }); @@ -1242,7 +1241,10 @@ function buildTestScene(overrides?: Partial) { }), $data: new SceneDataTransformer({ transformations: [], - $data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }), + $data: new SceneQueryRunner({ + key: 'data-query-runner', + queries: [{ refId: 'A', target: 'aliasByMetric(carbon.**)' }], + }), }), }), }), @@ -1266,16 +1268,11 @@ function buildTestScene(overrides?: Partial) { }), }), new DashboardGridItem({ - body: new LibraryVizPanel({ - uid: 'uid', - name: 'libraryPanel', - panelKey: 'panel-5', + body: new VizPanel({ title: 'Library Panel', - panel: new VizPanel({ - title: 'Library Panel', - key: 'panel-5', - pluginId: 'table', - }), + pluginId: 'table', + key: 'panel-5', + $behaviors: [new LibraryPanelBehavior({ title: 'Library Panel', name: 'libraryPanel', uid: 'uid' })], }), }), ], @@ -1289,16 +1286,11 @@ function buildTestScene(overrides?: Partial) { }), }), new DashboardGridItem({ - body: new LibraryVizPanel({ - uid: 'uid', - name: 'libraryPanel', - panelKey: 'panel-6', + body: new VizPanel({ title: 'Library Panel', - panel: new VizPanel({ - title: 'Library Panel', - key: 'panel-6', - pluginId: 'table', - }), + pluginId: 'table', + key: 'panel-6', + $behaviors: [new LibraryPanelBehavior({ title: 'Library Panel', name: 'libraryPanel', uid: 'uid' })], }), }), ], diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 3378af54647..e1874dfb15d 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -42,17 +42,13 @@ import { ShowConfirmModalEvent } from 'app/types/events'; import { PanelEditor } from '../panel-edit/PanelEditor'; import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker'; import { SaveDashboardDrawer } from '../saving/SaveDashboardDrawer'; -import { - buildGridItemForLibPanel, - buildGridItemForPanel, - transformSaveModelToScene, -} from '../serialization/transformSaveModelToScene'; +import { buildGridItemForPanel, transformSaveModelToScene } from '../serialization/transformSaveModelToScene'; import { gridItemToPanel } from '../serialization/transformSceneToSaveModel'; import { DecoratedRevisionModel } from '../settings/VersionsEditView'; import { DashboardEditView } from '../settings/utils'; import { historySrv } from '../settings/version-history'; import { DashboardModelCompatibilityWrapper } from '../utils/DashboardModelCompatibilityWrapper'; -import { dashboardSceneGraph, getLibraryVizPanelFromVizPanel } from '../utils/dashboardSceneGraph'; +import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; import { djb2Hash } from '../utils/djb2Hash'; import { getDashboardUrl, getViewPanelUrl } from '../utils/urlBuilders'; import { @@ -64,6 +60,7 @@ import { getDefaultRow, getDefaultVizPanel, getPanelIdForVizPanel, + getQueryRunnerFor, getVizPanelKeyForPanelId, isPanelClone, } from '../utils/utils'; @@ -73,7 +70,7 @@ import { DashboardControls } from './DashboardControls'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardSceneRenderer } from './DashboardSceneRenderer'; import { DashboardSceneUrlSync } from './DashboardSceneUrlSync'; -import { LibraryVizPanel } from './LibraryVizPanel'; +import { LibraryPanelBehavior } from './LibraryPanelBehavior'; import { RowRepeaterBehavior } from './RowRepeaterBehavior'; import { ViewPanelScene } from './ViewPanelScene'; import { setupKeyboardShortcuts } from './keyboardShortcuts'; @@ -541,13 +538,8 @@ export class DashboardScene extends SceneObjectBase { throw new Error('Trying to add a panel in a layout that is not SceneGridLayout'); } - const panelKey = panelToReplace.state.key; - - const body = new LibraryVizPanel({ - title: libPanel.model?.title ?? 'Panel', - uid: libPanel.uid, - name: libPanel.name, - panelKey: panelKey ?? getVizPanelKeyForPanelId(dashboardSceneGraph.getNextPanelId(this)), + const body = panelToReplace.clone({ + $behaviors: [new LibraryPanelBehavior({ uid: libPanel.uid, name: libPanel.name })], }); const gridItem = panelToReplace.parent; @@ -564,9 +556,7 @@ export class DashboardScene extends SceneObjectBase { return; } - const libraryPanel = getLibraryVizPanelFromVizPanel(vizPanel); - - const gridItem = libraryPanel ? libraryPanel.parent : vizPanel.parent; + const gridItem = vizPanel.parent; if (!(gridItem instanceof DashboardGridItem)) { console.error('Trying to duplicate a panel in a layout that is not DashboardGridItem'); @@ -578,42 +568,36 @@ export class DashboardScene extends SceneObjectBase { let newGridItem; const newPanelId = dashboardSceneGraph.getNextPanelId(this); - if (libraryPanel) { - const gridItemToDuplicateState = sceneUtils.cloneSceneObjectState(gridItem.state); + if (gridItem instanceof DashboardGridItem) { + panelState = sceneUtils.cloneSceneObjectState(gridItem.state.body.state); - newGridItem = new DashboardGridItem({ - x: gridItemToDuplicateState.x, - y: gridItemToDuplicateState.y, - width: gridItemToDuplicateState.width, - height: gridItemToDuplicateState.height, - body: new LibraryVizPanel({ - title: libraryPanel.state.title, - uid: libraryPanel.state.uid, - name: libraryPanel.state.name, - panelKey: getVizPanelKeyForPanelId(newPanelId), - }), - }); + let queryRunner = getQueryRunnerFor(gridItem.state.body); + const queries = queryRunner?.state.queries.map((q) => ({ ...q })); + queryRunner = queryRunner?.clone({ queries }); + panelData = sceneGraph.getData(gridItem.state.body).clone({ $data: queryRunner }); } else { - if (gridItem instanceof DashboardGridItem) { - panelState = sceneUtils.cloneSceneObjectState(gridItem.state.body.state); - panelData = sceneGraph.getData(gridItem.state.body).clone(); - } else { - panelState = sceneUtils.cloneSceneObjectState(vizPanel.state); - panelData = sceneGraph.getData(vizPanel).clone(); - } + panelState = sceneUtils.cloneSceneObjectState(vizPanel.state); - // when we duplicate a panel we don't want to clone the alert state - delete panelData.state.data?.alertState; - - newGridItem = new DashboardGridItem({ - x: gridItem.state.x, - y: gridItem.state.y, - height: gridItem.state.height, - width: gridItem.state.width, - body: new VizPanel({ ...panelState, $data: panelData, key: getVizPanelKeyForPanelId(newPanelId) }), - }); + let queryRunner = getQueryRunnerFor(vizPanel); + const queries = queryRunner?.state.queries.map((q) => ({ ...q })); + queryRunner = queryRunner?.clone({ queries }); + panelData = sceneGraph.getData(vizPanel).clone({ $data: queryRunner }); } + // when we duplicate a panel we don't want to clone the alert state + delete panelData.state.data?.alertState; + + newGridItem = new DashboardGridItem({ + x: gridItem.state.x, + y: gridItem.state.y, + height: gridItem.state.height, + width: gridItem.state.width, + variableName: gridItem.state.variableName, + repeatDirection: gridItem.state.repeatDirection, + maxPerRow: gridItem.state.maxPerRow, + body: new VizPanel({ ...panelState, $data: panelData, key: getVizPanelKeyForPanelId(newPanelId) }), + }); + if (!(this.state.body instanceof SceneGridLayout)) { console.error('Trying to duplicate a panel in a layout that is not SceneGridLayout '); return; @@ -645,16 +629,6 @@ export class DashboardScene extends SceneObjectBase { let gridItem = vizPanel.parent; - if (vizPanel.parent instanceof LibraryVizPanel) { - const libraryVizPanel = vizPanel.parent; - - if (!libraryVizPanel.parent) { - return; - } - - gridItem = libraryVizPanel.parent; - } - if (!(gridItem instanceof DashboardGridItem)) { console.error('Trying to copy a panel that is not DashboardGridItem child'); throw new Error('Trying to copy a panel that is not DashboardGridItem child'); @@ -674,9 +648,7 @@ export class DashboardScene extends SceneObjectBase { const jsonData = store.get(LS_PANEL_COPY_KEY); const jsonObj = JSON.parse(jsonData); const panelModel = new PanelModel(jsonObj); - const gridItem = !panelModel.libraryPanel - ? buildGridItemForPanel(panelModel) - : buildGridItemForLibPanel(panelModel); + const gridItem = buildGridItemForPanel(panelModel); const sceneGridLayout = this.state.body; @@ -686,25 +658,9 @@ export class DashboardScene extends SceneObjectBase { const panelId = dashboardSceneGraph.getNextPanelId(this); - if (gridItem instanceof DashboardGridItem && gridItem.state.body instanceof LibraryVizPanel) { - const panelKey = getVizPanelKeyForPanelId(panelId); - - gridItem.state.body.setState({ panelKey }); - - const vizPanel = gridItem.state.body.state.panel; - - if (vizPanel instanceof VizPanel) { - vizPanel.setState({ key: panelKey }); - } - } else if (gridItem instanceof DashboardGridItem && gridItem.state.body) { - gridItem.state.body.setState({ - key: getVizPanelKeyForPanelId(panelId), - }); - } else if (gridItem instanceof DashboardGridItem) { - gridItem.state.body.setState({ - key: getVizPanelKeyForPanelId(panelId), - }); - } + gridItem.state.body.setState({ + key: getVizPanelKeyForPanelId(panelId), + }); gridItem.setState({ height: NEW_PANEL_HEIGHT, @@ -723,7 +679,7 @@ export class DashboardScene extends SceneObjectBase { public removePanel(panel: VizPanel) { const panels: SceneObject[] = []; - const key = panel.parent instanceof LibraryVizPanel ? panel.parent.parent?.state.key : panel.parent?.state.key; + const key = panel.parent?.state.key; if (!key) { return; @@ -764,7 +720,7 @@ export class DashboardScene extends SceneObjectBase { } } - public unlinkLibraryPanel(panel: LibraryVizPanel) { + public unlinkLibraryPanel(panel: VizPanel) { if (!panel.parent) { return; } @@ -772,13 +728,11 @@ export class DashboardScene extends SceneObjectBase { const gridItem = panel.parent; if (!(gridItem instanceof DashboardGridItem)) { - console.error('Trying to unlinka a lib panel in a layout that is not DashboardGridItem'); + console.error('Trying to unlink a lib panel in a layout that is not DashboardGridItem'); return; } - gridItem?.setState({ - body: panel.state.panel?.clone(), - }); + gridItem.state.body.setState({ $behaviors: undefined }); } public showModal(modal: SceneObject) { @@ -812,7 +766,7 @@ export class DashboardScene extends SceneObjectBase { locationService.partial({ editview: 'settings' }); }; - public onShowAddLibraryPanelDrawer(panelToReplaceRef?: SceneObjectRef) { + public onShowAddLibraryPanelDrawer(panelToReplaceRef?: SceneObjectRef) { this.setState({ overlay: new AddLibraryPanelDrawer({ panelToReplaceRef }), }); diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts index 276c82e9d5c..c1a7172044f 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.test.ts @@ -1,11 +1,10 @@ import { AppEvents } from '@grafana/data'; -import { SceneGridLayout, SceneGridRow, SceneQueryRunner, VizPanel } from '@grafana/scenes'; +import { SceneGridLayout, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import appEvents from 'app/core/app_events'; import { KioskMode } from 'app/types'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene } from './DashboardScene'; -import { RowRepeaterBehavior } from './RowRepeaterBehavior'; import { DashboardRepeatsProcessedEvent } from './types'; describe('DashboardSceneUrlSync', () => { @@ -109,35 +108,6 @@ describe('DashboardSceneUrlSync', () => { scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene })); expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1-clone-1'); }); - - it('should subscribe and update view panel if panel is in a repeated row', () => { - const scene = buildTestScene(); - - // fake adding row panel - const layout = scene.state.body as SceneGridLayout; - layout.setState({ - children: [ - new SceneGridRow({ - $behaviors: [new RowRepeaterBehavior({ variableName: 'test' })], - children: [ - new VizPanel({ - title: 'Panel A', - key: 'panel-1', - pluginId: 'table', - }), - ], - }), - ], - }); - - scene.urlSync?.updateFromUrl({ viewPanel: 'panel-1' }); - - expect(scene.state.viewPanelScene?.getUrlKey()).toBeUndefined(); - - // Verify it subscribes to DashboardRepeatsProcessedEvent - scene.publishEvent(new DashboardRepeatsProcessedEvent({ source: scene })); - expect(scene.state.viewPanelScene?.getUrlKey()).toBe('panel-1'); - }); }); function buildTestScene() { diff --git a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts index a169d05a60f..53067b92a34 100644 --- a/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts +++ b/public/app/features/dashboard-scene/scene/DashboardSceneUrlSync.ts @@ -18,16 +18,10 @@ import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { createDashboardEditViewFor } from '../settings/utils'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareModal } from '../sharing/ShareModal'; -import { - findVizPanelByKey, - getDashboardSceneFor, - getLibraryPanel, - isPanelClone, - isWithinUnactivatedRepeatRow, -} from '../utils/utils'; +import { findVizPanelByKey, getDashboardSceneFor, getLibraryPanelBehavior, isPanelClone } from '../utils/utils'; import { DashboardScene, DashboardSceneState } from './DashboardScene'; -import { LibraryVizPanel } from './LibraryVizPanel'; +import { LibraryPanelBehavior } from './LibraryPanelBehavior'; import { ViewPanelScene } from './ViewPanelScene'; import { DashboardRepeatsProcessedEvent } from './types'; @@ -83,20 +77,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { return; } - if (getLibraryPanel(panel)) { - this._handleLibraryPanel(panel, (p) => { - if (p.state.key === undefined) { - // Inspect drawer require a panel key to be set - throw new Error('library panel key is undefined'); - } - const drawer = new PanelInspectDrawer({ - $behaviors: [new ResolveInspectPanelByKey({ panelKey: p.state.key })], - }); - this._scene.setState({ overlay: drawer, inspectPanelKey: p.state.key }); - }); - return; - } - update.inspectPanelKey = values.inspect; update.overlay = new PanelInspectDrawer({ $behaviors: [new ResolveInspectPanelByKey({ panelKey: values.inspect })], @@ -122,16 +102,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { return; } - if (getLibraryPanel(panel)) { - this._handleLibraryPanel(panel, (p) => this._buildLibraryPanelViewScene(p)); - return; - } - - if (isWithinUnactivatedRepeatRow(panel)) { - this._handleViewRepeatClone(values.viewPanel); - return; - } - update.viewPanelScene = new ViewPanelScene({ panelRef: panel.getRef() }); } else if (viewPanelScene && values.viewPanel === null) { update.viewPanelScene = undefined; @@ -155,10 +125,10 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { if (!isEditing) { this._scene.onEnterEditMode(); } - if (getLibraryPanel(panel)) { - this._handleLibraryPanel(panel, (p) => { - this._scene.setState({ editPanel: buildPanelEditScene(p) }); - }); + + const libPanelBehavior = getLibraryPanelBehavior(panel); + if (libPanelBehavior && !libPanelBehavior?.state.isLoaded) { + this._waitForLibPanelToLoadBeforeEnteringPanelEdit(panel, libPanelBehavior); return; } @@ -202,25 +172,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { } } - private _buildLibraryPanelViewScene(vizPanel: VizPanel) { - this._scene.setState({ viewPanelScene: new ViewPanelScene({ panelRef: vizPanel.getRef() }) }); - } - - private _handleLibraryPanel(vizPanel: VizPanel, cb: (p: VizPanel) => void): void { - if (!(vizPanel.parent instanceof LibraryVizPanel)) { - throw new Error('Panel is not a child of a LibraryVizPanel'); - } - const libraryPanel = vizPanel.parent; - if (libraryPanel.state.isLoaded) { - cb(vizPanel); - } else { - libraryPanel.subscribeToState((n) => { - cb(n.panel!); - }); - libraryPanel.activate(); - } - } - private _handleViewRepeatClone(viewPanel: string) { if (!this._eventSub) { this._eventSub = this._scene.subscribeToEvent(DashboardRepeatsProcessedEvent, () => { @@ -232,6 +183,18 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler { }); } } + + /** + * Temporary solution, with some refactoring of PanelEditor we can remove this + */ + private _waitForLibPanelToLoadBeforeEnteringPanelEdit(panel: VizPanel, libPanel: LibraryPanelBehavior) { + const sub = libPanel.subscribeToState((state) => { + if (state.isLoaded) { + this._scene.setState({ editPanel: buildPanelEditScene(panel) }); + sub.unsubscribe(); + } + }); + } } interface ResolveInspectPanelByKeyState extends SceneObjectState { @@ -262,10 +225,6 @@ class ResolveInspectPanelByKey extends SceneObjectBase Promise.resolve(getPanelPlugin({})), + getPanelPluginFromCache: (id: string) => undefined, +}); + +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + setPluginExtensionGetter: jest.fn(), + getPluginLinkExtensions: jest.fn(() => ({ + extensions: [], + })), + getDataSourceSrv: () => { + return { + get: jest.fn().mockResolvedValue({ + getRef: () => ({ uid: 'ds1' }), + }), + getInstanceSettings: jest.fn().mockResolvedValue({ uid: 'ds1' }), + }; + }, +})); + +const runRequestMock = jest.fn().mockReturnValue( + of({ + state: LoadingState.Done, + timeRange: getDefaultTimeRange(), + series: [ + toDataFrame({ + fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3] }], + }), + ], + request: { + app: 'dashboard', + requestId: 'request-id', + dashboardUID: 'asd', + interval: '1s', + panelId: 1, + range: getDefaultTimeRange(), + targets: [], + timezone: 'utc', + intervalMs: 1000, + startTime: 1, + scopedVars: { + __sceneObject: { value: new SceneCanvasText({ text: 'asd' }) }, + }, + }, + }) +); + +setRunRequest(runRequestMock); + +describe('LibraryPanelBehavior', () => { + it('should load library panel', async () => { + const { gridItem, spy, behavior } = await buildTestSceneWithLibraryPanel(); + + expect(behavior.state.isLoaded).toBe(true); + expect(behavior.state._loadedPanel).toBeDefined(); + expect(behavior.state._loadedPanel?.model).toBeDefined(); + expect(behavior.state._loadedPanel?.name).toBe('LibraryPanel A'); + expect(behavior.state._loadedPanel?.type).toBe('table'); + + // Verify the viz panel state have been updated with lib panel options + expect(gridItem.state.body.state.options).toEqual({ showHeader: true }); + + expect(spy).toHaveBeenCalled(); + }); + + it('should not update panel if version is the same', async () => { + const { gridItem } = await buildTestSceneWithLibraryPanel(); + + const behavior = gridItem.state.body.state.$behaviors![0] as LibraryPanelBehavior; + expect(behavior).toBeDefined(); + + const panel = vizPanelToPanel(gridItem.state.body.clone({ $behaviors: undefined })); + + const libraryPanelState = { + name: 'LibraryPanel B', + title: 'LibraryPanel B title', + uid: '222', + type: 'table', + version: 1, + model: panel, + }; + + behavior.setPanelFromLibPanel(libraryPanelState); + + expect(behavior.state._loadedPanel?.name).toBe('LibraryPanel A'); + expect(behavior.state._loadedPanel?.uid).toBe('111'); + }); + + it('should not update panel if behavior not part of a vizPanel', async () => { + const { gridItem } = await buildTestSceneWithLibraryPanel(); + + const behavior = gridItem.state.body.state.$behaviors![0] as LibraryPanelBehavior; + expect(behavior).toBeDefined(); + + const panel = vizPanelToPanel(gridItem.state.body.clone({ $behaviors: undefined })); + + const libraryPanelState = { + name: 'LibraryPanel B', + title: 'LibraryPanel B title', + uid: '222', + type: 'table', + version: 2, + model: panel, + }; + + const behaviorClone = behavior.clone(); + behaviorClone.setPanelFromLibPanel(libraryPanelState); + + expect(behaviorClone.state._loadedPanel?.name).toBe('LibraryPanel A'); + expect(behaviorClone.state._loadedPanel?.uid).toBe('111'); + }); +}); + +async function buildTestSceneWithLibraryPanel() { + const behavior = new LibraryPanelBehavior({ title: 'LibraryPanel A title', name: 'LibraryPanel A', uid: '111' }); + + const vizPanel = new VizPanel({ + title: 'Panel A', + pluginId: 'lib-panel-loading', + key: 'panel-1', + $behaviors: [behavior], + }); + + const libraryPanel: LibraryPanel = { + name: 'LibraryPanel A', + uid: '111', + type: 'table', + model: { + title: 'LibraryPanel A title', + type: 'table', + options: { showHeader: true }, + fieldConfig: { defaults: {}, overrides: [] }, + datasource: { uid: 'abcdef' }, + targets: [{ refId: 'A' }], + }, + version: 1, + }; + + const spy = jest.spyOn(libpanels, 'getLibraryPanel').mockResolvedValue(libraryPanel); + + const gridItem = new DashboardGridItem({ + key: 'griditem-1', + x: 0, + y: 0, + width: 10, + height: 12, + body: vizPanel, + }); + + const scene = new DashboardScene({ + title: 'hello', + uid: 'dash-1', + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [gridItem], + }), + }); + + activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 1)); + + return { scene, gridItem, spy, behavior }; +} diff --git a/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx b/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx new file mode 100644 index 00000000000..501f38e527f --- /dev/null +++ b/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx @@ -0,0 +1,111 @@ +import { PanelPlugin, PanelProps } from '@grafana/data'; +import { SceneObjectBase, SceneObjectState, sceneUtils, VizPanel, VizPanelState } from '@grafana/scenes'; +import { LibraryPanel } from '@grafana/schema'; +import { Stack } from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; +import { PanelModel } from 'app/features/dashboard/state'; +import { getLibraryPanel } from 'app/features/library-panels/state/api'; + +import { createPanelDataProvider } from '../utils/createPanelDataProvider'; + +import { DashboardGridItem } from './DashboardGridItem'; + +interface LibraryPanelBehaviorState extends SceneObjectState { + // Library panels use title from dashboard JSON's panel model, not from library panel definition, hence we pass it. + title?: string; + uid: string; + name: string; + isLoaded?: boolean; + _loadedPanel?: LibraryPanel; +} + +export class LibraryPanelBehavior extends SceneObjectBase { + public static LOADING_VIZ_PANEL_PLUGIN_ID = 'library-panel-loading-plugin'; + + public constructor(state: LibraryPanelBehaviorState) { + super(state); + + this.addActivationHandler(() => this._activationHandler()); + } + + private _activationHandler() { + if (!this.state.isLoaded) { + this.loadLibraryPanelFromPanelModel(); + } + } + + public setPanelFromLibPanel(libPanel: LibraryPanel) { + if (this.state._loadedPanel?.version === libPanel.version) { + return; + } + + const vizPanel = this.parent; + + if (!(vizPanel instanceof VizPanel)) { + return; + } + + const libPanelModel = new PanelModel(libPanel.model); + + const vizPanelState: VizPanelState = { + title: libPanelModel.title, + options: libPanelModel.options ?? {}, + fieldConfig: libPanelModel.fieldConfig, + pluginId: libPanelModel.type, + pluginVersion: libPanelModel.pluginVersion, + displayMode: libPanelModel.transparent ? 'transparent' : undefined, + description: libPanelModel.description, + $data: createPanelDataProvider(libPanelModel), + }; + + vizPanel.setState(vizPanelState); + vizPanel.changePluginType(libPanelModel.type, vizPanelState.options, vizPanelState.fieldConfig); + + this.setState({ _loadedPanel: libPanel, isLoaded: true, name: libPanel.name, title: libPanelModel.title }); + + const layoutElement = vizPanel.parent!; + + // Migrate repeat options to layout element + if (libPanelModel.repeat && layoutElement instanceof DashboardGridItem) { + layoutElement.setState({ + variableName: libPanelModel.repeat, + repeatDirection: libPanelModel.repeatDirection === 'h' ? 'h' : 'v', + maxPerRow: libPanelModel.maxPerRow, + itemHeight: layoutElement.state.height ?? 10, + }); + layoutElement.performRepeat(); + } + } + + private async loadLibraryPanelFromPanelModel() { + let vizPanel = this.parent; + + if (!(vizPanel instanceof VizPanel)) { + return; + } + + try { + const libPanel = await getLibraryPanel(this.state.uid, true); + this.setPanelFromLibPanel(libPanel); + } catch (err) { + vizPanel.setState({ + _pluginLoadError: `Unable to load library panel: ${this.state.uid}`, + }); + } + } +} + +const LoadingVizPanelPlugin = new PanelPlugin(LoadingVizPanel); + +function LoadingVizPanel(props: PanelProps) { + return ( + + Loading library panel + + ); +} + +sceneUtils.registerRuntimePanelPlugin({ + pluginId: LibraryPanelBehavior.LOADING_VIZ_PANEL_PLUGIN_ID, + plugin: LoadingVizPanelPlugin, +}); diff --git a/public/app/features/dashboard-scene/scene/LibraryVizPanel.test.ts b/public/app/features/dashboard-scene/scene/LibraryVizPanel.test.ts deleted file mode 100644 index 5bb05548356..00000000000 --- a/public/app/features/dashboard-scene/scene/LibraryVizPanel.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { waitFor } from '@testing-library/dom'; -import { merge } from 'lodash'; -import { http, HttpResponse } from 'msw'; -import { setupServer, SetupServerApi } from 'msw/node'; - -import { setBackendSrv } from '@grafana/runtime'; -import { SceneGridLayout, VizPanel } from '@grafana/scenes'; -import { LibraryPanel } from '@grafana/schema'; -import { backendSrv } from 'app/core/services/backend_srv'; - -import { DashboardGridItem } from './DashboardGridItem'; -import { LibraryVizPanel } from './LibraryVizPanel'; - -describe('LibraryVizPanel', () => { - const server = setupServer(); - - beforeAll(() => { - setBackendSrv(backendSrv); - server.listen(); - }); - - afterAll(() => { - server.close(); - }); - - beforeEach(() => { - server.resetHandlers(); - }); - - it('should fetch and init', async () => { - setUpApiMock(server); - const libVizPanel = new LibraryVizPanel({ - name: 'My Library Panel', - title: 'Panel title', - uid: 'fdcvggvfy2qdca', - panelKey: 'lib-panel', - }); - libVizPanel.activate(); - await waitFor(() => { - expect(libVizPanel.state.panel).toBeInstanceOf(VizPanel); - }); - }); - - it('should configure repeat options of DashboardGridIem if repeat is set', async () => { - setUpApiMock(server, { model: { repeat: 'query0', repeatDirection: 'h' } }); - const libVizPanel = new LibraryVizPanel({ - name: 'My Library Panel', - title: 'Panel title', - uid: 'fdcvggvfy2qdca', - panelKey: 'lib-panel', - }); - - const layout = new SceneGridLayout({ - children: [new DashboardGridItem({ body: libVizPanel })], - }); - - layout.activate(); - libVizPanel.activate(); - - await waitFor(() => { - expect(layout.state.children[0]).toBeInstanceOf(DashboardGridItem); - expect((layout.state.children[0] as DashboardGridItem).state.variableName).toBe('query0'); - expect((layout.state.children[0] as DashboardGridItem).state.repeatDirection).toBe('h'); - expect((layout.state.children[0] as DashboardGridItem).state.maxPerRow).toBe(4); - }); - }); -}); - -function setUpApiMock( - server: SetupServerApi, - overrides: Omit, 'model'> & { model?: Partial } = {} -) { - const libPanel: LibraryPanel = merge( - { - folderUid: 'general', - uid: 'fdcvggvfy2qdca', - name: 'My Library Panel', - type: 'timeseries', - description: '', - model: { - datasource: { - type: 'grafana-testdata-datasource', - uid: 'PD8C576611E62080A', - }, - description: '', - - maxPerRow: 4, - options: { - legend: { - calcs: [], - displayMode: 'list', - placement: 'bottom', - showLegend: true, - }, - tooltip: { - maxHeight: 600, - mode: 'single', - sort: 'none', - }, - }, - targets: [ - { - datasource: { - type: 'grafana-testdata-datasource', - uid: 'PD8C576611E62080A', - }, - refId: 'A', - }, - ], - title: 'Panel Title', - type: 'timeseries', - }, - version: 6, - meta: { - folderName: 'Dashboards', - folderUid: '', - connectedDashboards: 1, - created: '2024-02-15T15:26:46Z', - updated: '2024-02-28T15:54:22Z', - createdBy: { - avatarUrl: '/avatar/46d229b033af06a191ff2267bca9ae56', - id: 1, - name: 'admin', - }, - updatedBy: { - avatarUrl: '/avatar/46d229b033af06a191ff2267bca9ae56', - id: 1, - name: 'admin', - }, - }, - }, - overrides - ); - - const libPanelMock: { result: LibraryPanel } = { - result: libPanel, - }; - - server.use(http.get('/api/library-elements/:uid', () => HttpResponse.json(libPanelMock))); -} diff --git a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx b/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx deleted file mode 100644 index c5b3d172817..00000000000 --- a/public/app/features/dashboard-scene/scene/LibraryVizPanel.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { - SceneComponentProps, - SceneObjectBase, - SceneObjectState, - VizPanel, - VizPanelMenu, - VizPanelState, -} from '@grafana/scenes'; -import { LibraryPanel } from '@grafana/schema'; -import { PanelModel } from 'app/features/dashboard/state'; -import { getLibraryPanel } from 'app/features/library-panels/state/api'; - -import { createPanelDataProvider } from '../utils/createPanelDataProvider'; - -import { DashboardGridItem } from './DashboardGridItem'; -import { VizPanelLinks, VizPanelLinksMenu } from './PanelLinks'; -import { panelLinksBehavior, panelMenuBehavior } from './PanelMenuBehavior'; -import { PanelNotices } from './PanelNotices'; - -export interface LibraryVizPanelState extends SceneObjectState { - // Library panels use title from dashboard JSON's panel model, not from library panel definition, hence we pass it. - title: string; - uid: string; - name: string; - panel?: VizPanel; - isLoaded?: boolean; - panelKey: string; - _loadedPanel?: LibraryPanel; -} - -export class LibraryVizPanel extends SceneObjectBase { - static Component = LibraryPanelRenderer; - - constructor(state: LibraryVizPanelState) { - super({ - panel: state.panel ?? getLoadingPanel(state.title, state.panelKey), - isLoaded: state.isLoaded ?? false, - ...state, - }); - - this.addActivationHandler(this._onActivate); - } - - private _onActivate = () => { - if (!this.state.isLoaded) { - this.loadLibraryPanelFromPanelModel(); - } - }; - - public setPanelFromLibPanel(libPanel: LibraryPanel) { - if (this.state._loadedPanel?.version === libPanel.version) { - return; - } - - const libPanelModel = new PanelModel(libPanel.model); - - const vizPanelState: VizPanelState = { - title: libPanelModel.title, - key: this.state.panelKey, - options: libPanelModel.options ?? {}, - fieldConfig: libPanelModel.fieldConfig, - pluginId: libPanelModel.type, - pluginVersion: libPanelModel.pluginVersion, - displayMode: libPanelModel.transparent ? 'transparent' : undefined, - description: libPanelModel.description, - // To be replaced with it's own option persisted option instead derived - hoverHeader: !libPanelModel.title && !libPanelModel.timeFrom && !libPanelModel.timeShift, - hoverHeaderOffset: 0, - $data: createPanelDataProvider(libPanelModel), - menu: new VizPanelMenu({ $behaviors: [panelMenuBehavior] }), - titleItems: [ - new VizPanelLinks({ - rawLinks: libPanelModel.links, - menu: new VizPanelLinksMenu({ $behaviors: [panelLinksBehavior] }), - }), - new PanelNotices(), - ], - }; - - const panel = new VizPanel(vizPanelState); - - this.setState({ panel, _loadedPanel: libPanel, isLoaded: true, name: libPanel.name }); - } - - private async loadLibraryPanelFromPanelModel() { - let vizPanel = this.state.panel!; - - try { - const libPanel = await getLibraryPanel(this.state.uid, true); - this.setPanelFromLibPanel(libPanel); - if (this.parent instanceof DashboardGridItem) { - this.parent.setState({ - variableName: libPanel.model.repeat, - repeatDirection: libPanel.model.repeatDirection === 'h' ? 'h' : 'v', - maxPerRow: libPanel.model.maxPerRow, - }); - } - } catch (err) { - vizPanel.setState({ - _pluginLoadError: `Unable to load library panel: ${this.state.uid}`, - }); - } - } -} - -function getLoadingPanel(title: string, panelKey: string) { - return new VizPanel({ - key: panelKey, - title, - menu: new VizPanelMenu({ - $behaviors: [panelMenuBehavior], - }), - }); -} - -function LibraryPanelRenderer({ model }: SceneComponentProps) { - const { panel } = model.useState(); - - if (!panel) { - return null; - } - - return ; -} diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index 07ab8bfd828..d062d96a7f0 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -30,10 +30,10 @@ import ExportButton from '../sharing/ExportButton/ExportButton'; import ShareButton from '../sharing/ShareButton/ShareButton'; import { DashboardInteractions } from '../utils/interactions'; import { DynamicDashNavButtonModel, dynamicDashNavActions } from '../utils/registerDynamicDashNavAction'; +import { isLibraryPanel } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; import { GoToSnapshotOriginButton } from './GoToSnapshotOriginButton'; -import { LibraryVizPanel } from './LibraryVizPanel'; interface Props { dashboard: DashboardScene; @@ -619,7 +619,7 @@ function useEditingLibraryPanel(panelEditor?: PanelEditor) { useEffect(() => { if (panelEditor) { const unsub = panelEditor.state.vizManager.subscribeToState((vizManagerState) => - setEditingLibraryPanel(vizManagerState.sourcePanel.resolve().parent instanceof LibraryVizPanel) + setEditingLibraryPanel(isLibraryPanel(vizManagerState.sourcePanel.resolve())) ); return () => { unsub.unsubscribe(); diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index 9d3d657401b..982bd12b6b2 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -26,10 +26,9 @@ import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareModal } from '../sharing/ShareModal'; import { DashboardInteractions } from '../utils/interactions'; import { getEditPanelUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders'; -import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; +import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; -import { LibraryVizPanel } from './LibraryVizPanel'; import { VizPanelLinks, VizPanelLinksMenu } from './PanelLinks'; import { UnlinkLibraryPanelModal } from './UnlinkLibraryPanelModal'; @@ -41,7 +40,6 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { // hm.. add another generic param to SceneObject to specify parent type? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const panel = menu.parent as VizPanel; - const parent = panel.parent; const plugin = panel.getPlugin(); const items: PanelMenuItem[] = []; @@ -147,6 +145,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { if (dashboard.state.isEditing && !isRepeat && !isEditingPanel) { moreSubMenu.push({ text: t('panel.header-menu.duplicate', `Duplicate`), + iconClassName: 'file-copy-alt', onClick: () => { dashboard.duplicatePanel(panel); }, @@ -157,6 +156,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { if (!isEditingPanel) { moreSubMenu.push({ text: t('panel.header-menu.copy', `Copy`), + iconClassName: 'copy', onClick: () => { dashboard.copyPanel(panel); }, @@ -164,13 +164,14 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { } if (dashboard.state.isEditing && !isRepeat && !isEditingPanel) { - if (parent instanceof LibraryVizPanel) { + if (isLibraryPanel(panel)) { moreSubMenu.push({ text: t('panel.header-menu.unlink-library-panel', `Unlink library panel`), + iconClassName: 'link-broken', onClick: () => { dashboard.showModal( new UnlinkLibraryPanelModal({ - panelRef: parent.getRef(), + panelRef: panel.getRef(), }) ); }, @@ -178,14 +179,16 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { moreSubMenu.push({ text: t('panel.header-menu.replace-library-panel', `Replace library panel`), + iconClassName: 'library-panel', onClick: () => { - dashboard.onShowAddLibraryPanelDrawer(parent.getRef()); + dashboard.onShowAddLibraryPanelDrawer(panel.getRef()); }, }); } else { if (config.featureToggles.newDashboardSharingComponent) { moreSubMenu.push({ text: t('share-panel.menu.new-library-panel-title', 'New library panel'), + iconClassName: 'plus-square', onClick: () => { const drawer = new ShareDrawer({ shareView: shareDashboardType.libraryPanel, @@ -213,6 +216,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { moreSubMenu.push({ text: t('panel.header-menu.new-alert-rule', `New alert rule`), + iconClassName: 'bell', onClick: (e) => onCreateAlert(panel), }); @@ -221,6 +225,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { text: panel.state.options.legend.showLegend ? t('panel.header-menu.hide-legend', 'Hide legend') : t('panel.header-menu.show-legend', 'Show legend'), + iconClassName: panel.state.options.legend.showLegend ? 'legend-hide' : 'legend-show', onClick: (e) => { e.preventDefault(); toggleVizPanelLegend(panel); @@ -232,6 +237,7 @@ export function panelMenuBehavior(menu: VizPanelMenu, isRepeat = false) { if (dashboard.canEditDashboard() && plugin && !plugin.meta.skipDataQuery && !isRepeat) { moreSubMenu.push({ text: t('panel.header-menu.get-help', 'Get help'), + iconClassName: 'question-circle', onClick: (e: React.MouseEvent) => { e.preventDefault(); onInspectPanel(panel, InspectTab.Help); diff --git a/public/app/features/dashboard-scene/scene/UnlinkLibraryPanelModal.tsx b/public/app/features/dashboard-scene/scene/UnlinkLibraryPanelModal.tsx index 3db5c17c0c1..3b1db237165 100644 --- a/public/app/features/dashboard-scene/scene/UnlinkLibraryPanelModal.tsx +++ b/public/app/features/dashboard-scene/scene/UnlinkLibraryPanelModal.tsx @@ -1,13 +1,12 @@ -import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState } from '@grafana/scenes'; +import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes'; import { ModalSceneObjectLike } from '../sharing/types'; import { getDashboardSceneFor } from '../utils/utils'; -import { LibraryVizPanel } from './LibraryVizPanel'; import { UnlinkModal } from './UnlinkModal'; interface UnlinkLibraryPanelModalState extends SceneObjectState { - panelRef?: SceneObjectRef; + panelRef?: SceneObjectRef; } export class UnlinkLibraryPanelModal diff --git a/public/app/features/dashboard-scene/scene/ViewPanelScene.test.tsx b/public/app/features/dashboard-scene/scene/ViewPanelScene.test.tsx index d0914330094..5919e5c432c 100644 --- a/public/app/features/dashboard-scene/scene/ViewPanelScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/ViewPanelScene.test.tsx @@ -4,19 +4,19 @@ import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene } from './DashboardScene'; import { ViewPanelScene } from './ViewPanelScene'; +jest.mock('@grafana/runtime', () => ({ + ...jest.requireActual('@grafana/runtime'), + getPluginImportUtils: () => ({ + getPanelPluginFromCache: jest.fn(() => {}), + }), +})); + describe('ViewPanelScene', () => { - it('Should build scene on activate', () => { - const { viewPanelScene } = buildScene(); + it('Should activate panel parents', () => { + const { viewPanelScene, dashboard } = buildScene(); viewPanelScene.activate(); - expect(viewPanelScene.state.body).toBeDefined(); - }); - - it('Should look copy row variable scope', () => { - const { viewPanelScene } = buildScene({ rowVariables: true, panelVariables: true }); - viewPanelScene.activate(); - - const variables = viewPanelScene.state.body?.state.$variables; - expect(variables?.state.variables.length).toBe(2); + expect(viewPanelScene.state.panelRef.resolve().isActive).toBe(true); + expect(dashboard.state.body.isActive).toBe(true); }); }); @@ -29,11 +29,6 @@ function buildScene(options?: SceneOptions) { // builds a scene how it looks like after row and panel repeats are processed const panel = new VizPanel({ key: 'panel-22', - $variables: options?.panelVariables - ? new SceneVariableSet({ - variables: [new LocalValueVariable({ value: 'panel-var-value' })], - }) - : undefined, }); const dashboard = new DashboardScene({ @@ -43,11 +38,9 @@ function buildScene(options?: SceneOptions) { x: 0, y: 10, width: 24, - $variables: options?.rowVariables - ? new SceneVariableSet({ - variables: [new LocalValueVariable({ value: 'row-var-value' })], - }) - : undefined, + $variables: new SceneVariableSet({ + variables: [new LocalValueVariable({ value: 'row-var-value' })], + }), height: 1, children: [ new DashboardGridItem({ diff --git a/public/app/features/dashboard-scene/scene/ViewPanelScene.tsx b/public/app/features/dashboard-scene/scene/ViewPanelScene.tsx index 33885113f0a..969ed5f1358 100644 --- a/public/app/features/dashboard-scene/scene/ViewPanelScene.tsx +++ b/public/app/features/dashboard-scene/scene/ViewPanelScene.tsx @@ -1,20 +1,9 @@ -import { - SceneComponentProps, - SceneObjectBase, - SceneObjectRef, - SceneObjectState, - VizPanel, - sceneUtils, - SceneVariables, - SceneGridRow, - sceneGraph, - SceneVariableSet, - SceneVariable, -} from '@grafana/scenes'; +import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes'; + +import { activateInActiveParents } from '../utils/utils'; interface ViewPanelSceneState extends SceneObjectState { panelRef: SceneObjectRef; - body?: VizPanel; } export class ViewPanelScene extends SceneObjectBase { @@ -26,47 +15,7 @@ export class ViewPanelScene extends SceneObjectBase { public _activationHandler() { const panel = this.state.panelRef.resolve(); - const panelState = sceneUtils.cloneSceneObjectState(panel.state, { - key: panel.state.key + '-view', - $variables: this.getScopedVariables(panel), - }); - - const body = new VizPanel(panelState); - - this.setState({ body }); - - return () => { - // Make sure we preserve data state - if (body.state.$data) { - panel.setState({ $data: body.state.$data.clone() }); - } - }; - } - - // In case the panel is inside a repeated row - private getScopedVariables(panel: VizPanel): SceneVariables | undefined { - const row = tryGetParentRow(panel); - const variables: SceneVariable[] = []; - - // Because we are rendering the panel outside it's potential row context we need to copy the row (scoped) varables - if (row && row.state.$variables) { - for (const variable of row.state.$variables.state.variables) { - variables.push(variable.clone()); - } - } - - // If we have local scoped panel variables we need to add the row variables to it - if (panel.state.$variables) { - for (const variable of panel.state.$variables.state.variables) { - variables.push(variable.clone()); - } - } - - if (variables.length > 0) { - return new SceneVariableSet({ variables }); - } - - return undefined; + return activateInActiveParents(panel); } public getUrlKey() { @@ -74,20 +23,9 @@ export class ViewPanelScene extends SceneObjectBase { } public static Component = ({ model }: SceneComponentProps) => { - const { body } = model.useState(); + const { panelRef } = model.useState(); + const panel = panelRef.resolve(); - if (!body) { - return null; - } - - return ; + return ; }; } - -function tryGetParentRow(panel: VizPanel): SceneGridRow | undefined { - try { - return sceneGraph.getAncestor(panel, SceneGridRow); - } catch { - return undefined; - } -} diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts index f1e4361f04b..e5c7ab36bca 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts @@ -28,7 +28,7 @@ import { DashboardDataDTO } from 'app/types'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { NEW_LINK } from '../settings/links/utils'; @@ -44,7 +44,6 @@ import { buildGridItemForPanel, transformSaveModelToScene, convertOldSnapshotToScenesSnapshot, - buildGridItemForLibPanel, } from './transformSaveModelToScene'; describe('transformSaveModelToScene', () => { @@ -388,7 +387,9 @@ describe('transformSaveModelToScene', () => { expect(rowScene.state.children[0]).toBeInstanceOf(DashboardGridItem); expect(rowScene.state.children[1]).toBeInstanceOf(DashboardGridItem); // Panels are sorted by position in the row - expect((rowScene.state.children[0] as DashboardGridItem).state.body!).toBeInstanceOf(LibraryVizPanel); + expect((rowScene.state.children[0] as DashboardGridItem).state.body.state.$behaviors![0]).toBeInstanceOf( + LibraryPanelBehavior + ); expect((rowScene.state.children[1] as DashboardGridItem).state.body!).toBeInstanceOf(VizPanel); }); @@ -481,7 +482,7 @@ describe('transformSaveModelToScene', () => { // lib panel out of row expect(body.state.children[1]).toBeInstanceOf(DashboardGridItem); const panelOutOfRowLibVizPanel = body.state.children[1] as DashboardGridItem; - expect(panelOutOfRowLibVizPanel.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(panelOutOfRowLibVizPanel.state.body.state.$behaviors![0]).toBeInstanceOf(LibraryPanelBehavior); // Row with panels expect(body.state.children[2]).toBeInstanceOf(SceneGridRow); const rowWithPanelsScene = body.state.children[2] as SceneGridRow; @@ -489,7 +490,7 @@ describe('transformSaveModelToScene', () => { expect(rowWithPanelsScene.state.key).toBe('panel-10'); expect(rowWithPanelsScene.state.children).toHaveLength(2); const libPanel = rowWithPanelsScene.state.children[1] as DashboardGridItem; - expect(libPanel.state.body!).toBeInstanceOf(LibraryVizPanel); + expect(libPanel.state.body.state.$behaviors![0]).toBeInstanceOf(LibraryPanelBehavior); // Panel within row expect(rowWithPanelsScene.state.children[0]).toBeInstanceOf(DashboardGridItem); const panelInRowVizPanel = rowWithPanelsScene.state.children[0] as DashboardGridItem; @@ -721,7 +722,7 @@ describe('transformSaveModelToScene', () => { expect(runner.state.queryCachingTTL).toBe(200000); }); - it('should convert saved lib panel to LibraryVizPanel', () => { + it('should convert saved lib panel to a viz panel with LibraryPanelBehavior', () => { const panel = { title: 'Panel', gridPos: { x: 0, y: 0, w: 12, h: 8 }, @@ -733,12 +734,13 @@ describe('transformSaveModelToScene', () => { }, }; - const gridItem = buildGridItemForLibPanel(new PanelModel(panel))!; - const libVizPanel = gridItem.state.body as LibraryVizPanel; + const gridItem = buildGridItemForPanel(new PanelModel(panel))!; + const libPanelBehavior = gridItem.state.body.state.$behaviors![0]; - expect(libVizPanel.state.uid).toEqual(panel.libraryPanel.uid); - expect(libVizPanel.state.name).toEqual(panel.libraryPanel.name); - expect(libVizPanel.state.title).toEqual(panel.title); + expect(libPanelBehavior).toBeInstanceOf(LibraryPanelBehavior); + expect((libPanelBehavior as LibraryPanelBehavior).state.uid).toEqual(panel.libraryPanel.uid); + expect((libPanelBehavior as LibraryPanelBehavior).state.name).toEqual(panel.libraryPanel.name); + expect(gridItem.state.body.state.title).toEqual(panel.title); }); }); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index f2c1ce703f3..5f761ad10ab 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -32,7 +32,7 @@ import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem, RepeatDirection } from '../scene/DashboardGridItem'; import { registerDashboardMacro } from '../scene/DashboardMacro'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { panelLinksBehavior, panelMenuBehavior } from '../scene/PanelMenuBehavior'; import { PanelNotices } from '../scene/PanelNotices'; @@ -107,18 +107,6 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI currentRowPanels = []; } } - } else if (panel.libraryPanel?.uid && !('model' in panel.libraryPanel)) { - const gridItem = buildGridItemForLibPanel(panel); - - if (!gridItem) { - continue; - } - - if (currentRow) { - currentRowPanels.push(gridItem); - } else { - panels.push(gridItem); - } } else { // when rendering a snapshot created with the legacy Dashboards convert data to new snapshot format to be compatible with Scenes if (panel.snapshotData) { @@ -153,16 +141,6 @@ function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]): saveModel = new PanelModel(saveModel); } - if (saveModel.libraryPanel?.uid && !('model' in saveModel.libraryPanel)) { - const gridItem = buildGridItemForLibPanel(saveModel); - - if (!gridItem) { - throw new Error('Failed to build grid item for library panel'); - } - - return gridItem; - } - return buildGridItemForPanel(saveModel); }); } @@ -287,29 +265,6 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, return dashboardScene; } -export function buildGridItemForLibPanel(panel: PanelModel) { - if (!panel.libraryPanel) { - return null; - } - - const body = new LibraryVizPanel({ - title: panel.title, - uid: panel.libraryPanel.uid, - name: panel.libraryPanel.name, - panelKey: getVizPanelKeyForPanelId(panel.id), - }); - - return new DashboardGridItem({ - key: `grid-item-${panel.id}`, - y: panel.gridPos.y, - x: panel.gridPos.x, - width: panel.gridPos.w, - height: panel.gridPos.h, - itemHeight: panel.gridPos.h, - body, - }); -} - export function buildGridItemForPanel(panel: PanelModel): DashboardGridItem { const repeatOptions: Partial<{ variableName: string; repeatDirection: RepeatDirection }> = panel.repeat ? { @@ -333,7 +288,7 @@ export function buildGridItemForPanel(panel: PanelModel): DashboardGridItem { key: getVizPanelKeyForPanelId(panel.id), title: panel.title, description: panel.description, - pluginId: panel.type, + pluginId: panel.type ?? 'timeseries', options: panel.options ?? {}, fieldConfig: panel.fieldConfig, pluginVersion: panel.pluginVersion, @@ -343,11 +298,19 @@ export function buildGridItemForPanel(panel: PanelModel): DashboardGridItem { hoverHeaderOffset: 0, $data: createPanelDataProvider(panel), titleItems, - + $behaviors: [], extendPanelContext: setDashboardPanelContext, _UNSAFE_customMigrationHandler: getAngularPanelMigrationHandler(panel), }; + if (panel.libraryPanel) { + vizPanelState.$behaviors!.push( + new LibraryPanelBehavior({ uid: panel.libraryPanel.uid, name: panel.libraryPanel.name }) + ); + vizPanelState.pluginId = LibraryPanelBehavior.LOADING_VIZ_PANEL_PLUGIN_ID; + vizPanelState.$data = undefined; + } + if (!config.publicDashboardAccessToken) { vizPanelState.menu = new VizPanelMenu({ $behaviors: [panelMenuBehavior], diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts index 042d2cd2ce7..94497918e64 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts @@ -33,7 +33,7 @@ import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { NEW_LINK } from '../settings/links/utils'; import { activateFullSceneTree, buildPanelRepeaterScene } from '../utils/test-utils'; @@ -44,11 +44,7 @@ import dashboard_to_load1 from './testfiles/dashboard_to_load1.json'; import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json'; import snapshotableDashboardJson from './testfiles/snapshotable_dashboard.json'; import snapshotableWithRowsDashboardJson from './testfiles/snapshotable_with_rows.json'; -import { - buildGridItemForLibPanel, - buildGridItemForPanel, - transformSaveModelToScene, -} from './transformSaveModelToScene'; +import { buildGridItemForPanel, transformSaveModelToScene } from './transformSaveModelToScene'; import { gridItemToPanel, gridRowToSaveModel, @@ -348,33 +344,33 @@ describe('transformSceneToSaveModel', () => { describe('Library panels', () => { it('given a library panel', () => { - // Not using buildGridItemFromPanelSchema since it strips options/fieldConfig - const libVizPanel = new LibraryVizPanel({ - name: 'Some lib panel panel', - title: 'A panel', - uid: 'lib-panel-uid', - panelKey: 'lib-panel', - panel: new VizPanel({ - key: 'panel-4', - title: 'Panel blahh blah', - fieldConfig: { - defaults: {}, - overrides: [], + const libVizPanel = new VizPanel({ + key: 'panel-4', + title: 'Panel blahh blah', + $behaviors: [ + new LibraryPanelBehavior({ + name: 'Some lib panel panel', + title: 'A panel', + uid: 'lib-panel-uid', + }), + ], + fieldConfig: { + defaults: {}, + overrides: [], + }, + options: { + legend: { + calcs: [], + displayMode: 'list', + placement: 'bottom', + showLegend: true, }, - options: { - legend: { - calcs: [], - displayMode: 'list', - placement: 'bottom', - showLegend: true, - }, - tooltip: { - maxHeight: 600, - mode: 'single', - sort: 'none', - }, + tooltip: { + maxHeight: 600, + mode: 'single', + sort: 'none', }, - }), + }, }); const panel = new DashboardGridItem({ @@ -824,32 +820,33 @@ describe('transformSceneToSaveModel', () => { it('handles repeated library panels', () => { const { scene, repeater } = buildPanelRepeaterScene( { variableQueryTime: 0, numberOfOptions: 2 }, - new LibraryVizPanel({ - name: 'Some lib panel panel', - title: 'A panel', - uid: 'lib-panel-uid', - panelKey: 'lib-panel', - panel: new VizPanel({ - key: 'panel-4', - title: 'Panel blahh blah', - fieldConfig: { - defaults: {}, - overrides: [], + new VizPanel({ + key: 'panel-4', + title: 'Panel blahh blah', + fieldConfig: { + defaults: {}, + overrides: [], + }, + options: { + legend: { + calcs: [], + displayMode: 'list', + placement: 'bottom', + showLegend: true, }, - options: { - legend: { - calcs: [], - displayMode: 'list', - placement: 'bottom', - showLegend: true, - }, - tooltip: { - maxHeight: 600, - mode: 'single', - sort: 'none', - }, + tooltip: { + maxHeight: 600, + mode: 'single', + sort: 'none', }, - }), + }, + $behaviors: [ + new LibraryPanelBehavior({ + name: 'Some lib panel panel', + title: 'A panel', + uid: 'lib-panel-uid', + }), + ], }) ); @@ -1138,9 +1135,5 @@ describe('transformSceneToSaveModel', () => { }); export function buildGridItemFromPanelSchema(panel: Partial) { - if (panel.libraryPanel) { - return buildGridItemForLibPanel(new PanelModel(panel))!; - } - return buildGridItemForPanel(new PanelModel(panel)); } diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index 1077fcc0145..45db9cf1fdb 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -34,11 +34,10 @@ import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; -import { getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; +import { getLibraryPanelBehavior, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel } from '../utils/utils'; import { GRAFANA_DATASOURCE_REF } from './const'; import { dataLayersToAnnotations } from './dataLayersToAnnotations'; @@ -140,22 +139,6 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa return sortedDeepCloneWithoutNulls(dashboard); } -export function libraryVizPanelToPanel(libPanel: LibraryVizPanel, gridPos: GridPos): Panel { - if (!libPanel.state.panel) { - throw new Error('Library panel has no panel'); - } - - return { - id: getPanelIdForVizPanel(libPanel.state.panel), - title: libPanel.state.title, - gridPos: gridPos, - libraryPanel: { - name: libPanel.state.name, - uid: libPanel.state.uid, - }, - } as Panel; -} - export function gridItemToPanel( gridItem: DashboardGridItem, sceneState?: DashboardSceneState, @@ -167,16 +150,6 @@ export function gridItemToPanel( w = 0, h = 0; - // Handle library panels, early exit - if (gridItem.state.body instanceof LibraryVizPanel) { - x = gridItem.state.x ?? 0; - y = gridItem.state.y ?? 0; - w = gridItem.state.width ?? 0; - h = gridItem.state.itemHeight ?? gridItem.state.height ?? 0; - - return libraryVizPanelToPanel(gridItem.state.body, { x, y, w, h }); - } - let gridItem_ = gridItem; // If we're saving while the panel editor is open, we need to persist those changes in the panel model @@ -186,7 +159,7 @@ export function gridItemToPanel( sceneState.editPanel.state.vizManager.state.sourcePanel.resolve() === gridItem.state.body ) { const gridItemClone = gridItem.clone(); - if (gridItemClone.state.body instanceof VizPanel) { + if (gridItemClone.state.body instanceof VizPanel && !isLibraryPanel(gridItemClone.state.body)) { sceneState.editPanel.state.vizManager.commitChangesTo(gridItemClone.state.body); gridItem_ = gridItemClone; } @@ -217,18 +190,36 @@ export function vizPanelToPanel( isSnapshot = false, gridItem?: SceneGridItemLike ) { - const panel: Panel = { - id: getPanelIdForVizPanel(vizPanel), - type: vizPanel.state.pluginId, - title: vizPanel.state.title, - description: vizPanel.state.description ?? undefined, - gridPos, - fieldConfig: (vizPanel.state.fieldConfig as FieldConfigSource) ?? { defaults: {}, overrides: [] }, - transformations: [], - transparent: vizPanel.state.displayMode === 'transparent', - pluginVersion: vizPanel.state.pluginVersion, - ...vizPanelDataToPanel(vizPanel, isSnapshot), - }; + let panel: Panel; + + if (isLibraryPanel(vizPanel)) { + const libPanel = getLibraryPanelBehavior(vizPanel); + + panel = { + id: getPanelIdForVizPanel(vizPanel), + title: libPanel!.state.title, + gridPos: gridPos, + libraryPanel: { + name: libPanel!.state.name, + uid: libPanel!.state.uid, + }, + } as Panel; + + return panel; + } else { + panel = { + id: getPanelIdForVizPanel(vizPanel), + type: vizPanel.state.pluginId, + title: vizPanel.state.title, + description: vizPanel.state.description ?? undefined, + gridPos, + fieldConfig: (vizPanel.state.fieldConfig as FieldConfigSource) ?? { defaults: {}, overrides: [] }, + transformations: [], + transparent: vizPanel.state.displayMode === 'transparent', + pluginVersion: vizPanel.state.pluginVersion, + ...vizPanelDataToPanel(vizPanel, isSnapshot), + }; + } if (vizPanel.state.options) { const { angularOptions, ...rest } = vizPanel.state.options as any; @@ -342,9 +333,10 @@ export function panelRepeaterToPanels( if (!isSnapshot) { return [gridItemToPanel(repeater, sceneState)]; } else { - if (repeater.state.body instanceof LibraryVizPanel) { + // return early if the repeated panel is a library panel + if (repeater.state.body instanceof VizPanel && isLibraryPanel(repeater.state.body)) { const { x = 0, y = 0, width: w = 0, height: h = 0 } = repeater.state; - return [libraryVizPanelToPanel(repeater.state.body, { x, y, w, h })]; + return [vizPanelToPanel(repeater.state.body, { x, y, w, h }, isSnapshot)]; } if (repeater.state.repeatedPanels) { diff --git a/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsJson.tsx b/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsJson.tsx index 42fd0ef7a51..0130441e843 100644 --- a/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsJson.tsx +++ b/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsJson.tsx @@ -19,7 +19,7 @@ export class ExportAsJson extends ShareExportTab { static Component = ExportAsJsonRenderer; public getTabLabel(): string { - return t('export.json.title', 'Save dashboard JSON'); + return t('export.json.title', 'Export dashboard JSON'); } } diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/CreateEmailSharing.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/CreateEmailSharing.tsx index bdece958b30..cf2761f8a40 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/CreateEmailSharing.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/CreateEmailSharing.tsx @@ -15,7 +15,7 @@ import { EmailSharingPricingAlert } from '../../../../../dashboard/components/Sh import { useShareDrawerContext } from '../../../ShareDrawer/ShareDrawerContext'; export const CreateEmailSharing = ({ hasError }: { hasError: boolean }) => { - const { dashboard } = useShareDrawerContext(); + const { dashboard, onDismiss } = useShareDrawerContext(); const styles = useStyles2(getStyles); const [createPublicDashboard, { isLoading, isError }] = useCreatePublicDashboardMutation(); @@ -49,7 +49,7 @@ export const CreateEmailSharing = ({ hasError }: { hasError: boolean }) => { - {isLoading && } diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.test.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.test.tsx index 39399c1d8c5..205bf6bc12c 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.test.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.test.tsx @@ -108,7 +108,7 @@ async function setup(panelState?: Partial, dashboardState?: Parti await act(async () => render( - + {} }}> ) diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.tsx index e81d6159dae..9e74065d62e 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareAlerts.tsx @@ -10,6 +10,7 @@ import { import { AccessControlAction } from 'app/types'; import { NoUpsertPermissionsAlert } from '../../../../dashboard/components/ShareModal/SharePublicDashboard/ModalAlerts/NoUpsertPermissionsAlert'; +import { PublicDashboardAlert } from '../../../../dashboard/components/ShareModal/SharePublicDashboard/ModalAlerts/PublicDashboardAlert'; import { useShareDrawerContext } from '../../ShareDrawer/ShareDrawerContext'; import { useUnsupportedDatasources } from '../../public-dashboards/hooks'; @@ -29,6 +30,7 @@ export default function ShareAlerts({ publicDashboard }: { publicDashboard?: Pub {publicDashboard?.share === PublicDashboardShareType.EMAIL && isEmailSharingEnabled() && ( )} + {publicDashboard?.share === PublicDashboardShareType.PUBLIC && } ); } diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx index 405389d802f..f27a9d2e84b 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareConfiguration.tsx @@ -113,7 +113,7 @@ export default function ShareConfiguration() { style={{ flex: 1 }} description={t( 'public-dashboard.configuration.display-annotations-description', - 'Present annotations on this Dashboard' + 'Present annotations on this dashboard' )} > Display annotations diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareTypeSelect.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareTypeSelect.tsx index 6ad6e11776e..395ea3cf380 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareTypeSelect.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-externally/ShareTypeSelect.tsx @@ -82,10 +82,10 @@ export default function ShareTypeSelect({ className={styles.select} /> ) : ( - <> + {toIconName(anyOneWithTheLinkOpt.icon) && } {anyOneWithTheLinkOpt.label} - + )} ); diff --git a/public/app/features/dashboard-scene/sharing/ShareButton/share-internally/ShareInternally.tsx b/public/app/features/dashboard-scene/sharing/ShareButton/share-internally/ShareInternally.tsx index fb29868b59e..76a9111d645 100644 --- a/public/app/features/dashboard-scene/sharing/ShareButton/share-internally/ShareInternally.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareButton/share-internally/ShareInternally.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; import { SceneComponentProps } from '@grafana/scenes'; -import { Alert, ClipboardButton, Divider, Text, useStyles2 } from '@grafana/ui'; +import { Alert, ClipboardButton, Divider, Stack, Text, useStyles2 } from '@grafana/ui'; import { t, Trans } from 'app/core/internationalization'; import ShareInternallyConfiguration from '../../ShareInternallyConfiguration'; @@ -66,7 +66,7 @@ function ShareInternallyRenderer({ model }: SceneComponentProps return ( <> - + Updating your settings will modify the default copy link to include these changes. Please note that these settings are saved within your current browser scope. @@ -90,17 +90,19 @@ function ShareInternallyRenderer({ model }: SceneComponentProps isLoading={isBuildUrlLoading} /> - - Copy link - + + + Copy link + + ); } diff --git a/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawer.tsx b/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawer.tsx index e06e31549a4..5e3753ca32c 100644 --- a/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawer.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawer.tsx @@ -64,7 +64,7 @@ function ShareDrawerRenderer({ model }: SceneComponentProps) { return ( - + {} diff --git a/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerContext.tsx b/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerContext.tsx index 213f2c2f964..1c9ab287cf3 100644 --- a/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerContext.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareDrawer/ShareDrawerContext.tsx @@ -4,6 +4,7 @@ import { DashboardScene } from '../../scene/DashboardScene'; interface Context { dashboard: DashboardScene; + onDismiss: () => void; } export const ShareDrawerContext = createContext(undefined); diff --git a/public/app/features/dashboard-scene/sharing/ShareModal.tsx b/public/app/features/dashboard-scene/sharing/ShareModal.tsx index da4d8c75320..7e76957e41c 100644 --- a/public/app/features/dashboard-scene/sharing/ShareModal.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareModal.tsx @@ -8,9 +8,8 @@ import { t } from 'app/core/internationalization'; import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils'; import { getTrackingSource } from '../../dashboard/components/ShareModal/utils'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { DashboardInteractions } from '../utils/interactions'; -import { getDashboardSceneFor } from '../utils/utils'; +import { getDashboardSceneFor, isLibraryPanel } from '../utils/utils'; import { ShareExportTab } from './ShareExportTab'; import { ShareLibraryPanelTab } from './ShareLibraryPanelTab'; @@ -66,9 +65,8 @@ export class ShareModal extends SceneObjectBase implements Moda if (panelRef) { tabs.push(new SharePanelEmbedTab({ panelRef })); const panel = panelRef.resolve(); - const isLibraryPanel = panel.parent instanceof LibraryVizPanel; if (panel instanceof VizPanel) { - if (!isLibraryPanel) { + if (!isLibraryPanel(panel)) { tabs.push(new ShareLibraryPanelTab({ panelRef, modalRef })); } } diff --git a/public/app/features/dashboard-scene/sharing/public-dashboards/utils.ts b/public/app/features/dashboard-scene/sharing/public-dashboards/utils.ts index ba10cb5e976..37b49dc9d29 100644 --- a/public/app/features/dashboard-scene/sharing/public-dashboards/utils.ts +++ b/public/app/features/dashboard-scene/sharing/public-dashboards/utils.ts @@ -12,7 +12,6 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { DashboardGridItem } from '../../scene/DashboardGridItem'; import { DashboardScene } from '../../scene/DashboardScene'; -import { LibraryVizPanel } from '../../scene/LibraryVizPanel'; export const getUnsupportedDashboardDatasources = async (types: string[]): Promise => { let unsupportedDS = new Set(); @@ -64,12 +63,10 @@ function rowTypes(gridRow: SceneGridRow) { } function panelDatasourceTypes(gridItem: SceneGridItemLike) { - let vizPanel: VizPanel | LibraryVizPanel | undefined; + let vizPanel: VizPanel | undefined; if (gridItem instanceof DashboardGridItem) { - if (gridItem.state.body instanceof LibraryVizPanel) { - vizPanel = gridItem.state.body.state.panel; - } else if (gridItem.state.body instanceof VizPanel) { + if (gridItem.state.body instanceof VizPanel) { vizPanel = gridItem.state.body; } else { throw new Error('DashboardGridItem body expected to be VizPanel'); diff --git a/public/app/features/dashboard-scene/utils/PanelModelCompatibilityWrapper.test.ts b/public/app/features/dashboard-scene/utils/PanelModelCompatibilityWrapper.test.ts index ed445f65cf3..e6e44c7a583 100644 --- a/public/app/features/dashboard-scene/utils/PanelModelCompatibilityWrapper.test.ts +++ b/public/app/features/dashboard-scene/utils/PanelModelCompatibilityWrapper.test.ts @@ -1,6 +1,6 @@ import { VizPanel } from '@grafana/scenes'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { PanelModelCompatibilityWrapper } from './PanelModelCompatibilityWrapper'; @@ -12,15 +12,19 @@ describe('PanelModelCompatibilityWrapper', () => { }); it('Can get legacy id for lib panel', () => { - const libPanel = new LibraryVizPanel({ + const panel = new VizPanel({ pluginId: 'test', title: 'test', description: 'test', key: 'panel-24' }); + + const libPanel = new LibraryPanelBehavior({ uid: 'a', name: 'aa', title: 'a', - panelKey: 'panel-24', - panel: new VizPanel({ pluginId: 'test', title: 'test', description: 'test', key: 'panel-24' }), }); - const panelModel = new PanelModelCompatibilityWrapper(libPanel.state.panel!); + panel.setState({ + $behaviors: [libPanel], + }); + + const panelModel = new PanelModelCompatibilityWrapper(panel); expect(panelModel.id).toBe(24); }); }); diff --git a/public/app/features/dashboard-scene/utils/dashboardSceneGraph.test.ts b/public/app/features/dashboard-scene/utils/dashboardSceneGraph.test.ts index e9cd110226d..7e97d9530dd 100644 --- a/public/app/features/dashboard-scene/utils/dashboardSceneGraph.test.ts +++ b/public/app/features/dashboard-scene/utils/dashboardSceneGraph.test.ts @@ -6,7 +6,7 @@ import { DashboardControls } from '../scene/DashboardControls'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { dashboardSceneGraph, getNextPanelId } from './dashboardSceneGraph'; @@ -129,11 +129,16 @@ describe('dashboardSceneGraph', () => { }), }), new DashboardGridItem({ - body: new LibraryVizPanel({ - uid: 'uid', - name: 'LibPanel', - title: 'Library Panel', - panelKey: 'panel-2', + body: new VizPanel({ + title: 'Library Panel 1', + key: 'panel-2', + $behaviors: [ + new LibraryPanelBehavior({ + uid: 'uid', + name: 'LibPanel', + title: 'Library Panel 1', + }), + ], }), }), new DashboardGridItem({ @@ -166,11 +171,16 @@ describe('dashboardSceneGraph', () => { }), }), new DashboardGridItem({ - body: new LibraryVizPanel({ - uid: 'uid', - name: 'LibPanel', - title: 'Library Panel', - panelKey: 'panel-3', + body: new VizPanel({ + title: 'Library Panel 2', + key: 'panel-3', + $behaviors: [ + new LibraryPanelBehavior({ + uid: 'uid', + name: 'LibPanel', + title: 'Library Panel 2', + }), + ], }), }), ], diff --git a/public/app/features/dashboard-scene/utils/dashboardSceneGraph.ts b/public/app/features/dashboard-scene/utils/dashboardSceneGraph.ts index 5c85b33a284..7f6198fd605 100644 --- a/public/app/features/dashboard-scene/utils/dashboardSceneGraph.ts +++ b/public/app/features/dashboard-scene/utils/dashboardSceneGraph.ts @@ -3,10 +3,9 @@ import { VizPanel, SceneGridRow, sceneGraph, SceneGridLayout, behaviors } from ' import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardGridItem } from '../scene/DashboardGridItem'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { VizPanelLinks } from '../scene/PanelLinks'; -import { getPanelIdForLibraryVizPanel, getPanelIdForVizPanel } from './utils'; +import { getPanelIdForVizPanel } from './utils'; function getTimePicker(scene: DashboardScene) { return scene.state.controls?.state.timePicker; @@ -87,10 +86,7 @@ export function getNextPanelId(dashboard: DashboardScene): number { const vizPanel = child.state.body; if (vizPanel) { - const panelId = - vizPanel instanceof LibraryVizPanel - ? getPanelIdForLibraryVizPanel(vizPanel) - : getPanelIdForVizPanel(vizPanel); + const panelId = getPanelIdForVizPanel(vizPanel); if (panelId > max) { max = panelId; @@ -111,10 +107,7 @@ export function getNextPanelId(dashboard: DashboardScene): number { const vizPanel = rowChild.state.body; if (vizPanel) { - const panelId = - vizPanel instanceof LibraryVizPanel - ? getPanelIdForLibraryVizPanel(vizPanel) - : getPanelIdForVizPanel(vizPanel); + const panelId = getPanelIdForVizPanel(vizPanel); if (panelId > max) { max = panelId; @@ -128,19 +121,6 @@ export function getNextPanelId(dashboard: DashboardScene): number { return max + 1; } -// Returns the LibraryVizPanel that corresponds to the given VizPanel if it exists -export const getLibraryVizPanelFromVizPanel = (vizPanel: VizPanel): LibraryVizPanel | null => { - if (vizPanel.parent instanceof LibraryVizPanel) { - return vizPanel.parent; - } - - if (vizPanel.parent instanceof DashboardGridItem && vizPanel.parent.state.body instanceof LibraryVizPanel) { - return vizPanel.parent.state.body; - } - - return null; -}; - export const dashboardSceneGraph = { getTimePicker, getRefreshPicker, diff --git a/public/app/features/dashboard-scene/utils/test-utils.ts b/public/app/features/dashboard-scene/utils/test-utils.ts index 6bc2f591cfb..aa55c8a7eac 100644 --- a/public/app/features/dashboard-scene/utils/test-utils.ts +++ b/public/app/features/dashboard-scene/utils/test-utils.ts @@ -16,7 +16,6 @@ import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/co import { DashboardDTO } from 'app/types'; import { DashboardGridItem, RepeatDirection } from '../scene/DashboardGridItem'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; @@ -107,7 +106,7 @@ interface SceneOptions { variableRefresh?: VariableRefresh; } -export function buildPanelRepeaterScene(options: SceneOptions, source?: VizPanel | LibraryVizPanel) { +export function buildPanelRepeaterScene(options: SceneOptions, source?: VizPanel) { const defaults = { usePanelRepeater: true, ...options }; const withRepeat = new DashboardGridItem({ diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index 0da64d3f52a..004e152bc4c 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -1,6 +1,7 @@ import { getDataSourceRef, IntervalVariableModel } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; import { + CancelActivationHandler, CustomVariable, MultiValueVariable, SceneDataTransformer, @@ -15,10 +16,9 @@ import { initialIntervalVariableModelState } from 'app/features/variables/interv import { DashboardDatasourceBehaviour } from '../scene/DashboardDatasourceBehaviour'; import { DashboardScene } from '../scene/DashboardScene'; -import { LibraryVizPanel } from '../scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { panelMenuBehavior } from '../scene/PanelMenuBehavior'; -import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { RowActions } from '../scene/row-actions/RowActions'; import { dashboardSceneGraph } from './dashboardSceneGraph'; @@ -34,10 +34,6 @@ export function getPanelIdForVizPanel(panel: SceneObject): number { return parseInt(panel.state.key!.replace('panel-', ''), 10); } -export function getPanelIdForLibraryVizPanel(panel: LibraryVizPanel): number { - return parseInt(panel.state.panelKey!.replace('panel-', ''), 10); -} - /** * This will also try lookup based on panelId */ @@ -256,28 +252,41 @@ export function getDefaultRow(dashboard: DashboardScene): SceneGridRow { }); } -export function getLibraryPanel(vizPanel: VizPanel): LibraryVizPanel | undefined { - if (vizPanel.parent instanceof LibraryVizPanel) { - return vizPanel.parent; +export function isLibraryPanel(vizPanel: VizPanel): boolean { + return getLibraryPanelBehavior(vizPanel) !== undefined; +} + +export function getLibraryPanelBehavior(vizPanel: VizPanel): LibraryPanelBehavior | undefined { + const behavior = vizPanel.state.$behaviors?.find((behaviour) => behaviour instanceof LibraryPanelBehavior); + + if (behavior) { + return behavior; } - return; + + return undefined; } /** - * If the panel is within a repeated row, it must wait until the row resolves the variables. - * This ensures that the scoped variable for the row is assigned and the panel is initialized with them. + * Activates any inactive parents of the scene object. + * Useful when rendering a scene object out of context of it's parent + * @returns */ -export function isWithinUnactivatedRepeatRow(panel: VizPanel): boolean { - let row; +export function activateInActiveParents(so: SceneObject): CancelActivationHandler | undefined { + let cancel: CancelActivationHandler | undefined; + let parentCancel: CancelActivationHandler | undefined; - try { - row = sceneGraph.getAncestor(panel, SceneGridRow); - } catch (err) { - return false; + if (so.isActive) { + return cancel; } - const hasBehavior = !!(row.state.$behaviors && row.state.$behaviors.find((b) => b instanceof RowRepeaterBehavior)); - const hasVariables = !!(row.state.$variables && row.state.$variables.state.variables.length > 0); + if (so.parent) { + parentCancel = activateInActiveParents(so.parent); + } - return hasBehavior && !hasVariables; + cancel = so.activate(); + + return () => { + parentCancel?.(); + cancel(); + }; } diff --git a/public/app/features/dashboard/api/publicDashboardApi.ts b/public/app/features/dashboard/api/publicDashboardApi.ts index 5ff1aff23f2..3a94b0670c4 100644 --- a/public/app/features/dashboard/api/publicDashboardApi.ts +++ b/public/app/features/dashboard/api/publicDashboardApi.ts @@ -209,7 +209,7 @@ export const publicDashboardApi = createApi({ ) : t( 'public-dashboard.email-sharing.success-share-type-change', - 'Dashboard access restricted: Only specific people can now access with the link' + 'Dashboard access updated: Only specific people can now access with the link' ); } dispatch(notifyApp(createSuccessNotification(message))); diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index b0bb9796b16..86a3986be31 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -326,7 +326,6 @@ export const DashNav = memo((props) => { diff --git a/public/app/features/dashboard/components/PanelEditor/getVisualizationOptions.tsx b/public/app/features/dashboard/components/PanelEditor/getVisualizationOptions.tsx index eb5cdf588b9..0a607ee3ff3 100644 --- a/public/app/features/dashboard/components/PanelEditor/getVisualizationOptions.tsx +++ b/public/app/features/dashboard/components/PanelEditor/getVisualizationOptions.tsx @@ -17,7 +17,7 @@ import { import { VizPanel } from '@grafana/scenes'; import { Input } from '@grafana/ui'; import { LibraryVizPanelInfo } from 'app/features/dashboard-scene/panel-edit/LibraryVizPanelInfo'; -import { LibraryVizPanel } from 'app/features/dashboard-scene/scene/LibraryVizPanel'; +import { LibraryPanelBehavior } from 'app/features/dashboard-scene/scene/LibraryPanelBehavior'; import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv'; import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor'; @@ -150,7 +150,7 @@ export function getVisualizationOptions(props: OptionPaneRenderProps): OptionsPa return Object.values(categoryIndex); } -export function getLibraryVizPanelOptionsCategory(libraryPanel: LibraryVizPanel): OptionsPaneCategoryDescriptor { +export function getLibraryVizPanelOptionsCategory(libraryPanel: LibraryPanelBehavior): OptionsPaneCategoryDescriptor { const descriptor = new OptionsPaneCategoryDescriptor({ title: 'Library panel options', id: 'Library panel options', diff --git a/public/app/features/gops/configuration-tracker/components/ConfigureIRM.tsx b/public/app/features/gops/configuration-tracker/components/ConfigureIRM.tsx index 750100c3162..802e7a7091c 100644 --- a/public/app/features/gops/configuration-tracker/components/ConfigureIRM.tsx +++ b/public/app/features/gops/configuration-tracker/components/ConfigureIRM.tsx @@ -1,9 +1,10 @@ import { css } from '@emotion/css'; -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { GrafanaTheme2 } from '@grafana/data'; import { IconName, Text, useStyles2 } from '@grafana/ui'; +import { useURLSearchParams } from 'app/features/alerting/unified/hooks/useURLSearchParams'; import { getFirstCompatibleDataSource } from 'app/features/alerting/unified/utils/datasource'; import { DATASOURCES_ROUTES } from 'app/features/datasources/constants'; @@ -58,7 +59,9 @@ export function ConfigureIRM() { }); }, [dataSourceConfigurationData.dataSourceCompatibleWithAlerting]); - const [essentialsOpen, setEssentialsOpen] = useState(false); + // query param 'essentials' is used to open essentials drawer + const [queryParams, setQueryParams] = useURLSearchParams(); + const essentialsOpen = queryParams.get('essentials') === 'open'; const handleActionClick = (configID: number, isDone?: boolean) => { trackIrmConfigurationTrackerEvent(IRMInteractionNames.ClickDataSources, { @@ -75,7 +78,7 @@ export function ConfigureIRM() { } break; case ConfigurationStepsEnum.ESSENTIALS: - setEssentialsOpen(true); + setQueryParams({ essentials: 'open' }); trackIrmConfigurationTrackerEvent(IRMInteractionNames.OpenEssentials, { essentialStepsDone: essentialsConfigurationData.stepsDone, essentialStepsToDo: essentialsConfigurationData.totalStepsToDo, @@ -88,7 +91,7 @@ export function ConfigureIRM() { }; function onCloseEssentials() { - setEssentialsOpen(false); + setQueryParams({ essentials: undefined }); trackIrmConfigurationTrackerEvent(IRMInteractionNames.CloseEssentials, { essentialStepsDone: essentialsConfigurationData.stepsDone, essentialStepsToDo: essentialsConfigurationData.totalStepsToDo, diff --git a/public/app/features/library-panels/state/api.ts b/public/app/features/library-panels/state/api.ts index ce5faba1f2c..c92d295b3d6 100644 --- a/public/app/features/library-panels/state/api.ts +++ b/public/app/features/library-panels/state/api.ts @@ -1,10 +1,12 @@ import { lastValueFrom } from 'rxjs'; -import { defaultDashboard } from '@grafana/schema'; +import { VizPanel } from '@grafana/scenes'; +import { LibraryPanel, defaultDashboard } from '@grafana/schema'; import { DashboardModel } from 'app/features/dashboard/state'; +import { VizPanelManager } from 'app/features/dashboard-scene/panel-edit/VizPanelManager'; import { DashboardGridItem } from 'app/features/dashboard-scene/scene/DashboardGridItem'; -import { LibraryVizPanel } from 'app/features/dashboard-scene/scene/LibraryVizPanel'; import { vizPanelToPanel } from 'app/features/dashboard-scene/serialization/transformSceneToSaveModel'; +import { getLibraryPanelBehavior } from 'app/features/dashboard-scene/utils/utils'; import { getBackendSrv } from '../../../core/services/backend_srv'; import { DashboardSearchItem } from '../../search/types'; @@ -137,22 +139,30 @@ export async function getConnectedDashboards(uid: string): Promise { - const { uid, folderUID, name, model, version, kind } = libraryVizPanelToSaveModel(libraryPanel); +export async function updateLibraryVizPanel(vizPanel: VizPanel): Promise { + const { uid, folderUid, name, model, version, kind } = libraryVizPanelToSaveModel(vizPanel); const { result } = await getBackendSrv().patch(`/api/library-elements/${uid}`, { - folderUID, + folderUid, name, model, version, @@ -179,3 +190,15 @@ export async function updateLibraryVizPanel(libraryPanel: LibraryVizPanel): Prom }); return result; } + +export async function saveLibPanel(panel: VizPanel) { + const updatedLibPanel = await updateLibraryVizPanel(panel); + + const libPanelBehavior = getLibraryPanelBehavior(panel); + + if (!libPanelBehavior) { + throw new Error('Could not find library panel behavior for panel'); + } + + libPanelBehavior.setPanelFromLibPanel(updatedLibPanel); +} diff --git a/public/app/features/plugins/admin/components/UpdateAllModal.tsx b/public/app/features/plugins/admin/components/UpdateAllModal.tsx index 8ef7d3a3d6a..6c247fa6da6 100644 --- a/public/app/features/plugins/admin/components/UpdateAllModal.tsx +++ b/public/app/features/plugins/admin/components/UpdateAllModal.tsx @@ -218,14 +218,24 @@ export const UpdateAllModal = ({ isOpen, onDismiss, plugins }: Props) => { } }, [error, errorMap, inProgress, selectedPlugins]); - const onConfirm = () => { + const onConfirm = async () => { if (!inProgress) { setInProgress(true); - plugins.forEach((plugin) => { - if (selectedPlugins?.has(plugin.id)) { - install(plugin.id, plugin.latestVersion, true); + + // in cloud the requests need to be sync + if (config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall) { + for (let plugin of plugins) { + if (selectedPlugins?.has(plugin.id)) { + await install(plugin.id, plugin.latestVersion, true); + } } - }); + } else { + plugins.forEach((plugin) => { + if (selectedPlugins?.has(plugin.id)) { + install(plugin.id, plugin.latestVersion, true); + } + }); + } } }; diff --git a/public/app/features/plugins/admin/pages/Browse.tsx b/public/app/features/plugins/admin/pages/Browse.tsx index 7327730c1df..8a724d79784 100644 --- a/public/app/features/plugins/admin/pages/Browse.tsx +++ b/public/app/features/plugins/admin/pages/Browse.tsx @@ -3,7 +3,7 @@ import { ReactElement, useState } from 'react'; import { useLocation } from 'react-router-dom-v5-compat'; import { SelectableValue, GrafanaTheme2, PluginType } from '@grafana/data'; -import { config, locationSearchToObject } from '@grafana/runtime'; +import { locationSearchToObject } from '@grafana/runtime'; import { Select, RadioButtonGroup, useStyles2, Tooltip, Field, Button } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { Trans } from 'app/core/internationalization'; @@ -94,10 +94,8 @@ export default function Browse({ route }: GrafanaRouteComponentProps): ReactElem ); - const hideUpdateAllButton = config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall; - return ( - + diff --git a/public/app/features/search/page/components/SearchResultsTable.tsx b/public/app/features/search/page/components/SearchResultsTable.tsx index 471bdeb0108..20ae3c292ff 100644 --- a/public/app/features/search/page/components/SearchResultsTable.tsx +++ b/public/app/features/search/page/components/SearchResultsTable.tsx @@ -7,7 +7,6 @@ import InfiniteLoader from 'react-window-infinite-loader'; import { Observable } from 'rxjs'; import { Field, GrafanaTheme2 } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; import { TableCellHeight } from '@grafana/schema'; import { useStyles2, useTheme2 } from '@grafana/ui'; import { TableCell } from '@grafana/ui/src/components/Table/TableCell'; @@ -138,13 +137,9 @@ export const SearchResultsTable = React.memo( className += ' ' + styles.selectedRow; } const { key, ...rowProps } = row.getRowProps({ style }); + return ( -
                      +
                      {row.cells.map((cell: Cell, index: number) => { return ( +
                      {headerGroups.map((headerGroup) => { const { key, ...headerGroupProps } = headerGroup.getHeaderGroupProps({ style: { width }, diff --git a/public/app/features/trails/DataTrail.tsx b/public/app/features/trails/DataTrail.tsx index 54194dcea59..54c08d11f2f 100644 --- a/public/app/features/trails/DataTrail.tsx +++ b/public/app/features/trails/DataTrail.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/css'; -import { AdHocVariableFilter, GrafanaTheme2, PageLayoutType, VariableHide, urlUtil } from '@grafana/data'; +import { AdHocVariableFilter, GrafanaTheme2, urlUtil, VariableHide } from '@grafana/data'; import { config, locationService, useChromeHeaderHeight } from '@grafana/runtime'; import { AdHocFiltersVariable, @@ -24,7 +24,6 @@ import { VariableValueSelectors, } from '@grafana/scenes'; import { useStyles2 } from '@grafana/ui'; -import { Page } from 'app/core/components/Page/Page'; import { DataTrailSettings } from './DataTrailSettings'; import { DataTrailHistory } from './DataTrailsHistory'; @@ -35,7 +34,6 @@ import { getTrailStore } from './TrailStore/TrailStore'; import { MetricDatasourceHelper } from './helpers/MetricDatasourceHelper'; import { reportChangeInLabelFilters } from './interactions'; import { MetricSelectedEvent, trailDS, VAR_DATASOURCE, VAR_FILTERS } from './shared'; -import { getMetricName } from './utils'; export interface DataTrailState extends SceneObjectState { topScene?: SceneObject; @@ -211,27 +209,25 @@ export class DataTrail extends SceneObjectBase { } static Component = ({ model }: SceneComponentProps) => { - const { controls, topScene, history, settings, metric } = model.useState(); + const { controls, topScene, history, settings } = model.useState(); const chromeHeaderHeight = useChromeHeaderHeight(); const styles = useStyles2(getStyles, chromeHeaderHeight ?? 0); const showHeaderForFirstTimeUsers = getTrailStore().recent.length < 2; return ( - -
                      - {showHeaderForFirstTimeUsers && } - - {controls && ( -
                      - {controls.map((control) => ( - - ))} - -
                      - )} -
                      {topScene && }
                      -
                      -
                      +
                      + {showHeaderForFirstTimeUsers && } + + {controls && ( +
                      + {controls.map((control) => ( + + ))} + +
                      + )} +
                      {topScene && }
                      +
                      ); }; } diff --git a/public/app/features/trails/DataTrailsApp.tsx b/public/app/features/trails/DataTrailsApp.tsx index 37fc404f321..a3b7c8ec46a 100644 --- a/public/app/features/trails/DataTrailsApp.tsx +++ b/public/app/features/trails/DataTrailsApp.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { Route, Switch } from 'react-router-dom'; import { PageLayoutType } from '@grafana/data'; @@ -11,7 +11,7 @@ import { DataTrailsHome } from './DataTrailsHome'; import { MetricsHeader } from './MetricsHeader'; import { getTrailStore } from './TrailStore/TrailStore'; import { HOME_ROUTE, TRAILS_ROUTE } from './shared'; -import { getUrlForTrail, newMetricsTrail } from './utils'; +import { getMetricName, getUrlForTrail, newMetricsTrail } from './utils'; export interface DataTrailsAppState extends SceneObjectState { trail: DataTrail; @@ -55,6 +55,7 @@ export class DataTrailsApp extends SceneObjectBase { function DataTrailView({ trail }: { trail: DataTrail }) { const [isInitialized, setIsInitialized] = useState(false); + const { metric } = trail.useState(); useEffect(() => { if (!isInitialized) { @@ -69,7 +70,9 @@ function DataTrailView({ trail }: { trail: DataTrail }) { return ( - + + + ); } diff --git a/public/app/features/trails/Integrations/DataTrailEmbedded.tsx b/public/app/features/trails/Integrations/DataTrailEmbedded.tsx index 52f2dd39775..fa6ee7c7a5e 100644 --- a/public/app/features/trails/Integrations/DataTrailEmbedded.tsx +++ b/public/app/features/trails/Integrations/DataTrailEmbedded.tsx @@ -1,14 +1,21 @@ import { AdHocVariableFilter } from '@grafana/data'; -import { SceneComponentProps, SceneObjectBase, SceneObjectState, SceneTimeRangeLike } from '@grafana/scenes'; +import { + SceneComponentProps, + SceneObjectBase, + SceneObjectState, + SceneTimeRange, + SceneTimeRangeState, +} from '@grafana/scenes'; import { DataTrail } from '../DataTrail'; export interface DataTrailEmbeddedState extends SceneObjectState { - timeRange: SceneTimeRangeLike; + timeRangeState: SceneTimeRangeState; metric?: string; filters?: AdHocVariableFilter[]; dataSourceUid?: string; } + export class DataTrailEmbedded extends SceneObjectBase { static Component = DataTrailEmbeddedRenderer; @@ -24,9 +31,9 @@ function DataTrailEmbeddedRenderer({ model }: SceneComponentProps; } -function buildDataTrailFromState({ metric, filters, dataSourceUid, timeRange }: DataTrailEmbeddedState) { +function buildDataTrailFromState({ metric, filters, dataSourceUid, timeRangeState }: DataTrailEmbeddedState) { return new DataTrail({ - $timeRange: timeRange, + $timeRange: new SceneTimeRange(timeRangeState), metric, initialDS: dataSourceUid, initialFilters: filters, diff --git a/public/app/features/trails/Integrations/dashboardIntegration.ts b/public/app/features/trails/Integrations/dashboardIntegration.ts index 12fa49cc771..ea2a34ea91f 100644 --- a/public/app/features/trails/Integrations/dashboardIntegration.ts +++ b/public/app/features/trails/Integrations/dashboardIntegration.ts @@ -1,7 +1,7 @@ import { PanelMenuItem } from '@grafana/data'; import { PromQuery } from '@grafana/prometheus'; import { getDataSourceSrv } from '@grafana/runtime'; -import { SceneTimeRangeLike, VizPanel } from '@grafana/scenes'; +import { SceneTimeRangeState, VizPanel } from '@grafana/scenes'; import { DataQuery, DataSourceRef } from '@grafana/schema'; import { getQueryRunnerFor } from 'app/features/dashboard-scene/utils/utils'; @@ -11,8 +11,8 @@ import { reportExploreMetrics } from '../interactions'; import { DataTrailEmbedded, DataTrailEmbeddedState } from './DataTrailEmbedded'; import { SceneDrawerAsScene } from './SceneDrawer'; -import { QueryMetric, getQueryMetrics } from './getQueryMetrics'; -import { createAdHocFilters, getQueryMetricLabel, getTimeRangeFromDashboard } from './utils'; +import { getQueryMetrics, QueryMetric } from './getQueryMetrics'; +import { createAdHocFilters, getQueryMetricLabel, getTimeRangeStateFromDashboard } from './utils'; export async function addDataTrailPanelAction(dashboard: DashboardScene, panel: VizPanel, items: PanelMenuItem[]) { if (panel.state.pluginId !== 'timeseries') { @@ -64,33 +64,35 @@ export async function addDataTrailPanelAction(dashboard: DashboardScene, panel: function getUnique(items: T[]) { const uniqueMenuTexts = new Set(); + function isUnique({ text }: { text: string }) { const before = uniqueMenuTexts.size; uniqueMenuTexts.add(text); const after = uniqueMenuTexts.size; return after > before; } + return items.filter(isUnique); } function getEmbeddedTrailsState( { metric, labelFilters, query }: QueryMetric, - timeRange: SceneTimeRangeLike, + timeRangeState: SceneTimeRangeState, dataSourceUid: string | undefined ) { const state: DataTrailEmbeddedState = { metric, filters: createAdHocFilters(labelFilters), dataSourceUid, - timeRange, + timeRangeState, }; return state; } function createCommonEmbeddedTrailStateProps(item: QueryMetric, dashboard: DashboardScene, ds: DataSourceRef) { - const timeRange = getTimeRangeFromDashboard(dashboard); - const trailState = getEmbeddedTrailsState(item, timeRange, ds.uid); + const timeRangeState = getTimeRangeStateFromDashboard(dashboard); + const trailState = getEmbeddedTrailsState(item, timeRangeState, ds.uid); const embeddedTrail: DataTrailEmbedded = new DataTrailEmbedded(trailState); embeddedTrail.trail.addActivationHandler(() => { diff --git a/public/app/features/trails/Integrations/utils.ts b/public/app/features/trails/Integrations/utils.ts index 372d81ae999..9cb4b51e592 100644 --- a/public/app/features/trails/Integrations/utils.ts +++ b/public/app/features/trails/Integrations/utils.ts @@ -1,15 +1,15 @@ import { QueryBuilderLabelFilter } from '@grafana/prometheus/src/querybuilder/shared/types'; import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene'; -import { QueryMetric } from './getQueryMetrics'; +import { QueryMetric } from './getQueryMetrics'; // We only support label filters with the '=' operator // We only support label filters with the '=' operator export function isEquals(labelFilter: QueryBuilderLabelFilter) { return labelFilter.op === '='; } -export function getTimeRangeFromDashboard(dashboard: DashboardScene) { - return dashboard.state.$timeRange!.clone(); +export function getTimeRangeStateFromDashboard(dashboard: DashboardScene) { + return dashboard.state.$timeRange!.state; } export function getQueryMetricLabel({ metric, labelFilters }: QueryMetric) { diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index d85c944aef9..43a09ce53f1 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -286,18 +286,13 @@ export class TempoDatasource extends DataSourceWithBackend + + diff --git a/public/img/icons/unicons/legend-show.svg b/public/img/icons/unicons/legend-show.svg new file mode 100644 index 00000000000..b56c4ed3b9e --- /dev/null +++ b/public/img/icons/unicons/legend-show.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index b4f006446c3..99631238344 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -912,7 +912,7 @@ "download-successful_toast_message": "Your JSON has been downloaded", "export-externally-label": "Export the dashboard to use in another instance", "info-text": "Copy or download a JSON file containing the JSON of your dashboard", - "title": "Save dashboard JSON" + "title": "Export dashboard JSON" }, "menu": { "export-as-json-label": "Export", @@ -1045,7 +1045,30 @@ "use-ssl-label": "Use SSL", "use-ssl-tooltip": "For a complete list of supported ciphers and TLS versions, refer to: {\n \n https://go.dev/src/crypto/tls/cipher_suites.go\n }" }, + "group-mapping": { + "grafana-admin": { + "description": "If enabled, all users from this group will be Grafana Admins", + "label": "Grafana Admin" + }, + "group-dn": { + "description": "The name of the key used to extract the ID token from the returned OAuth2 token.", + "label": "Group DN" + }, + "org-id": { + "description": "The Grafana organization database id. Default org (ID 1) will be used if left out", + "label": "Org ID" + }, + "org-role": { + "label": "Org role *" + }, + "remove": { + "button": "Remove group mapping" + } + }, "group-mapping-section": { + "add": { + "button": "Add group mapping" + }, "description": "Map LDAP groups to Grafana org roles", "group-search-base-dns-description": "Separate by commas or spaces", "group-search-base-dns-label": "Group search base DNS", @@ -1110,6 +1133,10 @@ "label": "Server host", "placeholder": "example: 127.0.0.1" }, + "login-form-alert": { + "description": "Your LDAP configuration is not working because the basic login form is currently disabled. Please enable the login form to use LDAP authentication. You can enable it on the Authentication page under “Auth settings”.", + "title": "Basic login disabled" + }, "search_filter": { "description": "LDAP search filter used to locate specific entries within the directory.", "label": "Search filter*", @@ -1144,6 +1171,7 @@ "empty-state": { "message": "No library panels found" }, + "loading-panel-text": "Loading library panel", "modal": { "body_one": "This panel is being used in {{count}} dashboard. Please choose which dashboard to view the panel in:", "body_other": "This panel is being used in {{count}} dashboard. Please choose which dashboard to view the panel in:", @@ -1163,7 +1191,7 @@ "link": { "share": { "config-alert-description": "Updating your settings will modify the default copy link to include these changes. Please note that these settings are saved within your current browser scope.", - "config-alert-title": "Link configuration", + "config-alert-title": "Link settings", "config-description": "Create a personalized, direct link to share your dashboard within your organization, with the following customization settings:", "copy-link-button": "Copy link", "copy-to-clipboard": "Link copied to clipboard", @@ -1906,7 +1934,7 @@ "settings-title": "Settings" }, "configuration": { - "display-annotations-description": "Present annotations on this Dashboard", + "display-annotations-description": "Present annotations on this dashboard", "display-annotations-label": "Display annotations", "enable-time-range-description": "Allow people to change time range", "enable-time-range-label": "Enable time range", @@ -1952,7 +1980,7 @@ "revoke-button": "Revoke", "revoke-button-title": "Revoke", "success-creation": "Your dashboard is ready for external sharing", - "success-share-type-change": "Dashboard access restricted: Only specific people can now access with the link" + "success-share-type-change": "Dashboard access updated: Only specific people can now access with the link" }, "modal-alerts": { "no-upsert-perm-alert-desc": "Contact your admin to get permission to {{mode}} public dashboards", diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index e13ef14d739..436de096e2c 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -912,7 +912,7 @@ "download-successful_toast_message": "Ÿőūř ĴŜØŃ ĥäş þęęʼn đőŵʼnľőäđęđ", "export-externally-label": "Ēχpőřŧ ŧĥę đäşĥþőäřđ ŧő ūşę įʼn äʼnőŧĥęř įʼnşŧäʼnčę", "info-text": "Cőpy őř đőŵʼnľőäđ ä ĴŜØŃ ƒįľę čőʼnŧäįʼnįʼnģ ŧĥę ĴŜØŃ őƒ yőūř đäşĥþőäřđ", - "title": "Ŝävę đäşĥþőäřđ ĴŜØŃ" + "title": "Ēχpőřŧ đäşĥþőäřđ ĴŜØŃ" }, "menu": { "export-as-json-label": "Ēχpőřŧ", @@ -1045,7 +1045,30 @@ "use-ssl-label": "Ůşę ŜŜĿ", "use-ssl-tooltip": "Főř ä čőmpľęŧę ľįşŧ őƒ şūppőřŧęđ čįpĥęřş äʼnđ ŦĿŜ vęřşįőʼnş, řęƒęř ŧő: {\n <ŦęχŧĿįʼnĸ şŧyľę={{ fontSize: 'inherit' }} ĥřęƒ=\"ĥŧŧpş://ģő.đęv/şřč/čřypŧő/ŧľş/čįpĥęř_şūįŧęş.ģő\" ęχŧęřʼnäľ>\n ĥŧŧpş://ģő.đęv/şřč/čřypŧő/ŧľş/čįpĥęř_şūįŧęş.ģő\n }" }, + "group-mapping": { + "grafana-admin": { + "description": "Ĩƒ ęʼnäþľęđ, äľľ ūşęřş ƒřőm ŧĥįş ģřőūp ŵįľľ þę Ğřäƒäʼnä Åđmįʼnş", + "label": "Ğřäƒäʼnä Åđmįʼn" + }, + "group-dn": { + "description": "Ŧĥę ʼnämę őƒ ŧĥę ĸęy ūşęđ ŧő ęχŧřäčŧ ŧĥę ĨĐ ŧőĸęʼn ƒřőm ŧĥę řęŧūřʼnęđ ØÅūŧĥ2 ŧőĸęʼn.", + "label": "Ğřőūp ĐŃ" + }, + "org-id": { + "description": "Ŧĥę Ğřäƒäʼnä őřģäʼnįžäŧįőʼn đäŧäþäşę įđ. Đęƒäūľŧ őřģ (ĨĐ 1) ŵįľľ þę ūşęđ įƒ ľęƒŧ őūŧ", + "label": "Øřģ ĨĐ" + }, + "org-role": { + "label": "Øřģ řőľę *" + }, + "remove": { + "button": "Ŗęmővę ģřőūp mäppįʼnģ" + } + }, "group-mapping-section": { + "add": { + "button": "Åđđ ģřőūp mäppįʼnģ" + }, "description": "Mäp ĿĐÅP ģřőūpş ŧő Ğřäƒäʼnä őřģ řőľęş", "group-search-base-dns-description": "Ŝępäřäŧę þy čőmmäş őř şpäčęş", "group-search-base-dns-label": "Ğřőūp şęäřčĥ þäşę ĐŃŜ", @@ -1110,6 +1133,10 @@ "label": "Ŝęřvęř ĥőşŧ", "placeholder": "ęχämpľę: 127.0.0.1" }, + "login-form-alert": { + "description": "Ÿőūř ĿĐÅP čőʼnƒįģūřäŧįőʼn įş ʼnőŧ ŵőřĸįʼnģ þęčäūşę ŧĥę þäşįč ľőģįʼn ƒőřm įş čūřřęʼnŧľy đįşäþľęđ. Pľęäşę ęʼnäþľę ŧĥę ľőģįʼn ƒőřm ŧő ūşę ĿĐÅP äūŧĥęʼnŧįčäŧįőʼn. Ÿőū čäʼn ęʼnäþľę įŧ őʼn ŧĥę Åūŧĥęʼnŧįčäŧįőʼn päģę ūʼnđęř “Åūŧĥ şęŧŧįʼnģş”.", + "title": "ßäşįč ľőģįʼn đįşäþľęđ" + }, "search_filter": { "description": "ĿĐÅP şęäřčĥ ƒįľŧęř ūşęđ ŧő ľőčäŧę şpęčįƒįč ęʼnŧřįęş ŵįŧĥįʼn ŧĥę đįřęčŧőřy.", "label": "Ŝęäřčĥ ƒįľŧęř*", @@ -1144,6 +1171,7 @@ "empty-state": { "message": "Ńő ľįþřäřy päʼnęľş ƒőūʼnđ" }, + "loading-panel-text": "Ŀőäđįʼnģ ľįþřäřy päʼnęľ", "modal": { "body_one": "Ŧĥįş päʼnęľ įş þęįʼnģ ūşęđ įʼn {{count}} đäşĥþőäřđ. Pľęäşę čĥőőşę ŵĥįčĥ đäşĥþőäřđ ŧő vįęŵ ŧĥę päʼnęľ įʼn:", "body_other": "Ŧĥįş päʼnęľ įş þęįʼnģ ūşęđ įʼn {{count}} đäşĥþőäřđ. Pľęäşę čĥőőşę ŵĥįčĥ đäşĥþőäřđ ŧő vįęŵ ŧĥę päʼnęľ įʼn:", @@ -1163,7 +1191,7 @@ "link": { "share": { "config-alert-description": "Ůpđäŧįʼnģ yőūř şęŧŧįʼnģş ŵįľľ mőđįƒy ŧĥę đęƒäūľŧ čőpy ľįʼnĸ ŧő įʼnčľūđę ŧĥęşę čĥäʼnģęş. Pľęäşę ʼnőŧę ŧĥäŧ ŧĥęşę şęŧŧįʼnģş äřę şävęđ ŵįŧĥįʼn yőūř čūřřęʼnŧ þřőŵşęř şčőpę.", - "config-alert-title": "Ŀįʼnĸ čőʼnƒįģūřäŧįőʼn", + "config-alert-title": "Ŀįʼnĸ şęŧŧįʼnģş", "config-description": "Cřęäŧę ä pęřşőʼnäľįžęđ, đįřęčŧ ľįʼnĸ ŧő şĥäřę yőūř đäşĥþőäřđ ŵįŧĥįʼn yőūř őřģäʼnįžäŧįőʼn, ŵįŧĥ ŧĥę ƒőľľőŵįʼnģ čūşŧőmįžäŧįőʼn şęŧŧįʼnģş:", "copy-link-button": "Cőpy ľįʼnĸ", "copy-to-clipboard": "Ŀįʼnĸ čőpįęđ ŧő čľįpþőäřđ", @@ -1906,7 +1934,7 @@ "settings-title": "Ŝęŧŧįʼnģş" }, "configuration": { - "display-annotations-description": "Přęşęʼnŧ äʼnʼnőŧäŧįőʼnş őʼn ŧĥįş Đäşĥþőäřđ", + "display-annotations-description": "Přęşęʼnŧ äʼnʼnőŧäŧįőʼnş őʼn ŧĥįş đäşĥþőäřđ", "display-annotations-label": "Đįşpľäy äʼnʼnőŧäŧįőʼnş", "enable-time-range-description": "Åľľőŵ pęőpľę ŧő čĥäʼnģę ŧįmę řäʼnģę", "enable-time-range-label": "Ēʼnäþľę ŧįmę řäʼnģę", @@ -1952,7 +1980,7 @@ "revoke-button": "Ŗęvőĸę", "revoke-button-title": "Ŗęvőĸę", "success-creation": "Ÿőūř đäşĥþőäřđ įş řęäđy ƒőř ęχŧęřʼnäľ şĥäřįʼnģ", - "success-share-type-change": "Đäşĥþőäřđ äččęşş řęşŧřįčŧęđ: Øʼnľy şpęčįƒįč pęőpľę čäʼn ʼnőŵ äččęşş ŵįŧĥ ŧĥę ľįʼnĸ" + "success-share-type-change": "Đäşĥþőäřđ äččęşş ūpđäŧęđ: Øʼnľy şpęčįƒįč pęőpľę čäʼn ʼnőŵ äččęşş ŵįŧĥ ŧĥę ľįʼnĸ" }, "modal-alerts": { "no-upsert-perm-alert-desc": "Cőʼnŧäčŧ yőūř äđmįʼn ŧő ģęŧ pęřmįşşįőʼn ŧő {{mode}} pūþľįč đäşĥþőäřđş", diff --git a/public/openapi3.json b/public/openapi3.json index 309ce67bff5..6ef714a02a4 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -1372,6 +1372,16 @@ }, "description": "(empty)" }, + "goneError": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponseBody" + } + } + }, + "description": "GoneError is returned when the requested endpoint was removed." + }, "helpFlagResponse": { "content": { "application/json": { @@ -1628,16 +1638,6 @@ }, "description": "An OKResponse is returned if the request was successful." }, - "postAPIkeyResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NewApiKeyResult" - } - } - }, - "description": "(empty)" - }, "postAnnotationResponse": { "content": { "application/json": { @@ -15392,8 +15392,8 @@ "description": "Will return details of the created API key.", "operationId": "addAPIkey", "responses": { - "301": { - "$ref": "#/components/responses/statusMovedPermanently" + "410": { + "$ref": "#/components/responses/goneError" } }, "summary": "Creates an API key.", diff --git a/public/sass/_angular.scss b/public/sass/_angular.scss index 33e6364f4c9..31117d57430 100644 --- a/public/sass/_angular.scss +++ b/public/sass/_angular.scss @@ -4,6 +4,7 @@ @use 'sass:map'; @use 'sass:color'; @use 'sass:list'; +@use 'sass:math'; // Media of at least the minimum breakpoint width. No query for the smallest breakpoint. // Makes the @content apply to the given breakpoint and wider. @@ -29,6 +30,24 @@ font-family: $font-family-monospace; } +@mixin make-col($size, $columns: $grid-columns) { + position: relative; + min-height: 1px; + padding-right: calc($grid-gutter-width / 2); + padding-left: calc($grid-gutter-width / 2); + + @if $enable-flex { + flex: 0 0 math.percentage(calc($size / $columns)); + // Add a `max-width` to ensure content within each column does not blow out + // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari + // do not appear to require this. + max-width: math.percentage(calc($size / $columns)); + } @else { + float: left; + width: math.percentage(calc($size / $columns)); + } +} + .edit-tab-content { flex-grow: 1; min-width: 0; @@ -2606,3 +2625,13 @@ input[type='checkbox'].cr1:checked + label { .max-width { width: 100%; } + +@each $breakpoint in map-keys($grid-breakpoints) { + @include media-breakpoint-up($breakpoint, $grid-breakpoints) { + @for $i from 1 through $grid-columns { + .col-#{$breakpoint}-#{$i} { + @include make-col($i, $grid-columns); + } + } + } +} diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index ec3c07791dc..480b73ee71e 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -18,7 +18,6 @@ body { } // BASE -@import 'base/grid'; @import 'base/font_awesome'; // ANGULAR diff --git a/public/sass/base/_grid.scss b/public/sass/base/_grid.scss deleted file mode 100644 index 75deba50990..00000000000 --- a/public/sass/base/_grid.scss +++ /dev/null @@ -1,174 +0,0 @@ -@use 'sass:math'; -@use 'sass:map'; - -// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. -// Makes the @content apply to the given breakpoint and wider. -@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { - $min: if(map.get($breakpoints, $name) != 0, map.get($breakpoints, $name), null); - @if $min { - @media (min-width: $min) { - @content; - } - } @else { - @content; - } -} - -/// Grid system -// -// Generate semantic grid columns with these mixins. - -@mixin make-container($gutter: $grid-gutter-width) { - margin-left: auto; - margin-right: auto; - padding-left: calc($gutter / 2); - padding-right: calc($gutter / 2); - @if not $enable-flex { - &::after { - content: ''; - display: table; - clear: both; - } - } -} - -// For each breakpoint, define the maximum width of the container in a media query -@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) { - @each $breakpoint, $container-max-width in $max-widths { - @include media-breakpoint-up($breakpoint, $breakpoints) { - max-width: $container-max-width; - } - } -} - -@mixin make-row($gutter: $grid-gutter-width) { - @if $enable-flex { - display: flex; - flex-wrap: wrap; - } @else { - &::after { - content: ''; - display: table; - clear: both; - } - } - margin-left: calc($gutter / -2); - margin-right: calc($gutter / -2); -} - -@mixin make-col($size, $columns: $grid-columns) { - position: relative; - min-height: 1px; - padding-right: calc($grid-gutter-width / 2); - padding-left: calc($grid-gutter-width / 2); - - @if $enable-flex { - flex: 0 0 math.percentage(calc($size / $columns)); - // Add a `max-width` to ensure content within each column does not blow out - // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari - // do not appear to require this. - max-width: math.percentage(calc($size / $columns)); - } @else { - float: left; - width: math.percentage(calc($size / $columns)); - } -} - -@mixin make-col-offset($size, $columns: $grid-columns) { - margin-left: math.percentage(calc($size / $columns)); -} - -@mixin make-col-push($size, $columns: $grid-columns) { - left: if($size > 0, math.percentage(calc($size / $columns)), auto); -} - -@mixin make-col-pull($size, $columns: $grid-columns) { - right: if($size > 0, math.percentage(calc($size / $columns)), auto); -} - -@mixin make-col-modifier($type, $size, $columns) { - // Work around the lack of dynamic mixin @include support (https://github.com/sass/sass/issues/626) - @if $type == push { - @include make-col-push($size, $columns); - } @else if $type == pull { - @include make-col-pull($size, $columns); - } @else if $type == offset { - @include make-col-offset($size, $columns); - } -} - -@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) { - $breakpoint-counter: 0; - @each $breakpoint in map-keys($breakpoints) { - $breakpoint-counter: ($breakpoint-counter + 1); - @include media-breakpoint-up($breakpoint, $breakpoints) { - @if $enable-flex { - .col-#{$breakpoint} { - position: relative; - flex-basis: 0; - flex-grow: 1; - max-width: 100%; - min-height: 1px; - padding-right: calc($grid-gutter-width / 2); - padding-left: calc($grid-gutter-width / 2); - } - } - - @for $i from 1 through $columns { - .col-#{$breakpoint}-#{$i} { - @include make-col($i, $columns); - } - } - - @each $modifier in (pull, push) { - @for $i from 0 through $columns { - .#{$modifier}-#{$breakpoint}-#{$i} { - @include make-col-modifier($modifier, $i, $columns); - } - } - } - - // `$columns - 1` because offsetting by the width of an entire row isn't possible - @for $i from 0 through ($columns - 1) { - @if $breakpoint-counter != 1 or $i != 0 { - // Avoid emitting useless .col-xs-offset-0 - .offset-#{$breakpoint}-#{$i} { - @include make-col-modifier(offset, $i, $columns); - } - } - } - } - } -} - -// Container widths -// -// Set the container width, and override it for fixed navbars in media queries. - -.container { - @include make-container(); - @include make-container-max-widths(); -} - -// Fluid container -// -// Utilizes the mixin meant for fixed width containers, but without any defined -// width for fluid, full width layouts. - -.container-fluid { - @include make-container(); -} - -// Row -// -// Rows contain and clear the floats of your columns. - -.row { - @include make-row(); -} - -// Columns -// -// Common styles for small and large grid columns - -@include make-grid-columns(); diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index 80756682c3b..ccdc41ac03e 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -1266,7 +1266,8 @@ def publish_linux_packages_step(package_manager = "deb"): }, } -def retry_command(command, attempts = 5, delay = 60): +# This retry will currently continue for 30 minutes until fail, unless successful. +def retry_command(command, attempts = 60, delay = 30): return [ "for i in $(seq 1 %d); do" % attempts, " if %s; then" % command, diff --git a/scripts/drone/variables.star b/scripts/drone/variables.star index 6dcc72a7a1b..9cc69bb2829 100644 --- a/scripts/drone/variables.star +++ b/scripts/drone/variables.star @@ -3,7 +3,7 @@ global variables """ grabpl_version = "v3.0.50" -golang_version = "1.23.0" +golang_version = "1.23.1" # nodejs_version should match what's in ".nvmrc", but without the v prefix. nodejs_version = "20.9.0" diff --git a/scripts/go-workspace/go.mod b/scripts/go-workspace/go.mod index 1003029a81f..3dd4675a42d 100644 --- a/scripts/go-workspace/go.mod +++ b/scripts/go-workspace/go.mod @@ -1,5 +1,5 @@ module github.com/grafana/grafana/scripts/go-workspace -go 1.23.0 +go 1.23.1 require golang.org/x/mod v0.20.0 diff --git a/scripts/grafana-server/custom.ini b/scripts/grafana-server/custom.ini index d1751079a18..5d09d40ac63 100644 --- a/scripts/grafana-server/custom.ini +++ b/scripts/grafana-server/custom.ini @@ -4,7 +4,7 @@ content_security_policy = true content_security_policy_template = """require-trusted-types-for 'script'; script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';""" [feature_toggles] -enable = publicDashboards, dashboardRestore, dashboardRestoreUI +enable = publicDashboards [plugins] allow_loading_unsigned_plugins=grafana-extensionstest-app,grafana-extensionexample1-app,grafana-extensionexample2-app, diff --git a/yarn.lock b/yarn.lock index bbe010e1dfd..1d2bb386279 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3452,13 +3452,13 @@ __metadata: linkType: soft "@grafana/e2e-selectors@npm:^11.0.0": - version: 11.1.0 - resolution: "@grafana/e2e-selectors@npm:11.1.0" + version: 11.2.0 + resolution: "@grafana/e2e-selectors@npm:11.2.0" dependencies: "@grafana/tsconfig": "npm:^1.3.0-rc1" tslib: "npm:2.6.3" typescript: "npm:5.4.5" - checksum: 10/010a32e8b562d0da83b008646b9928a96a79957096eed713aa67b227d8ad6055d22cc0ec26f87fd9839cfb28344d0012f49c3c823defc6e91f4ab05ed7d8c465 + checksum: 10/aec06529dfedcd2611b4a0c25b256fdd860979105a7734309d8a06655c18a5915e7132ed99d110e4742adf904dc483c7bfb20fbda0b5668773a77f607351e49f languageName: node linkType: hard @@ -3932,9 +3932,9 @@ __metadata: languageName: unknown linkType: soft -"@grafana/scenes@npm:^5.13.0": - version: 5.13.0 - resolution: "@grafana/scenes@npm:5.13.0" +"@grafana/scenes@npm:5.14.1": + version: 5.14.1 + resolution: "@grafana/scenes@npm:5.14.1" dependencies: "@floating-ui/react": "npm:0.26.16" "@grafana/e2e-selectors": "npm:^11.0.0" @@ -3951,7 +3951,7 @@ __metadata: "@grafana/ui": ">=10.4" react: ^18.0.0 react-dom: ^18.0.0 - checksum: 10/d22cbbc146b26e4d3e9b1f2b873d44e771e7c87b4a211cc61701cbba5460eb0a5f49ba982c4dfff7c86729487712e94340d6cc5b0d6a19483549205dfea1ebe6 + checksum: 10/0f8bac7829eb87ab1e46eb94ebf73b730468c9a1f946667c567fe40a8d8741af9956a27fa63a82ee79e5dafa72f748bf1332013c439169ea5291a4394cd1abcf languageName: node linkType: hard @@ -4130,7 +4130,7 @@ __metadata: ol: "npm:7.4.0" prismjs: "npm:1.29.0" process: "npm:^0.11.10" - rc-cascader: "npm:3.27.0" + rc-cascader: "npm:3.28.1" rc-drawer: "npm:7.2.0" rc-slider: "npm:11.1.5" rc-time-picker: "npm:^3.7.3" @@ -8845,7 +8845,7 @@ __metadata: languageName: node linkType: hard -"@tanstack/react-virtual@npm:^3.5.1, @tanstack/react-virtual@npm:^3.9.0": +"@tanstack/react-virtual@npm:^3.5.1": version: 3.10.6 resolution: "@tanstack/react-virtual@npm:3.10.6" dependencies: @@ -8857,6 +8857,18 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-virtual@npm:^3.9.0": + version: 3.10.7 + resolution: "@tanstack/react-virtual@npm:3.10.7" + dependencies: + "@tanstack/virtual-core": "npm:3.10.7" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10/cf4324687a3849a981f6e7baad26199fc9419184d69585d9aca35876f9a83a0d9167b31d8d5e74ad739465961eaedc7b5c9177efc62c502adbb7f061be8dc554 + languageName: node + linkType: hard + "@tanstack/virtual-core@npm:3.10.6": version: 3.10.6 resolution: "@tanstack/virtual-core@npm:3.10.6" @@ -8864,6 +8876,13 @@ __metadata: languageName: node linkType: hard +"@tanstack/virtual-core@npm:3.10.7": + version: 3.10.7 + resolution: "@tanstack/virtual-core@npm:3.10.7" + checksum: 10/28aa29e454f2674bd2e3609d2a9ace4ad9913f55b2edd71363b770cde150ffafd67d5fd846cc7d1538b5b0e4e62a1960f253c48c85ad9774692b3be14579eaad + languageName: node + linkType: hard + "@test-plugins/extensions-test-app@workspace:e2e/test-plugins/grafana-extensionstest-app": version: 0.0.0-use.local resolution: "@test-plugins/extensions-test-app@workspace:e2e/test-plugins/grafana-extensionstest-app" @@ -18534,7 +18553,7 @@ __metadata: "@grafana/prometheus": "workspace:*" "@grafana/runtime": "workspace:*" "@grafana/saga-icons": "workspace:*" - "@grafana/scenes": "npm:^5.13.0" + "@grafana/scenes": "npm:5.14.1" "@grafana/schema": "workspace:*" "@grafana/sql": "workspace:*" "@grafana/tsconfig": "npm:^2.0.0" @@ -26585,20 +26604,20 @@ __metadata: languageName: node linkType: hard -"rc-cascader@npm:3.27.0": - version: 3.27.0 - resolution: "rc-cascader@npm:3.27.0" +"rc-cascader@npm:3.28.1": + version: 3.28.1 + resolution: "rc-cascader@npm:3.28.1" dependencies: "@babel/runtime": "npm:^7.12.5" array-tree-filter: "npm:^2.1.0" classnames: "npm:^2.3.1" rc-select: "npm:~14.15.0" - rc-tree: "npm:~5.8.1" + rc-tree: "npm:~5.9.0" rc-util: "npm:^5.37.0" peerDependencies: react: ">=16.9.0" react-dom: ">=16.9.0" - checksum: 10/ed0fa47131047097d08df341250bf0e3a5c01704d2a8a44ef7385aacc29ccc9aa8a60c05b3c102eb199593fa117ea9e595b36d7ab6c5ce4bf0d2917013b360a3 + checksum: 10/bb2feb79c0db19f459b265e9a0afb87611f4ba06c4776a5ea8ddcb0bfc81403292ada3a1c29cd7511872f8d2bfda7c29eab6dbc32052a60baf49576a50866d77 languageName: node linkType: hard @@ -26722,7 +26741,7 @@ __metadata: languageName: node linkType: hard -"rc-tree@npm:5.8.8, rc-tree@npm:~5.8.1": +"rc-tree@npm:5.8.8": version: 5.8.8 resolution: "rc-tree@npm:5.8.8" dependencies: @@ -26738,6 +26757,22 @@ __metadata: languageName: node linkType: hard +"rc-tree@npm:~5.9.0": + version: 5.9.0 + resolution: "rc-tree@npm:5.9.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.0.1" + rc-util: "npm:^5.16.1" + rc-virtual-list: "npm:^3.5.1" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/d7525c4a524c6de8e177ebc90fe9b924046951a02bacee85efd4529fb05a66add936802b43d5ca8d84469f9c63b8d542437365b911480f62a78e03e2c7fbaca0 + languageName: node + linkType: hard + "rc-trigger@npm:^2.2.0": version: 2.6.5 resolution: "rc-trigger@npm:2.6.5"