Compare commits
16 Commits
sriram/SQL
...
toddtreece
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07bb48e874 | ||
|
|
306186c4ea | ||
|
|
a28c70bbcc | ||
|
|
1ebcd2319a | ||
|
|
5dc3767854 | ||
|
|
040dbfb5e3 | ||
|
|
32d43f5b5d | ||
|
|
fef9c760a0 | ||
|
|
1fe9a38a2a | ||
|
|
59bf7896f4 | ||
|
|
4b4ad544a8 | ||
|
|
7e3289f2c9 | ||
|
|
0d0b5b757b | ||
|
|
c49261cce2 | ||
|
|
d5efce72f3 | ||
|
|
881c81f0b3 |
@@ -24,6 +24,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
cel.dev/expr v0.24.0 // indirect
|
cel.dev/expr v0.24.0 // indirect
|
||||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||||
github.com/apache/arrow-go/v18 v18.4.1 // indirect
|
github.com/apache/arrow-go/v18 v18.4.1 // indirect
|
||||||
github.com/armon/go-metrics v0.4.1 // indirect
|
github.com/armon/go-metrics v0.4.1 // indirect
|
||||||
@@ -35,16 +36,21 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
|
||||||
github.com/aws/smithy-go v1.23.1 // indirect
|
github.com/aws/smithy-go v1.23.1 // indirect
|
||||||
|
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
|
github.com/bluele/gcache v0.0.2 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cheekybits/genny v1.0.0 // indirect
|
github.com/cheekybits/genny v1.0.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/coreos/go-semver v0.3.1 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
|
github.com/diegoholiveira/jsonlogic/v3 v3.7.4 // indirect
|
||||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
@@ -132,11 +138,15 @@ require (
|
|||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
|
github.com/nikunjy/rules v1.5.0 // indirect
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||||
github.com/oklog/run v1.1.0 // indirect
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
|
github.com/open-feature/go-sdk v1.16.0 // indirect
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.6 // indirect
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.6 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
@@ -155,6 +165,7 @@ require (
|
|||||||
github.com/spf13/pflag v1.0.10 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
github.com/stoewer/go-strcase v1.3.1 // indirect
|
github.com/stoewer/go-strcase v1.3.1 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/thomaspoignant/go-feature-flag v1.42.0 // indirect
|
||||||
github.com/tjhop/slog-gokit v0.1.5 // indirect
|
github.com/tjhop/slog-gokit v0.1.5 // indirect
|
||||||
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
@@ -176,6 +187,8 @@ require (
|
|||||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
go.uber.org/mock v0.6.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.1 // indirect
|
go.uber.org/zap v1.27.1 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||||
|
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||||
|
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
@@ -38,12 +42,18 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B
|
|||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
|
||||||
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
|
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
|
||||||
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||||
|
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
|
||||||
|
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
|
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||||
|
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
||||||
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||||
@@ -60,6 +70,8 @@ github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wX
|
|||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
@@ -69,6 +81,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/diegoholiveira/jsonlogic/v3 v3.7.4 h1:92HSmB9bwM/o0ZvrCpcvTP2EsPXSkKtAniIr2W/dcIM=
|
||||||
|
github.com/diegoholiveira/jsonlogic/v3 v3.7.4/go.mod h1:OYRb6FSTVmMM+MNQ7ElmMsczyNSepw+OU4Z8emDSi4w=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||||
@@ -341,6 +355,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
|||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/nikunjy/rules v1.5.0 h1:KJDSLOsFhwt7kcXUyZqwkgrQg5YoUwj+TVu6ItCQShw=
|
||||||
|
github.com/nikunjy/rules v1.5.0/go.mod h1:TlZtZdBChrkqi8Lr2AXocme8Z7EsbxtFdDoKeI6neBQ=
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||||
@@ -355,6 +371,12 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU
|
|||||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||||
|
github.com/open-feature/go-sdk v1.16.0 h1:5NCHYv5slvNBIZhYXAzAufo0OI59OACZ5tczVqSE+Tg=
|
||||||
|
github.com/open-feature/go-sdk v1.16.0/go.mod h1:EIF40QcoYT1VbQkMPy2ZJH4kvZeY+qGUXAorzSWgKSo=
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.6 h1:megzzlQGjsRVWDX8oJnLaa5eEcsAHekiL4Uvl3jSAcY=
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/go-feature-flag v0.2.6/go.mod h1:K1gDKvt76CGFLSUMHUydd5ba2V5Cv69gQZsdbnXhAm8=
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.6 h1:WinefYxeVx5rV0uQmuWbxQf8iACu/JiRubo5w0saToc=
|
||||||
|
github.com/open-feature/go-sdk-contrib/providers/ofrep v0.1.6/go.mod h1:Dwcaoma6lZVqYwyfVlY7eB6RXbG+Ju3b9cnpTlUN+Hc=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
@@ -440,6 +462,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM=
|
||||||
|
github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574GlUxHD4rs8=
|
||||||
|
github.com/thomaspoignant/go-feature-flag v1.42.0 h1:C7embmOTzaLyRki+OoU2RvtVjJE9IrvgBA2C1mRN1lc=
|
||||||
|
github.com/thomaspoignant/go-feature-flag v1.42.0/go.mod h1:y0QiWH7chHWhGATb/+XqwAwErORmPSH2MUsQlCmmWlM=
|
||||||
github.com/tjhop/slog-gokit v0.1.5 h1:ayloIUi5EK2QYB8eY4DOPO95/mRtMW42lUkp3quJohc=
|
github.com/tjhop/slog-gokit v0.1.5 h1:ayloIUi5EK2QYB8eY4DOPO95/mRtMW42lUkp3quJohc=
|
||||||
github.com/tjhop/slog-gokit v0.1.5/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
|
github.com/tjhop/slog-gokit v0.1.5/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
|
||||||
@@ -507,8 +533,12 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
|
|||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
|
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||||
|
|||||||
@@ -5,7 +5,24 @@ metaV0Alpha1: {
|
|||||||
scope: "Namespaced"
|
scope: "Namespaced"
|
||||||
schema: {
|
schema: {
|
||||||
spec: {
|
spec: {
|
||||||
pluginJSON: #JSONData,
|
pluginJson: #JSONData
|
||||||
|
module?: {
|
||||||
|
path: string
|
||||||
|
hash?: string
|
||||||
|
loadingStrategy?: "fetch" | "script"
|
||||||
|
}
|
||||||
|
baseURL?: string
|
||||||
|
signature?: {
|
||||||
|
status: "internal" | "valid" | "invalid" | "modified" | "unsigned"
|
||||||
|
type?: "grafana" | "commercial" | "community" | "private" | "private-glob"
|
||||||
|
org?: string
|
||||||
|
}
|
||||||
|
angular?: {
|
||||||
|
detected: bool
|
||||||
|
}
|
||||||
|
translations?: [string]: string
|
||||||
|
// +listType=atomic
|
||||||
|
children?: [...string]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,13 +208,20 @@ func NewMetaExtensions() *MetaExtensions {
|
|||||||
|
|
||||||
// +k8s:openapi-gen=true
|
// +k8s:openapi-gen=true
|
||||||
type MetaSpec struct {
|
type MetaSpec struct {
|
||||||
PluginJSON MetaJSONData `json:"pluginJSON"`
|
PluginJson MetaJSONData `json:"pluginJson"`
|
||||||
|
Module *MetaV0alpha1SpecModule `json:"module,omitempty"`
|
||||||
|
BaseURL *string `json:"baseURL,omitempty"`
|
||||||
|
Signature *MetaV0alpha1SpecSignature `json:"signature,omitempty"`
|
||||||
|
Angular *MetaV0alpha1SpecAngular `json:"angular,omitempty"`
|
||||||
|
Translations map[string]string `json:"translations,omitempty"`
|
||||||
|
// +listType=atomic
|
||||||
|
Children []string `json:"children,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMetaSpec creates a new MetaSpec object.
|
// NewMetaSpec creates a new MetaSpec object.
|
||||||
func NewMetaSpec() *MetaSpec {
|
func NewMetaSpec() *MetaSpec {
|
||||||
return &MetaSpec{
|
return &MetaSpec{
|
||||||
PluginJSON: *NewMetaJSONData(),
|
PluginJson: *NewMetaJSONData(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,6 +419,40 @@ func NewMetaV0alpha1ExtensionsExtensionPoints() *MetaV0alpha1ExtensionsExtension
|
|||||||
return &MetaV0alpha1ExtensionsExtensionPoints{}
|
return &MetaV0alpha1ExtensionsExtensionPoints{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecModule struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Hash *string `json:"hash,omitempty"`
|
||||||
|
LoadingStrategy *MetaV0alpha1SpecModuleLoadingStrategy `json:"loadingStrategy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetaV0alpha1SpecModule creates a new MetaV0alpha1SpecModule object.
|
||||||
|
func NewMetaV0alpha1SpecModule() *MetaV0alpha1SpecModule {
|
||||||
|
return &MetaV0alpha1SpecModule{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecSignature struct {
|
||||||
|
Status MetaV0alpha1SpecSignatureStatus `json:"status"`
|
||||||
|
Type *MetaV0alpha1SpecSignatureType `json:"type,omitempty"`
|
||||||
|
Org *string `json:"org,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetaV0alpha1SpecSignature creates a new MetaV0alpha1SpecSignature object.
|
||||||
|
func NewMetaV0alpha1SpecSignature() *MetaV0alpha1SpecSignature {
|
||||||
|
return &MetaV0alpha1SpecSignature{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecAngular struct {
|
||||||
|
Detected bool `json:"detected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetaV0alpha1SpecAngular creates a new MetaV0alpha1SpecAngular object.
|
||||||
|
func NewMetaV0alpha1SpecAngular() *MetaV0alpha1SpecAngular {
|
||||||
|
return &MetaV0alpha1SpecAngular{}
|
||||||
|
}
|
||||||
|
|
||||||
// +k8s:openapi-gen=true
|
// +k8s:openapi-gen=true
|
||||||
type MetaJSONDataType string
|
type MetaJSONDataType string
|
||||||
|
|
||||||
@@ -472,3 +513,33 @@ const (
|
|||||||
MetaV0alpha1DependenciesPluginsTypeDatasource MetaV0alpha1DependenciesPluginsType = "datasource"
|
MetaV0alpha1DependenciesPluginsTypeDatasource MetaV0alpha1DependenciesPluginsType = "datasource"
|
||||||
MetaV0alpha1DependenciesPluginsTypePanel MetaV0alpha1DependenciesPluginsType = "panel"
|
MetaV0alpha1DependenciesPluginsTypePanel MetaV0alpha1DependenciesPluginsType = "panel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecModuleLoadingStrategy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetaV0alpha1SpecModuleLoadingStrategyFetch MetaV0alpha1SpecModuleLoadingStrategy = "fetch"
|
||||||
|
MetaV0alpha1SpecModuleLoadingStrategyScript MetaV0alpha1SpecModuleLoadingStrategy = "script"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecSignatureStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetaV0alpha1SpecSignatureStatusInternal MetaV0alpha1SpecSignatureStatus = "internal"
|
||||||
|
MetaV0alpha1SpecSignatureStatusValid MetaV0alpha1SpecSignatureStatus = "valid"
|
||||||
|
MetaV0alpha1SpecSignatureStatusInvalid MetaV0alpha1SpecSignatureStatus = "invalid"
|
||||||
|
MetaV0alpha1SpecSignatureStatusModified MetaV0alpha1SpecSignatureStatus = "modified"
|
||||||
|
MetaV0alpha1SpecSignatureStatusUnsigned MetaV0alpha1SpecSignatureStatus = "unsigned"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
type MetaV0alpha1SpecSignatureType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetaV0alpha1SpecSignatureTypeGrafana MetaV0alpha1SpecSignatureType = "grafana"
|
||||||
|
MetaV0alpha1SpecSignatureTypeCommercial MetaV0alpha1SpecSignatureType = "commercial"
|
||||||
|
MetaV0alpha1SpecSignatureTypeCommunity MetaV0alpha1SpecSignatureType = "community"
|
||||||
|
MetaV0alpha1SpecSignatureTypePrivate MetaV0alpha1SpecSignatureType = "private"
|
||||||
|
MetaV0alpha1SpecSignatureTypePrivateGlob MetaV0alpha1SpecSignatureType = "private-glob"
|
||||||
|
)
|
||||||
|
|||||||
2
apps/plugins/pkg/apis/plugins_manifest.go
generated
2
apps/plugins/pkg/apis/plugins_manifest.go
generated
File diff suppressed because one or more lines are too long
@@ -10,8 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-app-sdk/logging"
|
"github.com/grafana/grafana-app-sdk/logging"
|
||||||
|
|
||||||
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -87,45 +85,9 @@ func (p *CatalogProvider) GetMeta(ctx context.Context, pluginID, version string)
|
|||||||
return nil, fmt.Errorf("failed to decode response: %w", err)
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metaSpec := grafanaComPluginVersionMetaToMetaSpec(gcomMeta)
|
||||||
return &Result{
|
return &Result{
|
||||||
Meta: gcomMeta.JSON,
|
Meta: metaSpec,
|
||||||
TTL: p.ttl,
|
TTL: p.ttl,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// grafanaComPluginVersionMeta represents the response from grafana.com API
|
|
||||||
// GET /api/plugins/{pluginId}/versions/{version}
|
|
||||||
type grafanaComPluginVersionMeta struct {
|
|
||||||
PluginID string `json:"pluginSlug"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Commit string `json:"commit"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Keywords []string `json:"keywords"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
JSON pluginsv0alpha1.MetaJSONData `json:"json"`
|
|
||||||
Readme string `json:"readme"`
|
|
||||||
Downloads int `json:"downloads"`
|
|
||||||
Verified bool `json:"verified"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
StatusContext string `json:"statusContext"`
|
|
||||||
DownloadSlug string `json:"downloadSlug"`
|
|
||||||
SignatureType string `json:"signatureType"`
|
|
||||||
SignedByOrg string `json:"signedByOrg"`
|
|
||||||
SignedByOrgName string `json:"signedByOrgName"`
|
|
||||||
Packages struct {
|
|
||||||
Any struct {
|
|
||||||
Md5 string `json:"md5"`
|
|
||||||
Sha256 string `json:"sha256"`
|
|
||||||
PackageName string `json:"packageName"`
|
|
||||||
DownloadURL string `json:"downloadUrl"`
|
|
||||||
} `json:"any"`
|
|
||||||
} `json:"packages"`
|
|
||||||
Links []struct {
|
|
||||||
Rel string `json:"rel"`
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"links"`
|
|
||||||
AngularDetected bool `json:"angularDetected"`
|
|
||||||
Scopes []string `json:"scopes"`
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func TestCatalogProvider_GetMeta(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, result)
|
require.NotNil(t, result)
|
||||||
assert.Equal(t, expectedMeta, result.Meta)
|
assert.Equal(t, expectedMeta, result.Meta.PluginJson)
|
||||||
assert.Equal(t, defaultCatalogTTL, result.TTL)
|
assert.Equal(t, defaultCatalogTTL, result.TTL)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
725
apps/plugins/pkg/app/meta/converter.go
Normal file
725
apps/plugins/pkg/app/meta/converter.go
Normal file
@@ -0,0 +1,725 @@
|
|||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// jsonDataToMetaJSONData converts a plugins.JSONData to a pluginsv0alpha1.MetaJSONData.
|
||||||
|
// nolint:gocyclo
|
||||||
|
func jsonDataToMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.MetaJSONData {
|
||||||
|
meta := pluginsv0alpha1.MetaJSONData{
|
||||||
|
Id: jsonData.ID,
|
||||||
|
Name: jsonData.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map plugin type
|
||||||
|
switch jsonData.Type {
|
||||||
|
case plugins.TypeApp:
|
||||||
|
meta.Type = pluginsv0alpha1.MetaJSONDataTypeApp
|
||||||
|
case plugins.TypeDataSource:
|
||||||
|
meta.Type = pluginsv0alpha1.MetaJSONDataTypeDatasource
|
||||||
|
case plugins.TypePanel:
|
||||||
|
meta.Type = pluginsv0alpha1.MetaJSONDataTypePanel
|
||||||
|
case plugins.TypeRenderer:
|
||||||
|
meta.Type = pluginsv0alpha1.MetaJSONDataTypeRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Info
|
||||||
|
meta.Info = pluginsv0alpha1.MetaInfo{
|
||||||
|
Keywords: jsonData.Info.Keywords,
|
||||||
|
Logos: pluginsv0alpha1.MetaV0alpha1InfoLogos{
|
||||||
|
Small: jsonData.Info.Logos.Small,
|
||||||
|
Large: jsonData.Info.Logos.Large,
|
||||||
|
},
|
||||||
|
Updated: jsonData.Info.Updated,
|
||||||
|
Version: jsonData.Info.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonData.Info.Description != "" {
|
||||||
|
meta.Info.Description = &jsonData.Info.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonData.Info.Author.Name != "" || jsonData.Info.Author.URL != "" {
|
||||||
|
author := &pluginsv0alpha1.MetaV0alpha1InfoAuthor{}
|
||||||
|
if jsonData.Info.Author.Name != "" {
|
||||||
|
author.Name = &jsonData.Info.Author.Name
|
||||||
|
}
|
||||||
|
if jsonData.Info.Author.URL != "" {
|
||||||
|
author.Url = &jsonData.Info.Author.URL
|
||||||
|
}
|
||||||
|
meta.Info.Author = author
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Info.Links) > 0 {
|
||||||
|
meta.Info.Links = make([]pluginsv0alpha1.MetaV0alpha1InfoLinks, 0, len(jsonData.Info.Links))
|
||||||
|
for _, link := range jsonData.Info.Links {
|
||||||
|
v0Link := pluginsv0alpha1.MetaV0alpha1InfoLinks{}
|
||||||
|
if link.Name != "" {
|
||||||
|
v0Link.Name = &link.Name
|
||||||
|
}
|
||||||
|
if link.URL != "" {
|
||||||
|
v0Link.Url = &link.URL
|
||||||
|
}
|
||||||
|
meta.Info.Links = append(meta.Info.Links, v0Link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Info.Screenshots) > 0 {
|
||||||
|
meta.Info.Screenshots = make([]pluginsv0alpha1.MetaV0alpha1InfoScreenshots, 0, len(jsonData.Info.Screenshots))
|
||||||
|
for _, screenshot := range jsonData.Info.Screenshots {
|
||||||
|
v0Screenshot := pluginsv0alpha1.MetaV0alpha1InfoScreenshots{}
|
||||||
|
if screenshot.Name != "" {
|
||||||
|
v0Screenshot.Name = &screenshot.Name
|
||||||
|
}
|
||||||
|
if screenshot.Path != "" {
|
||||||
|
v0Screenshot.Path = &screenshot.Path
|
||||||
|
}
|
||||||
|
meta.Info.Screenshots = append(meta.Info.Screenshots, v0Screenshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Dependencies
|
||||||
|
meta.Dependencies = pluginsv0alpha1.MetaDependencies{
|
||||||
|
GrafanaDependency: jsonData.Dependencies.GrafanaDependency,
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonData.Dependencies.GrafanaVersion != "" {
|
||||||
|
meta.Dependencies.GrafanaVersion = &jsonData.Dependencies.GrafanaVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Dependencies.Plugins) > 0 {
|
||||||
|
meta.Dependencies.Plugins = make([]pluginsv0alpha1.MetaV0alpha1DependenciesPlugins, 0, len(jsonData.Dependencies.Plugins))
|
||||||
|
for _, dep := range jsonData.Dependencies.Plugins {
|
||||||
|
var depType pluginsv0alpha1.MetaV0alpha1DependenciesPluginsType
|
||||||
|
switch dep.Type {
|
||||||
|
case "app":
|
||||||
|
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeApp
|
||||||
|
case "datasource":
|
||||||
|
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeDatasource
|
||||||
|
case "panel":
|
||||||
|
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypePanel
|
||||||
|
}
|
||||||
|
meta.Dependencies.Plugins = append(meta.Dependencies.Plugins, pluginsv0alpha1.MetaV0alpha1DependenciesPlugins{
|
||||||
|
Id: dep.ID,
|
||||||
|
Type: depType,
|
||||||
|
Name: dep.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Dependencies.Extensions.ExposedComponents) > 0 {
|
||||||
|
meta.Dependencies.Extensions = &pluginsv0alpha1.MetaV0alpha1DependenciesExtensions{
|
||||||
|
ExposedComponents: jsonData.Dependencies.Extensions.ExposedComponents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map optional boolean fields
|
||||||
|
if jsonData.Alerting {
|
||||||
|
meta.Alerting = &jsonData.Alerting
|
||||||
|
}
|
||||||
|
if jsonData.Annotations {
|
||||||
|
meta.Annotations = &jsonData.Annotations
|
||||||
|
}
|
||||||
|
if jsonData.AutoEnabled {
|
||||||
|
meta.AutoEnabled = &jsonData.AutoEnabled
|
||||||
|
}
|
||||||
|
if jsonData.Backend {
|
||||||
|
meta.Backend = &jsonData.Backend
|
||||||
|
}
|
||||||
|
if jsonData.BuiltIn {
|
||||||
|
meta.BuiltIn = &jsonData.BuiltIn
|
||||||
|
}
|
||||||
|
if jsonData.HideFromList {
|
||||||
|
meta.HideFromList = &jsonData.HideFromList
|
||||||
|
}
|
||||||
|
if jsonData.Logs {
|
||||||
|
meta.Logs = &jsonData.Logs
|
||||||
|
}
|
||||||
|
if jsonData.Metrics {
|
||||||
|
meta.Metrics = &jsonData.Metrics
|
||||||
|
}
|
||||||
|
if jsonData.MultiValueFilterOperators {
|
||||||
|
meta.MultiValueFilterOperators = &jsonData.MultiValueFilterOperators
|
||||||
|
}
|
||||||
|
if jsonData.Preload {
|
||||||
|
meta.Preload = &jsonData.Preload
|
||||||
|
}
|
||||||
|
if jsonData.SkipDataQuery {
|
||||||
|
meta.SkipDataQuery = &jsonData.SkipDataQuery
|
||||||
|
}
|
||||||
|
if jsonData.Streaming {
|
||||||
|
meta.Streaming = &jsonData.Streaming
|
||||||
|
}
|
||||||
|
if jsonData.Tracing {
|
||||||
|
meta.Tracing = &jsonData.Tracing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map category
|
||||||
|
if jsonData.Category != "" {
|
||||||
|
var category pluginsv0alpha1.MetaJSONDataCategory
|
||||||
|
switch jsonData.Category {
|
||||||
|
case "tsdb":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryTsdb
|
||||||
|
case "logging":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryLogging
|
||||||
|
case "cloud":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryCloud
|
||||||
|
case "tracing":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryTracing
|
||||||
|
case "profiling":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryProfiling
|
||||||
|
case "sql":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategorySql
|
||||||
|
case "enterprise":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryEnterprise
|
||||||
|
case "iot":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryIot
|
||||||
|
case "other":
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryOther
|
||||||
|
default:
|
||||||
|
category = pluginsv0alpha1.MetaJSONDataCategoryOther
|
||||||
|
}
|
||||||
|
meta.Category = &category
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map state
|
||||||
|
if jsonData.State != "" {
|
||||||
|
var state pluginsv0alpha1.MetaJSONDataState
|
||||||
|
switch jsonData.State {
|
||||||
|
case plugins.ReleaseStateAlpha:
|
||||||
|
state = pluginsv0alpha1.MetaJSONDataStateAlpha
|
||||||
|
case plugins.ReleaseStateBeta:
|
||||||
|
state = pluginsv0alpha1.MetaJSONDataStateBeta
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if state != "" {
|
||||||
|
meta.State = &state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map executable
|
||||||
|
if jsonData.Executable != "" {
|
||||||
|
meta.Executable = &jsonData.Executable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map QueryOptions
|
||||||
|
if len(jsonData.QueryOptions) > 0 {
|
||||||
|
queryOptions := &pluginsv0alpha1.MetaQueryOptions{}
|
||||||
|
if val, ok := jsonData.QueryOptions["maxDataPoints"]; ok {
|
||||||
|
queryOptions.MaxDataPoints = &val
|
||||||
|
}
|
||||||
|
if val, ok := jsonData.QueryOptions["minInterval"]; ok {
|
||||||
|
queryOptions.MinInterval = &val
|
||||||
|
}
|
||||||
|
if val, ok := jsonData.QueryOptions["cacheTimeout"]; ok {
|
||||||
|
queryOptions.CacheTimeout = &val
|
||||||
|
}
|
||||||
|
meta.QueryOptions = queryOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Includes
|
||||||
|
if len(jsonData.Includes) > 0 {
|
||||||
|
meta.Includes = make([]pluginsv0alpha1.MetaInclude, 0, len(jsonData.Includes))
|
||||||
|
for _, include := range jsonData.Includes {
|
||||||
|
v0Include := pluginsv0alpha1.MetaInclude{}
|
||||||
|
if include.UID != "" {
|
||||||
|
v0Include.Uid = &include.UID
|
||||||
|
}
|
||||||
|
if include.Type != "" {
|
||||||
|
var includeType pluginsv0alpha1.MetaIncludeType
|
||||||
|
switch include.Type {
|
||||||
|
case "dashboard":
|
||||||
|
includeType = pluginsv0alpha1.MetaIncludeTypeDashboard
|
||||||
|
case "page":
|
||||||
|
includeType = pluginsv0alpha1.MetaIncludeTypePage
|
||||||
|
case "panel":
|
||||||
|
includeType = pluginsv0alpha1.MetaIncludeTypePanel
|
||||||
|
case "datasource":
|
||||||
|
includeType = pluginsv0alpha1.MetaIncludeTypeDatasource
|
||||||
|
}
|
||||||
|
v0Include.Type = &includeType
|
||||||
|
}
|
||||||
|
if include.Name != "" {
|
||||||
|
v0Include.Name = &include.Name
|
||||||
|
}
|
||||||
|
if include.Component != "" {
|
||||||
|
v0Include.Component = &include.Component
|
||||||
|
}
|
||||||
|
if include.Role != "" {
|
||||||
|
var role pluginsv0alpha1.MetaIncludeRole
|
||||||
|
switch include.Role {
|
||||||
|
case "Admin":
|
||||||
|
role = pluginsv0alpha1.MetaIncludeRoleAdmin
|
||||||
|
case "Editor":
|
||||||
|
role = pluginsv0alpha1.MetaIncludeRoleEditor
|
||||||
|
case "Viewer":
|
||||||
|
role = pluginsv0alpha1.MetaIncludeRoleViewer
|
||||||
|
}
|
||||||
|
v0Include.Role = &role
|
||||||
|
}
|
||||||
|
if include.Action != "" {
|
||||||
|
v0Include.Action = &include.Action
|
||||||
|
}
|
||||||
|
if include.Path != "" {
|
||||||
|
v0Include.Path = &include.Path
|
||||||
|
}
|
||||||
|
if include.AddToNav {
|
||||||
|
v0Include.AddToNav = &include.AddToNav
|
||||||
|
}
|
||||||
|
if include.DefaultNav {
|
||||||
|
v0Include.DefaultNav = &include.DefaultNav
|
||||||
|
}
|
||||||
|
if include.Icon != "" {
|
||||||
|
v0Include.Icon = &include.Icon
|
||||||
|
}
|
||||||
|
meta.Includes = append(meta.Includes, v0Include)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Routes
|
||||||
|
if len(jsonData.Routes) > 0 {
|
||||||
|
meta.Routes = make([]pluginsv0alpha1.MetaRoute, 0, len(jsonData.Routes))
|
||||||
|
for _, route := range jsonData.Routes {
|
||||||
|
v0Route := pluginsv0alpha1.MetaRoute{}
|
||||||
|
if route.Path != "" {
|
||||||
|
v0Route.Path = &route.Path
|
||||||
|
}
|
||||||
|
if route.Method != "" {
|
||||||
|
v0Route.Method = &route.Method
|
||||||
|
}
|
||||||
|
if route.URL != "" {
|
||||||
|
v0Route.Url = &route.URL
|
||||||
|
}
|
||||||
|
if route.ReqRole != "" {
|
||||||
|
reqRole := string(route.ReqRole)
|
||||||
|
v0Route.ReqRole = &reqRole
|
||||||
|
}
|
||||||
|
if route.ReqAction != "" {
|
||||||
|
v0Route.ReqAction = &route.ReqAction
|
||||||
|
}
|
||||||
|
if len(route.Headers) > 0 {
|
||||||
|
headers := make([]string, 0, len(route.Headers))
|
||||||
|
for _, header := range route.Headers {
|
||||||
|
headers = append(headers, header.Name+": "+header.Content)
|
||||||
|
}
|
||||||
|
v0Route.Headers = headers
|
||||||
|
}
|
||||||
|
if len(route.URLParams) > 0 {
|
||||||
|
v0Route.UrlParams = make([]pluginsv0alpha1.MetaV0alpha1RouteUrlParams, 0, len(route.URLParams))
|
||||||
|
for _, param := range route.URLParams {
|
||||||
|
v0Param := pluginsv0alpha1.MetaV0alpha1RouteUrlParams{}
|
||||||
|
if param.Name != "" {
|
||||||
|
v0Param.Name = ¶m.Name
|
||||||
|
}
|
||||||
|
if param.Content != "" {
|
||||||
|
v0Param.Content = ¶m.Content
|
||||||
|
}
|
||||||
|
v0Route.UrlParams = append(v0Route.UrlParams, v0Param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if route.TokenAuth != nil {
|
||||||
|
v0Route.TokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteTokenAuth{}
|
||||||
|
if route.TokenAuth.Url != "" {
|
||||||
|
v0Route.TokenAuth.Url = &route.TokenAuth.Url
|
||||||
|
}
|
||||||
|
if len(route.TokenAuth.Scopes) > 0 {
|
||||||
|
v0Route.TokenAuth.Scopes = route.TokenAuth.Scopes
|
||||||
|
}
|
||||||
|
if len(route.TokenAuth.Params) > 0 {
|
||||||
|
v0Route.TokenAuth.Params = make(map[string]interface{})
|
||||||
|
for k, v := range route.TokenAuth.Params {
|
||||||
|
v0Route.TokenAuth.Params[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if route.JwtTokenAuth != nil {
|
||||||
|
v0Route.JwtTokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteJwtTokenAuth{}
|
||||||
|
if route.JwtTokenAuth.Url != "" {
|
||||||
|
v0Route.JwtTokenAuth.Url = &route.JwtTokenAuth.Url
|
||||||
|
}
|
||||||
|
if len(route.JwtTokenAuth.Scopes) > 0 {
|
||||||
|
v0Route.JwtTokenAuth.Scopes = route.JwtTokenAuth.Scopes
|
||||||
|
}
|
||||||
|
if len(route.JwtTokenAuth.Params) > 0 {
|
||||||
|
v0Route.JwtTokenAuth.Params = make(map[string]interface{})
|
||||||
|
for k, v := range route.JwtTokenAuth.Params {
|
||||||
|
v0Route.JwtTokenAuth.Params[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(route.Body) > 0 {
|
||||||
|
var bodyMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal(route.Body, &bodyMap); err == nil {
|
||||||
|
v0Route.Body = bodyMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meta.Routes = append(meta.Routes, v0Route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Extensions
|
||||||
|
if len(jsonData.Extensions.AddedLinks) > 0 || len(jsonData.Extensions.AddedComponents) > 0 ||
|
||||||
|
len(jsonData.Extensions.ExposedComponents) > 0 || len(jsonData.Extensions.ExtensionPoints) > 0 {
|
||||||
|
extensions := &pluginsv0alpha1.MetaExtensions{}
|
||||||
|
|
||||||
|
if len(jsonData.Extensions.AddedLinks) > 0 {
|
||||||
|
extensions.AddedLinks = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks, 0, len(jsonData.Extensions.AddedLinks))
|
||||||
|
for _, link := range jsonData.Extensions.AddedLinks {
|
||||||
|
v0Link := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks{
|
||||||
|
Targets: link.Targets,
|
||||||
|
Title: link.Title,
|
||||||
|
}
|
||||||
|
if link.Description != "" {
|
||||||
|
v0Link.Description = &link.Description
|
||||||
|
}
|
||||||
|
extensions.AddedLinks = append(extensions.AddedLinks, v0Link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Extensions.AddedComponents) > 0 {
|
||||||
|
extensions.AddedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents, 0, len(jsonData.Extensions.AddedComponents))
|
||||||
|
for _, comp := range jsonData.Extensions.AddedComponents {
|
||||||
|
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents{
|
||||||
|
Targets: comp.Targets,
|
||||||
|
Title: comp.Title,
|
||||||
|
}
|
||||||
|
if comp.Description != "" {
|
||||||
|
v0Comp.Description = &comp.Description
|
||||||
|
}
|
||||||
|
extensions.AddedComponents = append(extensions.AddedComponents, v0Comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Extensions.ExposedComponents) > 0 {
|
||||||
|
extensions.ExposedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents, 0, len(jsonData.Extensions.ExposedComponents))
|
||||||
|
for _, comp := range jsonData.Extensions.ExposedComponents {
|
||||||
|
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents{
|
||||||
|
Id: comp.Id,
|
||||||
|
}
|
||||||
|
if comp.Title != "" {
|
||||||
|
v0Comp.Title = &comp.Title
|
||||||
|
}
|
||||||
|
if comp.Description != "" {
|
||||||
|
v0Comp.Description = &comp.Description
|
||||||
|
}
|
||||||
|
extensions.ExposedComponents = append(extensions.ExposedComponents, v0Comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(jsonData.Extensions.ExtensionPoints) > 0 {
|
||||||
|
extensions.ExtensionPoints = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints, 0, len(jsonData.Extensions.ExtensionPoints))
|
||||||
|
for _, point := range jsonData.Extensions.ExtensionPoints {
|
||||||
|
v0Point := pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints{
|
||||||
|
Id: point.Id,
|
||||||
|
}
|
||||||
|
if point.Title != "" {
|
||||||
|
v0Point.Title = &point.Title
|
||||||
|
}
|
||||||
|
if point.Description != "" {
|
||||||
|
v0Point.Description = &point.Description
|
||||||
|
}
|
||||||
|
extensions.ExtensionPoints = append(extensions.ExtensionPoints, v0Point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meta.Extensions = extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Roles
|
||||||
|
if len(jsonData.Roles) > 0 {
|
||||||
|
meta.Roles = make([]pluginsv0alpha1.MetaRole, 0, len(jsonData.Roles))
|
||||||
|
for _, role := range jsonData.Roles {
|
||||||
|
v0Role := pluginsv0alpha1.MetaRole{
|
||||||
|
Grants: role.Grants,
|
||||||
|
}
|
||||||
|
if role.Role.Name != "" || role.Role.Description != "" || len(role.Role.Permissions) > 0 {
|
||||||
|
v0RoleRole := &pluginsv0alpha1.MetaV0alpha1RoleRole{}
|
||||||
|
if role.Role.Name != "" {
|
||||||
|
v0RoleRole.Name = &role.Role.Name
|
||||||
|
}
|
||||||
|
if role.Role.Description != "" {
|
||||||
|
v0RoleRole.Description = &role.Role.Description
|
||||||
|
}
|
||||||
|
if len(role.Role.Permissions) > 0 {
|
||||||
|
v0RoleRole.Permissions = make([]pluginsv0alpha1.MetaV0alpha1RoleRolePermissions, 0, len(role.Role.Permissions))
|
||||||
|
for _, perm := range role.Role.Permissions {
|
||||||
|
v0Perm := pluginsv0alpha1.MetaV0alpha1RoleRolePermissions{}
|
||||||
|
if perm.Action != "" {
|
||||||
|
v0Perm.Action = &perm.Action
|
||||||
|
}
|
||||||
|
if perm.Scope != "" {
|
||||||
|
v0Perm.Scope = &perm.Scope
|
||||||
|
}
|
||||||
|
v0RoleRole.Permissions = append(v0RoleRole.Permissions, v0Perm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v0Role.Role = v0RoleRole
|
||||||
|
}
|
||||||
|
meta.Roles = append(meta.Roles, v0Role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map IAM
|
||||||
|
if jsonData.IAM != nil && len(jsonData.IAM.Permissions) > 0 {
|
||||||
|
iam := &pluginsv0alpha1.MetaIAM{
|
||||||
|
Permissions: make([]pluginsv0alpha1.MetaV0alpha1IAMPermissions, 0, len(jsonData.IAM.Permissions)),
|
||||||
|
}
|
||||||
|
for _, perm := range jsonData.IAM.Permissions {
|
||||||
|
v0Perm := pluginsv0alpha1.MetaV0alpha1IAMPermissions{}
|
||||||
|
if perm.Action != "" {
|
||||||
|
v0Perm.Action = &perm.Action
|
||||||
|
}
|
||||||
|
if perm.Scope != "" {
|
||||||
|
v0Perm.Scope = &perm.Scope
|
||||||
|
}
|
||||||
|
iam.Permissions = append(iam.Permissions, v0Perm)
|
||||||
|
}
|
||||||
|
meta.Iam = iam
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// pluginStorePluginToMeta converts a pluginstore.Plugin to a pluginsv0alpha1.MetaSpec.
|
||||||
|
// This is similar to pluginToPluginMetaSpec but works with the plugin store DTO.
|
||||||
|
// loadingStrategy and moduleHash are optional calculated values that can be provided.
|
||||||
|
func pluginStorePluginToMeta(plugin pluginstore.Plugin, loadingStrategy plugins.LoadingStrategy, moduleHash string) pluginsv0alpha1.MetaSpec {
|
||||||
|
metaSpec := pluginsv0alpha1.MetaSpec{
|
||||||
|
PluginJson: jsonDataToMetaJSONData(plugin.JSONData),
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.Module != "" {
|
||||||
|
module := &pluginsv0alpha1.MetaV0alpha1SpecModule{
|
||||||
|
Path: plugin.Module,
|
||||||
|
}
|
||||||
|
if moduleHash != "" {
|
||||||
|
module.Hash = &moduleHash
|
||||||
|
}
|
||||||
|
if loadingStrategy != "" {
|
||||||
|
var ls pluginsv0alpha1.MetaV0alpha1SpecModuleLoadingStrategy
|
||||||
|
switch loadingStrategy {
|
||||||
|
case plugins.LoadingStrategyFetch:
|
||||||
|
ls = pluginsv0alpha1.MetaV0alpha1SpecModuleLoadingStrategyFetch
|
||||||
|
case plugins.LoadingStrategyScript:
|
||||||
|
ls = pluginsv0alpha1.MetaV0alpha1SpecModuleLoadingStrategyScript
|
||||||
|
}
|
||||||
|
module.LoadingStrategy = &ls
|
||||||
|
}
|
||||||
|
metaSpec.Module = module
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.BaseURL != "" {
|
||||||
|
metaSpec.BaseURL = &plugin.BaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.Signature != "" {
|
||||||
|
signature := &pluginsv0alpha1.MetaV0alpha1SpecSignature{
|
||||||
|
Status: convertSignatureStatus(plugin.Signature),
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.SignatureType != "" {
|
||||||
|
sigType := convertSignatureType(plugin.SignatureType)
|
||||||
|
signature.Type = &sigType
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.SignatureOrg != "" {
|
||||||
|
signature.Org = &plugin.SignatureOrg
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSpec.Signature = signature
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(plugin.Children) > 0 {
|
||||||
|
metaSpec.Children = plugin.Children
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
|
||||||
|
Detected: plugin.Angular.Detected,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(plugin.Translations) > 0 {
|
||||||
|
metaSpec.Translations = plugin.Translations
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertSignatureStatus converts plugins.SignatureStatus to pluginsv0alpha1.MetaV0alpha1SpecSignatureStatus.
|
||||||
|
func convertSignatureStatus(status plugins.SignatureStatus) pluginsv0alpha1.MetaV0alpha1SpecSignatureStatus {
|
||||||
|
switch status {
|
||||||
|
case plugins.SignatureStatusInternal:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusInternal
|
||||||
|
case plugins.SignatureStatusValid:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusValid
|
||||||
|
case plugins.SignatureStatusInvalid:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusInvalid
|
||||||
|
case plugins.SignatureStatusModified:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusModified
|
||||||
|
case plugins.SignatureStatusUnsigned:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusUnsigned
|
||||||
|
default:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusUnsigned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertSignatureType converts plugins.SignatureType to pluginsv0alpha1.MetaV0alpha1SpecSignatureType.
|
||||||
|
func convertSignatureType(sigType plugins.SignatureType) pluginsv0alpha1.MetaV0alpha1SpecSignatureType {
|
||||||
|
switch sigType {
|
||||||
|
case plugins.SignatureTypeGrafana:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeGrafana
|
||||||
|
case plugins.SignatureTypeCommercial:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeCommercial
|
||||||
|
case plugins.SignatureTypeCommunity:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeCommunity
|
||||||
|
case plugins.SignatureTypePrivate:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypePrivate
|
||||||
|
case plugins.SignatureTypePrivateGlob:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypePrivateGlob
|
||||||
|
default:
|
||||||
|
return pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeGrafana
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pluginToMetaSpec converts a fully loaded *plugins.Plugin to a pluginsv0alpha1.MetaSpec.
|
||||||
|
func pluginToMetaSpec(plugin *plugins.Plugin) pluginsv0alpha1.MetaSpec {
|
||||||
|
metaSpec := pluginsv0alpha1.MetaSpec{
|
||||||
|
PluginJson: jsonDataToMetaJSONData(plugin.JSONData),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set module information
|
||||||
|
if plugin.Module != "" {
|
||||||
|
module := &pluginsv0alpha1.MetaV0alpha1SpecModule{
|
||||||
|
Path: plugin.Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingStrategy := pluginsv0alpha1.MetaV0alpha1SpecModuleLoadingStrategyScript
|
||||||
|
module.LoadingStrategy = &loadingStrategy
|
||||||
|
|
||||||
|
metaSpec.Module = module
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set BaseURL
|
||||||
|
if plugin.BaseURL != "" {
|
||||||
|
metaSpec.BaseURL = &plugin.BaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set signature information
|
||||||
|
signature := &pluginsv0alpha1.MetaV0alpha1SpecSignature{
|
||||||
|
Status: convertSignatureStatus(plugin.Signature),
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.SignatureType != "" {
|
||||||
|
sigType := convertSignatureType(plugin.SignatureType)
|
||||||
|
signature.Type = &sigType
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin.SignatureOrg != "" {
|
||||||
|
signature.Org = &plugin.SignatureOrg
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSpec.Signature = signature
|
||||||
|
|
||||||
|
if len(plugin.Children) > 0 {
|
||||||
|
children := make([]string, 0, len(plugin.Children))
|
||||||
|
for _, child := range plugin.Children {
|
||||||
|
children = append(children, child.ID)
|
||||||
|
}
|
||||||
|
metaSpec.Children = children
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
|
||||||
|
Detected: plugin.Angular.Detected,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(plugin.Translations) > 0 {
|
||||||
|
metaSpec.Translations = plugin.Translations
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// grafanaComPluginVersionMeta represents the response from grafana.com API
|
||||||
|
// GET /api/plugins/{pluginId}/versions/{version}
|
||||||
|
type grafanaComPluginVersionMeta struct {
|
||||||
|
PluginID string `json:"pluginSlug"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Commit string `json:"commit"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Keywords []string `json:"keywords"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
JSON pluginsv0alpha1.MetaJSONData `json:"json"`
|
||||||
|
Readme string `json:"readme"`
|
||||||
|
Downloads int `json:"downloads"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
StatusContext string `json:"statusContext"`
|
||||||
|
DownloadSlug string `json:"downloadSlug"`
|
||||||
|
SignatureType string `json:"signatureType"`
|
||||||
|
SignedByOrg string `json:"signedByOrg"`
|
||||||
|
SignedByOrgName string `json:"signedByOrgName"`
|
||||||
|
Packages struct {
|
||||||
|
Any struct {
|
||||||
|
Md5 string `json:"md5"`
|
||||||
|
Sha256 string `json:"sha256"`
|
||||||
|
PackageName string `json:"packageName"`
|
||||||
|
DownloadURL string `json:"downloadUrl"`
|
||||||
|
} `json:"any"`
|
||||||
|
} `json:"packages"`
|
||||||
|
Links []struct {
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
} `json:"links"`
|
||||||
|
AngularDetected bool `json:"angularDetected"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// grafanaComPluginVersionMetaToMetaSpec converts a grafanaComPluginVersionMeta to a pluginsv0alpha1.MetaSpec.
|
||||||
|
func grafanaComPluginVersionMetaToMetaSpec(gcomMeta grafanaComPluginVersionMeta) pluginsv0alpha1.MetaSpec {
|
||||||
|
metaSpec := pluginsv0alpha1.MetaSpec{
|
||||||
|
PluginJson: gcomMeta.JSON,
|
||||||
|
}
|
||||||
|
|
||||||
|
if gcomMeta.SignatureType != "" {
|
||||||
|
signature := &pluginsv0alpha1.MetaV0alpha1SpecSignature{
|
||||||
|
Status: pluginsv0alpha1.MetaV0alpha1SpecSignatureStatusValid,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch gcomMeta.SignatureType {
|
||||||
|
case "grafana":
|
||||||
|
sigType := pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeGrafana
|
||||||
|
signature.Type = &sigType
|
||||||
|
case "commercial":
|
||||||
|
sigType := pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeCommercial
|
||||||
|
signature.Type = &sigType
|
||||||
|
case "community":
|
||||||
|
sigType := pluginsv0alpha1.MetaV0alpha1SpecSignatureTypeCommunity
|
||||||
|
signature.Type = &sigType
|
||||||
|
case "private":
|
||||||
|
sigType := pluginsv0alpha1.MetaV0alpha1SpecSignatureTypePrivate
|
||||||
|
signature.Type = &sigType
|
||||||
|
case "private-glob":
|
||||||
|
sigType := pluginsv0alpha1.MetaV0alpha1SpecSignatureTypePrivateGlob
|
||||||
|
signature.Type = &sigType
|
||||||
|
}
|
||||||
|
|
||||||
|
if gcomMeta.SignedByOrg != "" {
|
||||||
|
signature.Org = &gcomMeta.SignedByOrg
|
||||||
|
}
|
||||||
|
|
||||||
|
metaSpec.Signature = signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set angular info
|
||||||
|
metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
|
||||||
|
Detected: gcomMeta.AngularDetected,
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaSpec
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package meta
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -13,7 +12,15 @@ import (
|
|||||||
|
|
||||||
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
|
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/config"
|
||||||
|
pluginsLoader "github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/discovery"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/initialization"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/termination"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/validation"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/sources"
|
"github.com/grafana/grafana/pkg/plugins/manager/sources"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -23,9 +30,10 @@ const (
|
|||||||
// CoreProvider retrieves plugin metadata for core plugins.
|
// CoreProvider retrieves plugin metadata for core plugins.
|
||||||
type CoreProvider struct {
|
type CoreProvider struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
loadedPlugins map[string]pluginsv0alpha1.MetaJSONData
|
loadedPlugins map[string]pluginsv0alpha1.MetaSpec
|
||||||
initialized bool
|
initialized bool
|
||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
|
loader pluginsLoader.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCoreProvider creates a new CoreProvider for core plugins.
|
// NewCoreProvider creates a new CoreProvider for core plugins.
|
||||||
@@ -35,9 +43,13 @@ func NewCoreProvider() *CoreProvider {
|
|||||||
|
|
||||||
// NewCoreProviderWithTTL creates a new CoreProvider with a custom TTL.
|
// NewCoreProviderWithTTL creates a new CoreProvider with a custom TTL.
|
||||||
func NewCoreProviderWithTTL(ttl time.Duration) *CoreProvider {
|
func NewCoreProviderWithTTL(ttl time.Duration) *CoreProvider {
|
||||||
|
cfg := &config.PluginManagementCfg{
|
||||||
|
Features: config.Features{},
|
||||||
|
}
|
||||||
return &CoreProvider{
|
return &CoreProvider{
|
||||||
loadedPlugins: make(map[string]pluginsv0alpha1.MetaJSONData),
|
loadedPlugins: make(map[string]pluginsv0alpha1.MetaSpec),
|
||||||
ttl: ttl,
|
ttl: ttl,
|
||||||
|
loader: createLoader(cfg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,9 +88,9 @@ func (p *CoreProvider) GetMeta(ctx context.Context, pluginID, _ string) (*Result
|
|||||||
p.initialized = true
|
p.initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if meta, found := p.loadedPlugins[pluginID]; found {
|
if spec, found := p.loadedPlugins[pluginID]; found {
|
||||||
return &Result{
|
return &Result{
|
||||||
Meta: meta,
|
Meta: spec,
|
||||||
TTL: p.ttl,
|
TTL: p.ttl,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -86,8 +98,8 @@ func (p *CoreProvider) GetMeta(ctx context.Context, pluginID, _ string) (*Result
|
|||||||
return nil, ErrMetaNotFound
|
return nil, ErrMetaNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadPlugins discovers and caches all core plugins.
|
// loadPlugins discovers and caches all core plugins by fully loading them.
|
||||||
// Returns an error if the static root path cannot be found or if plugin discovery fails.
|
// Returns an error if the static root path cannot be found or if plugin loading fails.
|
||||||
// This error will be handled gracefully by GetMeta, which will return ErrMetaNotFound
|
// This error will be handled gracefully by GetMeta, which will return ErrMetaNotFound
|
||||||
// to allow other providers to handle the request.
|
// to allow other providers to handle the request.
|
||||||
func (p *CoreProvider) loadPlugins(ctx context.Context) error {
|
func (p *CoreProvider) loadPlugins(ctx context.Context) error {
|
||||||
@@ -108,496 +120,51 @@ func (p *CoreProvider) loadPlugins(ctx context.Context) error {
|
|||||||
panelPath := filepath.Join(staticRootPath, "app", "plugins", "panel")
|
panelPath := filepath.Join(staticRootPath, "app", "plugins", "panel")
|
||||||
|
|
||||||
src := sources.NewLocalSource(plugins.ClassCore, []string{datasourcePath, panelPath})
|
src := sources.NewLocalSource(plugins.ClassCore, []string{datasourcePath, panelPath})
|
||||||
ps, err := src.Discover(ctx)
|
loadedPlugins, err := p.loader.Load(ctx, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ps) == 0 {
|
if len(loadedPlugins) == 0 {
|
||||||
logging.DefaultLogger.Warn("CoreProvider: no core plugins found during discovery")
|
logging.DefaultLogger.Warn("CoreProvider: no core plugins found during loading")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bundle := range ps {
|
for _, plugin := range loadedPlugins {
|
||||||
meta := jsonDataToMetaJSONData(bundle.Primary.JSONData)
|
metaSpec := pluginToMetaSpec(plugin)
|
||||||
p.loadedPlugins[bundle.Primary.JSONData.ID] = meta
|
p.loadedPlugins[plugin.ID] = metaSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsonDataToMetaJSONData converts a plugins.JSONData to a pluginsv0alpha1.MetaJSONData.
|
// createLoader creates a loader service configured for core plugins.
|
||||||
// nolint:gocyclo
|
func createLoader(cfg *config.PluginManagementCfg) pluginsLoader.Service {
|
||||||
func jsonDataToMetaJSONData(jsonData plugins.JSONData) pluginsv0alpha1.MetaJSONData {
|
d := discovery.New(cfg, discovery.Opts{
|
||||||
meta := pluginsv0alpha1.MetaJSONData{
|
FilterFuncs: []discovery.FilterFunc{
|
||||||
Id: jsonData.ID,
|
// Allow all plugin types for core plugins
|
||||||
Name: jsonData.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map plugin type
|
|
||||||
switch jsonData.Type {
|
|
||||||
case plugins.TypeApp:
|
|
||||||
meta.Type = pluginsv0alpha1.MetaJSONDataTypeApp
|
|
||||||
case plugins.TypeDataSource:
|
|
||||||
meta.Type = pluginsv0alpha1.MetaJSONDataTypeDatasource
|
|
||||||
case plugins.TypePanel:
|
|
||||||
meta.Type = pluginsv0alpha1.MetaJSONDataTypePanel
|
|
||||||
case plugins.TypeRenderer:
|
|
||||||
meta.Type = pluginsv0alpha1.MetaJSONDataTypeRenderer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Info
|
|
||||||
meta.Info = pluginsv0alpha1.MetaInfo{
|
|
||||||
Keywords: jsonData.Info.Keywords,
|
|
||||||
Logos: pluginsv0alpha1.MetaV0alpha1InfoLogos{
|
|
||||||
Small: jsonData.Info.Logos.Small,
|
|
||||||
Large: jsonData.Info.Logos.Large,
|
|
||||||
},
|
},
|
||||||
Updated: jsonData.Info.Updated,
|
})
|
||||||
Version: jsonData.Info.Version,
|
b := bootstrap.New(cfg, bootstrap.Opts{
|
||||||
}
|
DecorateFuncs: []bootstrap.DecorateFunc{}, // no decoration required for metadata
|
||||||
|
})
|
||||||
|
v := validation.New(cfg, validation.Opts{
|
||||||
|
ValidateFuncs: []validation.ValidateFunc{
|
||||||
|
// Skip validation for core plugins - they're trusted
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i := initialization.New(cfg, initialization.Opts{
|
||||||
|
InitializeFuncs: []initialization.InitializeFunc{
|
||||||
|
// Skip initialization - we only need metadata, not running plugins
|
||||||
|
},
|
||||||
|
})
|
||||||
|
t, _ := termination.New(cfg, termination.Opts{
|
||||||
|
TerminateFuncs: []termination.TerminateFunc{
|
||||||
|
// No termination needed for metadata-only loading
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
if jsonData.Info.Description != "" {
|
et := pluginerrs.ProvideErrorTracker()
|
||||||
meta.Info.Description = &jsonData.Info.Description
|
|
||||||
}
|
|
||||||
|
|
||||||
if jsonData.Info.Author.Name != "" || jsonData.Info.Author.URL != "" {
|
return pluginsLoader.New(cfg, d, b, v, i, t, et)
|
||||||
author := &pluginsv0alpha1.MetaV0alpha1InfoAuthor{}
|
|
||||||
if jsonData.Info.Author.Name != "" {
|
|
||||||
author.Name = &jsonData.Info.Author.Name
|
|
||||||
}
|
|
||||||
if jsonData.Info.Author.URL != "" {
|
|
||||||
author.Url = &jsonData.Info.Author.URL
|
|
||||||
}
|
|
||||||
meta.Info.Author = author
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Info.Links) > 0 {
|
|
||||||
meta.Info.Links = make([]pluginsv0alpha1.MetaV0alpha1InfoLinks, 0, len(jsonData.Info.Links))
|
|
||||||
for _, link := range jsonData.Info.Links {
|
|
||||||
v0Link := pluginsv0alpha1.MetaV0alpha1InfoLinks{}
|
|
||||||
if link.Name != "" {
|
|
||||||
v0Link.Name = &link.Name
|
|
||||||
}
|
|
||||||
if link.URL != "" {
|
|
||||||
v0Link.Url = &link.URL
|
|
||||||
}
|
|
||||||
meta.Info.Links = append(meta.Info.Links, v0Link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Info.Screenshots) > 0 {
|
|
||||||
meta.Info.Screenshots = make([]pluginsv0alpha1.MetaV0alpha1InfoScreenshots, 0, len(jsonData.Info.Screenshots))
|
|
||||||
for _, screenshot := range jsonData.Info.Screenshots {
|
|
||||||
v0Screenshot := pluginsv0alpha1.MetaV0alpha1InfoScreenshots{}
|
|
||||||
if screenshot.Name != "" {
|
|
||||||
v0Screenshot.Name = &screenshot.Name
|
|
||||||
}
|
|
||||||
if screenshot.Path != "" {
|
|
||||||
v0Screenshot.Path = &screenshot.Path
|
|
||||||
}
|
|
||||||
meta.Info.Screenshots = append(meta.Info.Screenshots, v0Screenshot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Dependencies
|
|
||||||
meta.Dependencies = pluginsv0alpha1.MetaDependencies{
|
|
||||||
GrafanaDependency: jsonData.Dependencies.GrafanaDependency,
|
|
||||||
}
|
|
||||||
|
|
||||||
if jsonData.Dependencies.GrafanaVersion != "" {
|
|
||||||
meta.Dependencies.GrafanaVersion = &jsonData.Dependencies.GrafanaVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Dependencies.Plugins) > 0 {
|
|
||||||
meta.Dependencies.Plugins = make([]pluginsv0alpha1.MetaV0alpha1DependenciesPlugins, 0, len(jsonData.Dependencies.Plugins))
|
|
||||||
for _, dep := range jsonData.Dependencies.Plugins {
|
|
||||||
var depType pluginsv0alpha1.MetaV0alpha1DependenciesPluginsType
|
|
||||||
switch dep.Type {
|
|
||||||
case "app":
|
|
||||||
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeApp
|
|
||||||
case "datasource":
|
|
||||||
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypeDatasource
|
|
||||||
case "panel":
|
|
||||||
depType = pluginsv0alpha1.MetaV0alpha1DependenciesPluginsTypePanel
|
|
||||||
}
|
|
||||||
meta.Dependencies.Plugins = append(meta.Dependencies.Plugins, pluginsv0alpha1.MetaV0alpha1DependenciesPlugins{
|
|
||||||
Id: dep.ID,
|
|
||||||
Type: depType,
|
|
||||||
Name: dep.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Dependencies.Extensions.ExposedComponents) > 0 {
|
|
||||||
meta.Dependencies.Extensions = &pluginsv0alpha1.MetaV0alpha1DependenciesExtensions{
|
|
||||||
ExposedComponents: jsonData.Dependencies.Extensions.ExposedComponents,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map optional boolean fields
|
|
||||||
if jsonData.Alerting {
|
|
||||||
meta.Alerting = &jsonData.Alerting
|
|
||||||
}
|
|
||||||
if jsonData.Annotations {
|
|
||||||
meta.Annotations = &jsonData.Annotations
|
|
||||||
}
|
|
||||||
if jsonData.AutoEnabled {
|
|
||||||
meta.AutoEnabled = &jsonData.AutoEnabled
|
|
||||||
}
|
|
||||||
if jsonData.Backend {
|
|
||||||
meta.Backend = &jsonData.Backend
|
|
||||||
}
|
|
||||||
if jsonData.BuiltIn {
|
|
||||||
meta.BuiltIn = &jsonData.BuiltIn
|
|
||||||
}
|
|
||||||
if jsonData.HideFromList {
|
|
||||||
meta.HideFromList = &jsonData.HideFromList
|
|
||||||
}
|
|
||||||
if jsonData.Logs {
|
|
||||||
meta.Logs = &jsonData.Logs
|
|
||||||
}
|
|
||||||
if jsonData.Metrics {
|
|
||||||
meta.Metrics = &jsonData.Metrics
|
|
||||||
}
|
|
||||||
if jsonData.MultiValueFilterOperators {
|
|
||||||
meta.MultiValueFilterOperators = &jsonData.MultiValueFilterOperators
|
|
||||||
}
|
|
||||||
if jsonData.Preload {
|
|
||||||
meta.Preload = &jsonData.Preload
|
|
||||||
}
|
|
||||||
if jsonData.SkipDataQuery {
|
|
||||||
meta.SkipDataQuery = &jsonData.SkipDataQuery
|
|
||||||
}
|
|
||||||
if jsonData.Streaming {
|
|
||||||
meta.Streaming = &jsonData.Streaming
|
|
||||||
}
|
|
||||||
if jsonData.Tracing {
|
|
||||||
meta.Tracing = &jsonData.Tracing
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map category
|
|
||||||
if jsonData.Category != "" {
|
|
||||||
var category pluginsv0alpha1.MetaJSONDataCategory
|
|
||||||
switch jsonData.Category {
|
|
||||||
case "tsdb":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryTsdb
|
|
||||||
case "logging":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryLogging
|
|
||||||
case "cloud":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryCloud
|
|
||||||
case "tracing":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryTracing
|
|
||||||
case "profiling":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryProfiling
|
|
||||||
case "sql":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategorySql
|
|
||||||
case "enterprise":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryEnterprise
|
|
||||||
case "iot":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryIot
|
|
||||||
case "other":
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryOther
|
|
||||||
default:
|
|
||||||
category = pluginsv0alpha1.MetaJSONDataCategoryOther
|
|
||||||
}
|
|
||||||
meta.Category = &category
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map state
|
|
||||||
if jsonData.State != "" {
|
|
||||||
var state pluginsv0alpha1.MetaJSONDataState
|
|
||||||
switch jsonData.State {
|
|
||||||
case plugins.ReleaseStateAlpha:
|
|
||||||
state = pluginsv0alpha1.MetaJSONDataStateAlpha
|
|
||||||
case plugins.ReleaseStateBeta:
|
|
||||||
state = pluginsv0alpha1.MetaJSONDataStateBeta
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if state != "" {
|
|
||||||
meta.State = &state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map executable
|
|
||||||
if jsonData.Executable != "" {
|
|
||||||
meta.Executable = &jsonData.Executable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map QueryOptions
|
|
||||||
if len(jsonData.QueryOptions) > 0 {
|
|
||||||
queryOptions := &pluginsv0alpha1.MetaQueryOptions{}
|
|
||||||
if val, ok := jsonData.QueryOptions["maxDataPoints"]; ok {
|
|
||||||
queryOptions.MaxDataPoints = &val
|
|
||||||
}
|
|
||||||
if val, ok := jsonData.QueryOptions["minInterval"]; ok {
|
|
||||||
queryOptions.MinInterval = &val
|
|
||||||
}
|
|
||||||
if val, ok := jsonData.QueryOptions["cacheTimeout"]; ok {
|
|
||||||
queryOptions.CacheTimeout = &val
|
|
||||||
}
|
|
||||||
meta.QueryOptions = queryOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Includes
|
|
||||||
if len(jsonData.Includes) > 0 {
|
|
||||||
meta.Includes = make([]pluginsv0alpha1.MetaInclude, 0, len(jsonData.Includes))
|
|
||||||
for _, include := range jsonData.Includes {
|
|
||||||
v0Include := pluginsv0alpha1.MetaInclude{}
|
|
||||||
if include.UID != "" {
|
|
||||||
v0Include.Uid = &include.UID
|
|
||||||
}
|
|
||||||
if include.Type != "" {
|
|
||||||
var includeType pluginsv0alpha1.MetaIncludeType
|
|
||||||
switch include.Type {
|
|
||||||
case "dashboard":
|
|
||||||
includeType = pluginsv0alpha1.MetaIncludeTypeDashboard
|
|
||||||
case "page":
|
|
||||||
includeType = pluginsv0alpha1.MetaIncludeTypePage
|
|
||||||
case "panel":
|
|
||||||
includeType = pluginsv0alpha1.MetaIncludeTypePanel
|
|
||||||
case "datasource":
|
|
||||||
includeType = pluginsv0alpha1.MetaIncludeTypeDatasource
|
|
||||||
}
|
|
||||||
v0Include.Type = &includeType
|
|
||||||
}
|
|
||||||
if include.Name != "" {
|
|
||||||
v0Include.Name = &include.Name
|
|
||||||
}
|
|
||||||
if include.Component != "" {
|
|
||||||
v0Include.Component = &include.Component
|
|
||||||
}
|
|
||||||
if include.Role != "" {
|
|
||||||
var role pluginsv0alpha1.MetaIncludeRole
|
|
||||||
switch include.Role {
|
|
||||||
case "Admin":
|
|
||||||
role = pluginsv0alpha1.MetaIncludeRoleAdmin
|
|
||||||
case "Editor":
|
|
||||||
role = pluginsv0alpha1.MetaIncludeRoleEditor
|
|
||||||
case "Viewer":
|
|
||||||
role = pluginsv0alpha1.MetaIncludeRoleViewer
|
|
||||||
}
|
|
||||||
v0Include.Role = &role
|
|
||||||
}
|
|
||||||
if include.Action != "" {
|
|
||||||
v0Include.Action = &include.Action
|
|
||||||
}
|
|
||||||
if include.Path != "" {
|
|
||||||
v0Include.Path = &include.Path
|
|
||||||
}
|
|
||||||
if include.AddToNav {
|
|
||||||
v0Include.AddToNav = &include.AddToNav
|
|
||||||
}
|
|
||||||
if include.DefaultNav {
|
|
||||||
v0Include.DefaultNav = &include.DefaultNav
|
|
||||||
}
|
|
||||||
if include.Icon != "" {
|
|
||||||
v0Include.Icon = &include.Icon
|
|
||||||
}
|
|
||||||
meta.Includes = append(meta.Includes, v0Include)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Routes
|
|
||||||
if len(jsonData.Routes) > 0 {
|
|
||||||
meta.Routes = make([]pluginsv0alpha1.MetaRoute, 0, len(jsonData.Routes))
|
|
||||||
for _, route := range jsonData.Routes {
|
|
||||||
v0Route := pluginsv0alpha1.MetaRoute{}
|
|
||||||
if route.Path != "" {
|
|
||||||
v0Route.Path = &route.Path
|
|
||||||
}
|
|
||||||
if route.Method != "" {
|
|
||||||
v0Route.Method = &route.Method
|
|
||||||
}
|
|
||||||
if route.URL != "" {
|
|
||||||
v0Route.Url = &route.URL
|
|
||||||
}
|
|
||||||
if route.ReqRole != "" {
|
|
||||||
reqRole := string(route.ReqRole)
|
|
||||||
v0Route.ReqRole = &reqRole
|
|
||||||
}
|
|
||||||
if route.ReqAction != "" {
|
|
||||||
v0Route.ReqAction = &route.ReqAction
|
|
||||||
}
|
|
||||||
if len(route.Headers) > 0 {
|
|
||||||
headers := make([]string, 0, len(route.Headers))
|
|
||||||
for _, header := range route.Headers {
|
|
||||||
headers = append(headers, header.Name+": "+header.Content)
|
|
||||||
}
|
|
||||||
v0Route.Headers = headers
|
|
||||||
}
|
|
||||||
if len(route.URLParams) > 0 {
|
|
||||||
v0Route.UrlParams = make([]pluginsv0alpha1.MetaV0alpha1RouteUrlParams, 0, len(route.URLParams))
|
|
||||||
for _, param := range route.URLParams {
|
|
||||||
v0Param := pluginsv0alpha1.MetaV0alpha1RouteUrlParams{}
|
|
||||||
if param.Name != "" {
|
|
||||||
v0Param.Name = ¶m.Name
|
|
||||||
}
|
|
||||||
if param.Content != "" {
|
|
||||||
v0Param.Content = ¶m.Content
|
|
||||||
}
|
|
||||||
v0Route.UrlParams = append(v0Route.UrlParams, v0Param)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if route.TokenAuth != nil {
|
|
||||||
v0Route.TokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteTokenAuth{}
|
|
||||||
if route.TokenAuth.Url != "" {
|
|
||||||
v0Route.TokenAuth.Url = &route.TokenAuth.Url
|
|
||||||
}
|
|
||||||
if len(route.TokenAuth.Scopes) > 0 {
|
|
||||||
v0Route.TokenAuth.Scopes = route.TokenAuth.Scopes
|
|
||||||
}
|
|
||||||
if len(route.TokenAuth.Params) > 0 {
|
|
||||||
v0Route.TokenAuth.Params = make(map[string]interface{})
|
|
||||||
for k, v := range route.TokenAuth.Params {
|
|
||||||
v0Route.TokenAuth.Params[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if route.JwtTokenAuth != nil {
|
|
||||||
v0Route.JwtTokenAuth = &pluginsv0alpha1.MetaV0alpha1RouteJwtTokenAuth{}
|
|
||||||
if route.JwtTokenAuth.Url != "" {
|
|
||||||
v0Route.JwtTokenAuth.Url = &route.JwtTokenAuth.Url
|
|
||||||
}
|
|
||||||
if len(route.JwtTokenAuth.Scopes) > 0 {
|
|
||||||
v0Route.JwtTokenAuth.Scopes = route.JwtTokenAuth.Scopes
|
|
||||||
}
|
|
||||||
if len(route.JwtTokenAuth.Params) > 0 {
|
|
||||||
v0Route.JwtTokenAuth.Params = make(map[string]interface{})
|
|
||||||
for k, v := range route.JwtTokenAuth.Params {
|
|
||||||
v0Route.JwtTokenAuth.Params[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(route.Body) > 0 {
|
|
||||||
var bodyMap map[string]interface{}
|
|
||||||
if err := json.Unmarshal(route.Body, &bodyMap); err == nil {
|
|
||||||
v0Route.Body = bodyMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta.Routes = append(meta.Routes, v0Route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Extensions
|
|
||||||
if len(jsonData.Extensions.AddedLinks) > 0 || len(jsonData.Extensions.AddedComponents) > 0 ||
|
|
||||||
len(jsonData.Extensions.ExposedComponents) > 0 || len(jsonData.Extensions.ExtensionPoints) > 0 {
|
|
||||||
extensions := &pluginsv0alpha1.MetaExtensions{}
|
|
||||||
|
|
||||||
if len(jsonData.Extensions.AddedLinks) > 0 {
|
|
||||||
extensions.AddedLinks = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks, 0, len(jsonData.Extensions.AddedLinks))
|
|
||||||
for _, link := range jsonData.Extensions.AddedLinks {
|
|
||||||
v0Link := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedLinks{
|
|
||||||
Targets: link.Targets,
|
|
||||||
Title: link.Title,
|
|
||||||
}
|
|
||||||
if link.Description != "" {
|
|
||||||
v0Link.Description = &link.Description
|
|
||||||
}
|
|
||||||
extensions.AddedLinks = append(extensions.AddedLinks, v0Link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Extensions.AddedComponents) > 0 {
|
|
||||||
extensions.AddedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents, 0, len(jsonData.Extensions.AddedComponents))
|
|
||||||
for _, comp := range jsonData.Extensions.AddedComponents {
|
|
||||||
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsAddedComponents{
|
|
||||||
Targets: comp.Targets,
|
|
||||||
Title: comp.Title,
|
|
||||||
}
|
|
||||||
if comp.Description != "" {
|
|
||||||
v0Comp.Description = &comp.Description
|
|
||||||
}
|
|
||||||
extensions.AddedComponents = append(extensions.AddedComponents, v0Comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Extensions.ExposedComponents) > 0 {
|
|
||||||
extensions.ExposedComponents = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents, 0, len(jsonData.Extensions.ExposedComponents))
|
|
||||||
for _, comp := range jsonData.Extensions.ExposedComponents {
|
|
||||||
v0Comp := pluginsv0alpha1.MetaV0alpha1ExtensionsExposedComponents{
|
|
||||||
Id: comp.Id,
|
|
||||||
}
|
|
||||||
if comp.Title != "" {
|
|
||||||
v0Comp.Title = &comp.Title
|
|
||||||
}
|
|
||||||
if comp.Description != "" {
|
|
||||||
v0Comp.Description = &comp.Description
|
|
||||||
}
|
|
||||||
extensions.ExposedComponents = append(extensions.ExposedComponents, v0Comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jsonData.Extensions.ExtensionPoints) > 0 {
|
|
||||||
extensions.ExtensionPoints = make([]pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints, 0, len(jsonData.Extensions.ExtensionPoints))
|
|
||||||
for _, point := range jsonData.Extensions.ExtensionPoints {
|
|
||||||
v0Point := pluginsv0alpha1.MetaV0alpha1ExtensionsExtensionPoints{
|
|
||||||
Id: point.Id,
|
|
||||||
}
|
|
||||||
if point.Title != "" {
|
|
||||||
v0Point.Title = &point.Title
|
|
||||||
}
|
|
||||||
if point.Description != "" {
|
|
||||||
v0Point.Description = &point.Description
|
|
||||||
}
|
|
||||||
extensions.ExtensionPoints = append(extensions.ExtensionPoints, v0Point)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta.Extensions = extensions
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Roles
|
|
||||||
if len(jsonData.Roles) > 0 {
|
|
||||||
meta.Roles = make([]pluginsv0alpha1.MetaRole, 0, len(jsonData.Roles))
|
|
||||||
for _, role := range jsonData.Roles {
|
|
||||||
v0Role := pluginsv0alpha1.MetaRole{
|
|
||||||
Grants: role.Grants,
|
|
||||||
}
|
|
||||||
if role.Role.Name != "" || role.Role.Description != "" || len(role.Role.Permissions) > 0 {
|
|
||||||
v0RoleRole := &pluginsv0alpha1.MetaV0alpha1RoleRole{}
|
|
||||||
if role.Role.Name != "" {
|
|
||||||
v0RoleRole.Name = &role.Role.Name
|
|
||||||
}
|
|
||||||
if role.Role.Description != "" {
|
|
||||||
v0RoleRole.Description = &role.Role.Description
|
|
||||||
}
|
|
||||||
if len(role.Role.Permissions) > 0 {
|
|
||||||
v0RoleRole.Permissions = make([]pluginsv0alpha1.MetaV0alpha1RoleRolePermissions, 0, len(role.Role.Permissions))
|
|
||||||
for _, perm := range role.Role.Permissions {
|
|
||||||
v0Perm := pluginsv0alpha1.MetaV0alpha1RoleRolePermissions{}
|
|
||||||
if perm.Action != "" {
|
|
||||||
v0Perm.Action = &perm.Action
|
|
||||||
}
|
|
||||||
if perm.Scope != "" {
|
|
||||||
v0Perm.Scope = &perm.Scope
|
|
||||||
}
|
|
||||||
v0RoleRole.Permissions = append(v0RoleRole.Permissions, v0Perm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v0Role.Role = v0RoleRole
|
|
||||||
}
|
|
||||||
meta.Roles = append(meta.Roles, v0Role)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map IAM
|
|
||||||
if jsonData.IAM != nil && len(jsonData.IAM.Permissions) > 0 {
|
|
||||||
iam := &pluginsv0alpha1.MetaIAM{
|
|
||||||
Permissions: make([]pluginsv0alpha1.MetaV0alpha1IAMPermissions, 0, len(jsonData.IAM.Permissions)),
|
|
||||||
}
|
|
||||||
for _, perm := range jsonData.IAM.Permissions {
|
|
||||||
v0Perm := pluginsv0alpha1.MetaV0alpha1IAMPermissions{}
|
|
||||||
if perm.Action != "" {
|
|
||||||
v0Perm.Action = &perm.Action
|
|
||||||
}
|
|
||||||
if perm.Scope != "" {
|
|
||||||
v0Perm.Scope = &perm.Scope
|
|
||||||
}
|
|
||||||
iam.Permissions = append(iam.Permissions, v0Perm)
|
|
||||||
}
|
|
||||||
meta.Iam = iam
|
|
||||||
}
|
|
||||||
|
|
||||||
return meta
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ func TestCoreProvider_GetMeta(t *testing.T) {
|
|||||||
t.Run("returns cached plugin when available", func(t *testing.T) {
|
t.Run("returns cached plugin when available", func(t *testing.T) {
|
||||||
provider := NewCoreProvider()
|
provider := NewCoreProvider()
|
||||||
|
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.mu.Lock()
|
provider.mu.Lock()
|
||||||
@@ -58,10 +60,12 @@ func TestCoreProvider_GetMeta(t *testing.T) {
|
|||||||
t.Run("ignores version parameter", func(t *testing.T) {
|
t.Run("ignores version parameter", func(t *testing.T) {
|
||||||
provider := NewCoreProvider()
|
provider := NewCoreProvider()
|
||||||
|
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.mu.Lock()
|
provider.mu.Lock()
|
||||||
@@ -81,10 +85,12 @@ func TestCoreProvider_GetMeta(t *testing.T) {
|
|||||||
customTTL := 2 * time.Hour
|
customTTL := 2 * time.Hour
|
||||||
provider := NewCoreProviderWithTTL(customTTL)
|
provider := NewCoreProviderWithTTL(customTTL)
|
||||||
|
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.mu.Lock()
|
provider.mu.Lock()
|
||||||
@@ -226,8 +232,8 @@ func TestCoreProvider_loadPlugins(t *testing.T) {
|
|||||||
if loaded {
|
if loaded {
|
||||||
result, err := provider.GetMeta(ctx, "test-datasource", "1.0.0")
|
result, err := provider.GetMeta(ctx, "test-datasource", "1.0.0")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, "test-datasource", result.Meta.Id)
|
assert.Equal(t, "test-datasource", result.Meta.PluginJson.Id)
|
||||||
assert.Equal(t, "Test Datasource", result.Meta.Name)
|
assert.Equal(t, "Test Datasource", result.Meta.PluginJson.Name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
53
apps/plugins/pkg/app/meta/local.go
Normal file
53
apps/plugins/pkg/app/meta/local.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultLocalTTL = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginAssetsCalculator is an interface for calculating plugin asset information.
|
||||||
|
// LocalProvider requires this to calculate loading strategy and module hash.
|
||||||
|
type PluginAssetsCalculator interface {
|
||||||
|
LoadingStrategy(ctx context.Context, p pluginstore.Plugin) plugins.LoadingStrategy
|
||||||
|
ModuleHash(ctx context.Context, p pluginstore.Plugin) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalProvider retrieves plugin metadata for locally installed plugins.
|
||||||
|
// It uses the plugin store to access plugins that have already been loaded.
|
||||||
|
type LocalProvider struct {
|
||||||
|
store pluginstore.Store
|
||||||
|
pluginAssets PluginAssetsCalculator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLocalProvider creates a new LocalProvider for locally installed plugins.
|
||||||
|
// pluginAssets is required for calculating loading strategy and module hash.
|
||||||
|
func NewLocalProvider(pluginStore pluginstore.Store, pluginAssets PluginAssetsCalculator) *LocalProvider {
|
||||||
|
return &LocalProvider{
|
||||||
|
store: pluginStore,
|
||||||
|
pluginAssets: pluginAssets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMeta retrieves plugin metadata for locally installed plugins.
|
||||||
|
func (p *LocalProvider) GetMeta(ctx context.Context, pluginID, version string) (*Result, error) {
|
||||||
|
plugin, exists := p.store.Plugin(ctx, pluginID)
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrMetaNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingStrategy := p.pluginAssets.LoadingStrategy(ctx, plugin)
|
||||||
|
moduleHash := p.pluginAssets.ModuleHash(ctx, plugin)
|
||||||
|
|
||||||
|
spec := pluginStorePluginToMeta(plugin, loadingStrategy, moduleHash)
|
||||||
|
return &Result{
|
||||||
|
Meta: spec,
|
||||||
|
TTL: defaultLocalTTL,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ const (
|
|||||||
|
|
||||||
// cachedMeta represents a cached metadata entry with expiration time
|
// cachedMeta represents a cached metadata entry with expiration time
|
||||||
type cachedMeta struct {
|
type cachedMeta struct {
|
||||||
meta pluginsv0alpha1.MetaJSONData
|
meta pluginsv0alpha1.MetaSpec
|
||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
expiresAt time.Time
|
expiresAt time.Time
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ func (pm *ProviderManager) GetMeta(ctx context.Context, pluginID, version string
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
// Don't cache results with a zero TTL
|
// Don't cache results with a zero TTL
|
||||||
if result.TTL == 0 {
|
if result.TTL == 0 {
|
||||||
continue
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.cacheMu.Lock()
|
pm.cacheMu.Lock()
|
||||||
|
|||||||
@@ -35,10 +35,12 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
t.Run("returns cached result when available and not expired", func(t *testing.T) {
|
t.Run("returns cached result when available and not expired", func(t *testing.T) {
|
||||||
cachedMeta := pluginsv0alpha1.MetaJSONData{
|
cachedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := &mockProvider{
|
provider := &mockProvider{
|
||||||
@@ -60,8 +62,10 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
|
|
||||||
provider.getMetaFunc = func(ctx context.Context, pluginID, version string) (*Result, error) {
|
provider.getMetaFunc = func(ctx context.Context, pluginID, version string) (*Result, error) {
|
||||||
return &Result{
|
return &Result{
|
||||||
Meta: pluginsv0alpha1.MetaJSONData{Id: "different"},
|
Meta: pluginsv0alpha1.MetaSpec{
|
||||||
TTL: time.Hour,
|
PluginJson: pluginsv0alpha1.MetaJSONData{Id: "different"},
|
||||||
|
},
|
||||||
|
TTL: time.Hour,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,10 +77,12 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fetches from provider when not cached", func(t *testing.T) {
|
t.Run("fetches from provider when not cached", func(t *testing.T) {
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
expectedTTL := 2 * time.Hour
|
expectedTTL := 2 * time.Hour
|
||||||
|
|
||||||
@@ -107,19 +113,16 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
assert.Equal(t, expectedTTL, cached.ttl)
|
assert.Equal(t, expectedTTL, cached.ttl)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("does not cache result with zero TTL and tries next provider", func(t *testing.T) {
|
t.Run("does not cache result with zero TTL", func(t *testing.T) {
|
||||||
zeroTTLMeta := pluginsv0alpha1.MetaJSONData{
|
zeroTTLMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Zero TTL Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Zero TTL Plugin",
|
||||||
}
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
},
|
||||||
Id: "test-plugin",
|
|
||||||
Name: "Test Plugin",
|
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider1 := &mockProvider{
|
provider := &mockProvider{
|
||||||
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
|
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
|
||||||
return &Result{
|
return &Result{
|
||||||
Meta: zeroTTLMeta,
|
Meta: zeroTTLMeta,
|
||||||
@@ -127,37 +130,30 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
provider2 := &mockProvider{
|
|
||||||
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
|
|
||||||
return &Result{
|
|
||||||
Meta: expectedMeta,
|
|
||||||
TTL: time.Hour,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pm := NewProviderManager(provider1, provider2)
|
pm := NewProviderManager(provider)
|
||||||
|
|
||||||
result, err := pm.GetMeta(ctx, "test-plugin", "1.0.0")
|
result, err := pm.GetMeta(ctx, "test-plugin", "1.0.0")
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, result)
|
require.NotNil(t, result)
|
||||||
assert.Equal(t, expectedMeta, result.Meta)
|
assert.Equal(t, zeroTTLMeta, result.Meta)
|
||||||
|
assert.Equal(t, time.Duration(0), result.TTL)
|
||||||
|
|
||||||
pm.cacheMu.RLock()
|
pm.cacheMu.RLock()
|
||||||
cached, exists := pm.cache["test-plugin:1.0.0"]
|
_, exists := pm.cache["test-plugin:1.0.0"]
|
||||||
pm.cacheMu.RUnlock()
|
pm.cacheMu.RUnlock()
|
||||||
|
|
||||||
assert.True(t, exists)
|
assert.False(t, exists, "zero TTL results should not be cached")
|
||||||
assert.Equal(t, expectedMeta, cached.meta)
|
|
||||||
assert.Equal(t, time.Hour, cached.ttl)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("tries next provider when first returns ErrMetaNotFound", func(t *testing.T) {
|
t.Run("tries next provider when first returns ErrMetaNotFound", func(t *testing.T) {
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider1 := &mockProvider{
|
provider1 := &mockProvider{
|
||||||
@@ -229,15 +225,19 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("skips expired cache entries", func(t *testing.T) {
|
t.Run("skips expired cache entries", func(t *testing.T) {
|
||||||
expiredMeta := pluginsv0alpha1.MetaJSONData{
|
expiredMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Expired Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Expired Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
expectedMeta := pluginsv0alpha1.MetaJSONData{
|
expectedMeta := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Test Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Test Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
callCount := 0
|
callCount := 0
|
||||||
@@ -272,15 +272,19 @@ func TestProviderManager_GetMeta(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uses first successful provider", func(t *testing.T) {
|
t.Run("uses first successful provider", func(t *testing.T) {
|
||||||
expectedMeta1 := pluginsv0alpha1.MetaJSONData{
|
expectedMeta1 := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Provider 1 Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Provider 1 Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
expectedMeta2 := pluginsv0alpha1.MetaJSONData{
|
expectedMeta2 := pluginsv0alpha1.MetaSpec{
|
||||||
Id: "test-plugin",
|
PluginJson: pluginsv0alpha1.MetaJSONData{
|
||||||
Name: "Provider 2 Plugin",
|
Id: "test-plugin",
|
||||||
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
Name: "Provider 2 Plugin",
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider1 := &mockProvider{
|
provider1 := &mockProvider{
|
||||||
@@ -331,9 +335,9 @@ func TestProviderManager_Run(t *testing.T) {
|
|||||||
|
|
||||||
func TestProviderManager_cleanupExpired(t *testing.T) {
|
func TestProviderManager_cleanupExpired(t *testing.T) {
|
||||||
t.Run("removes expired entries", func(t *testing.T) {
|
t.Run("removes expired entries", func(t *testing.T) {
|
||||||
validMeta := pluginsv0alpha1.MetaJSONData{Id: "valid"}
|
validMeta := pluginsv0alpha1.MetaSpec{PluginJson: pluginsv0alpha1.MetaJSONData{Id: "valid"}}
|
||||||
expiredMeta1 := pluginsv0alpha1.MetaJSONData{Id: "expired1"}
|
expiredMeta1 := pluginsv0alpha1.MetaSpec{PluginJson: pluginsv0alpha1.MetaJSONData{Id: "expired1"}}
|
||||||
expiredMeta2 := pluginsv0alpha1.MetaJSONData{Id: "expired2"}
|
expiredMeta2 := pluginsv0alpha1.MetaSpec{PluginJson: pluginsv0alpha1.MetaJSONData{Id: "expired2"}}
|
||||||
|
|
||||||
provider := &mockProvider{
|
provider := &mockProvider{
|
||||||
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
|
getMetaFunc: func(ctx context.Context, pluginID, version string) (*Result, error) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ var (
|
|||||||
|
|
||||||
// Result contains plugin metadata along with its recommended TTL.
|
// Result contains plugin metadata along with its recommended TTL.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Meta pluginsv0alpha1.MetaJSONData
|
Meta pluginsv0alpha1.MetaSpec
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,8 +121,19 @@ func (s *MetaStorage) List(ctx context.Context, options *internalversion.ListOpt
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginMeta := createMetaFromMetaJSONData(result.Meta, plugin.Name, plugin.Namespace)
|
pluginMeta := pluginsv0alpha1.Meta{
|
||||||
metaItems = append(metaItems, *pluginMeta)
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: plugin.Name,
|
||||||
|
Namespace: plugin.Namespace,
|
||||||
|
},
|
||||||
|
Spec: result.Meta,
|
||||||
|
}
|
||||||
|
pluginMeta.SetGroupVersionKind(schema.GroupVersionKind{
|
||||||
|
Group: pluginsv0alpha1.APIGroup,
|
||||||
|
Version: pluginsv0alpha1.APIVersion,
|
||||||
|
Kind: pluginsv0alpha1.MetaKind().Kind(),
|
||||||
|
})
|
||||||
|
metaItems = append(metaItems, pluginMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
list := &pluginsv0alpha1.MetaList{
|
list := &pluginsv0alpha1.MetaList{
|
||||||
@@ -169,27 +180,18 @@ func (s *MetaStorage) Get(ctx context.Context, name string, options *metav1.GetO
|
|||||||
return nil, apierrors.NewInternalError(fmt.Errorf("failed to fetch plugin metadata: %w", err))
|
return nil, apierrors.NewInternalError(fmt.Errorf("failed to fetch plugin metadata: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return createMetaFromMetaJSONData(result.Meta, name, ns.Value), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createMetaFromMetaJSONData creates a Meta k8s object from MetaJSONData and plugin metadata.
|
|
||||||
func createMetaFromMetaJSONData(pluginJSON pluginsv0alpha1.MetaJSONData, name, namespace string) *pluginsv0alpha1.Meta {
|
|
||||||
pluginMeta := &pluginsv0alpha1.Meta{
|
pluginMeta := &pluginsv0alpha1.Meta{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: plugin.Name,
|
||||||
Namespace: namespace,
|
Namespace: plugin.Namespace,
|
||||||
},
|
|
||||||
Spec: pluginsv0alpha1.MetaSpec{
|
|
||||||
PluginJSON: pluginJSON,
|
|
||||||
},
|
},
|
||||||
|
Spec: result.Meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the GroupVersionKind
|
|
||||||
pluginMeta.SetGroupVersionKind(schema.GroupVersionKind{
|
pluginMeta.SetGroupVersionKind(schema.GroupVersionKind{
|
||||||
Group: pluginsv0alpha1.APIGroup,
|
Group: pluginsv0alpha1.APIGroup,
|
||||||
Version: pluginsv0alpha1.APIVersion,
|
Version: pluginsv0alpha1.APIVersion,
|
||||||
Kind: pluginsv0alpha1.MetaKind().Kind(),
|
Kind: pluginsv0alpha1.MetaKind().Kind(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return pluginMeta
|
return pluginMeta, nil
|
||||||
}
|
}
|
||||||
|
|||||||
249
apps/plugins/pkg/app/storage_test.go
Normal file
249
apps/plugins/pkg/app/storage_test.go
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-app-sdk/resource"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
|
||||||
|
pluginsv0alpha1 "github.com/grafana/grafana/apps/plugins/pkg/apis/plugins/v0alpha1"
|
||||||
|
"github.com/grafana/grafana/apps/plugins/pkg/app/meta"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetaStorageListPreload(t *testing.T) {
|
||||||
|
ctx := request.WithNamespace(context.Background(), "default")
|
||||||
|
|
||||||
|
preloadPlugin := pluginstore.Plugin{
|
||||||
|
JSONData: plugins.JSONData{
|
||||||
|
ID: "test-plugin",
|
||||||
|
Name: "Test Plugin",
|
||||||
|
Type: plugins.TypeDataSource,
|
||||||
|
Info: plugins.Info{Version: "1.0.0"},
|
||||||
|
Preload: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nonPreloadPlugin := pluginstore.Plugin{
|
||||||
|
JSONData: plugins.JSONData{
|
||||||
|
ID: "test-plugin-2",
|
||||||
|
Name: "Test Plugin 2",
|
||||||
|
Type: plugins.TypeDataSource,
|
||||||
|
Info: plugins.Info{Version: "1.0.0"},
|
||||||
|
Preload: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
store := &mockPluginStore{plugins: map[string]pluginstore.Plugin{
|
||||||
|
"test-plugin": preloadPlugin,
|
||||||
|
}}
|
||||||
|
store2 := &mockPluginStore{plugins: map[string]pluginstore.Plugin{
|
||||||
|
"test-plugin-2": nonPreloadPlugin,
|
||||||
|
}}
|
||||||
|
catalogServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
require.Equal(t, http.MethodGet, r.Method)
|
||||||
|
require.Equal(t, "application/json", r.Header.Get("Accept"))
|
||||||
|
require.Equal(t, "grafana-plugins-app", r.Header.Get("User-Agent"))
|
||||||
|
|
||||||
|
segments := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
|
||||||
|
require.Len(t, segments, 5)
|
||||||
|
require.Equal(t, "api", segments[0])
|
||||||
|
require.Equal(t, "plugins", segments[1])
|
||||||
|
require.Equal(t, "versions", segments[3])
|
||||||
|
|
||||||
|
preload := true
|
||||||
|
response := struct {
|
||||||
|
PluginID string `json:"pluginSlug"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
JSON pluginsv0alpha1.MetaJSONData `json:"json"`
|
||||||
|
}{
|
||||||
|
PluginID: segments[2],
|
||||||
|
Version: segments[4],
|
||||||
|
JSON: pluginsv0alpha1.MetaJSONData{
|
||||||
|
Id: segments[2],
|
||||||
|
Name: segments[2],
|
||||||
|
Type: pluginsv0alpha1.MetaJSONDataTypeDatasource,
|
||||||
|
Preload: &preload,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
require.NoError(t, json.NewEncoder(w).Encode(response))
|
||||||
|
}))
|
||||||
|
defer catalogServer.Close()
|
||||||
|
provider := meta.NewLocalProvider(store, mockPluginAssets{})
|
||||||
|
provider2 := meta.NewLocalProvider(store2, mockPluginAssets{})
|
||||||
|
catalogProvider := meta.NewCatalogProvider(catalogServer.URL + "/api/plugins")
|
||||||
|
metaManager := meta.NewProviderManager(provider2, provider, catalogProvider)
|
||||||
|
|
||||||
|
pluginClient := pluginsv0alpha1.NewPluginClient(&mockResourceClient{
|
||||||
|
listFunc: func(ctx context.Context, namespace string, opts resource.ListOptions) (resource.ListObject, error) {
|
||||||
|
return newPluginList(), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
storage := NewMetaStorage(metaManager, func(ctx context.Context) (*pluginsv0alpha1.PluginClient, error) {
|
||||||
|
return pluginClient, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
obj, err := storage.List(ctx, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
metaList, ok := obj.(*pluginsv0alpha1.MetaList)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Len(t, metaList.Items, 3)
|
||||||
|
|
||||||
|
require.NotNil(t, metaList.Items[0].Spec.PluginJson.Preload)
|
||||||
|
require.True(t, *metaList.Items[0].Spec.PluginJson.Preload)
|
||||||
|
require.NotNil(t, metaList.Items[1].Spec.PluginJson.Preload)
|
||||||
|
require.True(t, *metaList.Items[1].Spec.PluginJson.Preload)
|
||||||
|
require.Nil(t, metaList.Items[2].Spec.PluginJson.Preload)
|
||||||
|
|
||||||
|
obj, err = storage.List(ctx, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
metaList, ok = obj.(*pluginsv0alpha1.MetaList)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Len(t, metaList.Items, 3)
|
||||||
|
require.NotNil(t, metaList.Items[0].Spec.PluginJson.Preload)
|
||||||
|
require.True(t, *metaList.Items[0].Spec.PluginJson.Preload)
|
||||||
|
require.NotNil(t, metaList.Items[1].Spec.PluginJson.Preload)
|
||||||
|
require.True(t, *metaList.Items[1].Spec.PluginJson.Preload)
|
||||||
|
require.Nil(t, metaList.Items[2].Spec.PluginJson.Preload)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockPluginAssets struct{}
|
||||||
|
|
||||||
|
func (mockPluginAssets) LoadingStrategy(ctx context.Context, p pluginstore.Plugin) plugins.LoadingStrategy {
|
||||||
|
return plugins.LoadingStrategyFetch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mockPluginAssets) ModuleHash(ctx context.Context, p pluginstore.Plugin) string {
|
||||||
|
return "hash"
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockPluginStore struct {
|
||||||
|
plugins map[string]pluginstore.Plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockPluginStore) Plugin(ctx context.Context, pluginID string) (pluginstore.Plugin, bool) {
|
||||||
|
if m.plugins[pluginID].ID != pluginID {
|
||||||
|
return pluginstore.Plugin{}, false
|
||||||
|
}
|
||||||
|
return m.plugins[pluginID], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockPluginStore) Plugins(ctx context.Context, pluginTypes ...plugins.Type) []pluginstore.Plugin {
|
||||||
|
result := []pluginstore.Plugin{}
|
||||||
|
for _, plugin := range m.plugins {
|
||||||
|
if len(pluginTypes) == 0 || slices.Contains(pluginTypes, plugin.Type) {
|
||||||
|
result = append(result, plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPluginList() *pluginsv0alpha1.PluginList {
|
||||||
|
return &pluginsv0alpha1.PluginList{
|
||||||
|
Items: []pluginsv0alpha1.Plugin{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "grafana-plugins-app", Namespace: "org-1"},
|
||||||
|
Spec: pluginsv0alpha1.PluginSpec{Id: "grafana-plugins-app", Version: "1.0.0"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test-plugin", Namespace: "org-1"},
|
||||||
|
Spec: pluginsv0alpha1.PluginSpec{Id: "test-plugin", Version: "1.0.0"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test-plugin-2", Namespace: "org-1"},
|
||||||
|
Spec: pluginsv0alpha1.PluginSpec{Id: "test-plugin-2", Version: "1.0.0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockResourceClient struct {
|
||||||
|
listFunc func(ctx context.Context, namespace string, opts resource.ListOptions) (resource.ListObject, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (resource.ListObject, error) {
|
||||||
|
if m.listFunc != nil {
|
||||||
|
return m.listFunc(ctx, namespace, opts)
|
||||||
|
}
|
||||||
|
return &pluginsv0alpha1.PluginList{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) ListInto(ctx context.Context, namespace string, opts resource.ListOptions, into resource.ListObject) error {
|
||||||
|
list, err := m.List(ctx, namespace, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if src, ok := list.(*pluginsv0alpha1.PluginList); ok {
|
||||||
|
if dst, ok := into.(*pluginsv0alpha1.PluginList); ok {
|
||||||
|
*dst = *src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Get(ctx context.Context, identifier resource.Identifier) (resource.Object, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) GetInto(ctx context.Context, identifier resource.Identifier, into resource.Object) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Create(ctx context.Context, identifier resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) CreateInto(ctx context.Context, identifier resource.Identifier, obj resource.Object, opts resource.CreateOptions, into resource.Object) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Update(ctx context.Context, identifier resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) UpdateInto(ctx context.Context, identifier resource.Identifier, obj resource.Object, opts resource.UpdateOptions, into resource.Object) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Patch(ctx context.Context, identifier resource.Identifier, patch resource.PatchRequest, opts resource.PatchOptions) (resource.Object, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) PatchInto(ctx context.Context, identifier resource.Identifier, patch resource.PatchRequest, opts resource.PatchOptions, into resource.Object) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) SubresourceRequest(ctx context.Context, identifier resource.Identifier, req resource.CustomRouteRequestOptions) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResourceClient) Watch(ctx context.Context, namespace string, opts resource.WatchOptions) (resource.WatchResponse, error) {
|
||||||
|
return &mockWatchResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockWatchResponse struct{}
|
||||||
|
|
||||||
|
func (m *mockWatchResponse) Stop() {}
|
||||||
|
|
||||||
|
func (m *mockWatchResponse) WatchEvents() <-chan resource.WatchEvent {
|
||||||
|
ch := make(chan resource.WatchEvent)
|
||||||
|
close(ch)
|
||||||
|
return ch
|
||||||
|
}
|
||||||
@@ -9,9 +9,13 @@ import (
|
|||||||
|
|
||||||
pluginsapp "github.com/grafana/grafana/apps/plugins/pkg/app"
|
pluginsapp "github.com/grafana/grafana/apps/plugins/pkg/app"
|
||||||
"github.com/grafana/grafana/apps/plugins/pkg/app/meta"
|
"github.com/grafana/grafana/apps/plugins/pkg/app/meta"
|
||||||
|
"github.com/grafana/grafana/pkg/configprovider"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||||
"github.com/grafana/grafana/pkg/services/apiserver/appinstaller"
|
"github.com/grafana/grafana/pkg/services/apiserver/appinstaller"
|
||||||
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets"
|
||||||
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,10 +24,20 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AppInstaller struct {
|
type AppInstaller struct {
|
||||||
|
metaManager *meta.ProviderManager
|
||||||
|
cfgProvider configprovider.ConfigProvider
|
||||||
|
restConfigProvider apiserver.RestConfigProvider
|
||||||
|
|
||||||
*pluginsapp.PluginAppInstaller
|
*pluginsapp.PluginAppInstaller
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideAppInstaller(accessControlService accesscontrol.Service, accessClient authlib.AccessClient) (*AppInstaller, error) {
|
func ProvideAppInstaller(
|
||||||
|
cfgProvider configprovider.ConfigProvider,
|
||||||
|
restConfigProvider apiserver.RestConfigProvider,
|
||||||
|
pluginStore pluginstore.Store,
|
||||||
|
pluginAssetsService *pluginassets.Service,
|
||||||
|
accessControlService accesscontrol.Service, accessClient authlib.AccessClient,
|
||||||
|
) (*AppInstaller, error) {
|
||||||
if err := registerAccessControlRoles(accessControlService); err != nil {
|
if err := registerAccessControlRoles(accessControlService); err != nil {
|
||||||
return nil, fmt.Errorf("registering access control roles: %w", err)
|
return nil, fmt.Errorf("registering access control roles: %w", err)
|
||||||
}
|
}
|
||||||
@@ -35,8 +49,8 @@ func ProvideAppInstaller(accessControlService accesscontrol.Service, accessClien
|
|||||||
|
|
||||||
coreProvider := meta.NewCoreProvider()
|
coreProvider := meta.NewCoreProvider()
|
||||||
cloudProvider := meta.NewCatalogProvider(grafanaComAPIURL)
|
cloudProvider := meta.NewCatalogProvider(grafanaComAPIURL)
|
||||||
metaProviderManager := meta.NewProviderManager(coreProvider, cloudProvider)
|
localProvider := meta.NewLocalProvider(pluginStore, pluginAssetsService)
|
||||||
|
metaProviderManager := meta.NewProviderManager(localProvider, coreProvider, cloudProvider)
|
||||||
authorizer := grafanaauthorizer.NewResourceAuthorizer(accessClient)
|
authorizer := grafanaauthorizer.NewResourceAuthorizer(accessClient)
|
||||||
i, err := pluginsapp.ProvideAppInstaller(authorizer, metaProviderManager)
|
i, err := pluginsapp.ProvideAppInstaller(authorizer, metaProviderManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -44,6 +58,9 @@ func ProvideAppInstaller(accessControlService accesscontrol.Service, accessClien
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &AppInstaller{
|
return &AppInstaller{
|
||||||
|
metaManager: metaProviderManager,
|
||||||
|
cfgProvider: cfgProvider,
|
||||||
|
restConfigProvider: restConfigProvider,
|
||||||
PluginAppInstaller: i,
|
PluginAppInstaller: i,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package appregistry
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/historian"
|
"github.com/grafana/grafana/pkg/registry/apps/alerting/historian"
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||||
@@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/plugins"
|
"github.com/grafana/grafana/pkg/registry/apps/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/registry/apps/quotas"
|
||||||
"github.com/grafana/grafana/pkg/registry/apps/shorturl"
|
"github.com/grafana/grafana/pkg/registry/apps/shorturl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
4
pkg/server/wire_gen.go
generated
4
pkg/server/wire_gen.go
generated
@@ -784,7 +784,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
appInstaller, err := plugins.ProvideAppInstaller(acimplService, accessClient)
|
appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, pluginassetsService, acimplService, accessClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1442,7 +1442,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
appInstaller, err := plugins.ProvideAppInstaller(acimplService, accessClient)
|
appInstaller, err := plugins.ProvideAppInstaller(configProvider, eventualRestConfigProvider, pluginstoreService, pluginassetsService, acimplService, accessClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Plugin struct {
|
|||||||
|
|
||||||
// App fields
|
// App fields
|
||||||
Parent *ParentPlugin
|
Parent *ParentPlugin
|
||||||
|
Children []string
|
||||||
IncludedInAppID string
|
IncludedInAppID string
|
||||||
DefaultNavURL string
|
DefaultNavURL string
|
||||||
Pinned bool
|
Pinned bool
|
||||||
@@ -85,6 +86,18 @@ func ToGrafanaDTO(p *plugins.Plugin) Plugin {
|
|||||||
dto.Parent = &ParentPlugin{ID: p.Parent.ID}
|
dto.Parent = &ParentPlugin{ID: p.Parent.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(p.Children) > 0 {
|
||||||
|
children := make([]string, 0, len(p.Children))
|
||||||
|
for _, child := range p.Children {
|
||||||
|
if child != nil {
|
||||||
|
children = append(children, child.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(children) > 0 {
|
||||||
|
dto.Children = children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return dto
|
return dto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ func TestIntegrationPluginMeta(t *testing.T) {
|
|||||||
|
|
||||||
foundIDs := make(map[string]bool)
|
foundIDs := make(map[string]bool)
|
||||||
for _, item := range response.Result.Items {
|
for _, item := range response.Result.Items {
|
||||||
require.NotNil(t, item.Spec.PluginJSON)
|
require.NotNil(t, item.Spec.PluginJson)
|
||||||
foundIDs[item.Spec.PluginJSON.Id] = true
|
foundIDs[item.Spec.PluginJson.Id] = true
|
||||||
require.NotEmpty(t, item.Spec.PluginJSON.Id)
|
require.NotEmpty(t, item.Spec.PluginJson.Id)
|
||||||
require.NotEmpty(t, item.Spec.PluginJSON.Type)
|
require.NotEmpty(t, item.Spec.PluginJson.Type)
|
||||||
require.NotEmpty(t, item.Spec.PluginJSON.Name)
|
require.NotEmpty(t, item.Spec.PluginJson.Name)
|
||||||
}
|
}
|
||||||
require.True(t, foundIDs["grafana-piechart-panel"])
|
require.True(t, foundIDs["grafana-piechart-panel"])
|
||||||
require.True(t, foundIDs["grafana-clock-panel"])
|
require.True(t, foundIDs["grafana-clock-panel"])
|
||||||
@@ -109,10 +109,10 @@ func TestIntegrationPluginMeta(t *testing.T) {
|
|||||||
}, &pluginsv0alpha1.Meta{})
|
}, &pluginsv0alpha1.Meta{})
|
||||||
|
|
||||||
require.NotNil(t, response.Result)
|
require.NotNil(t, response.Result)
|
||||||
require.NotNil(t, response.Result.Spec.PluginJSON)
|
require.NotNil(t, response.Result.Spec.PluginJson)
|
||||||
require.Equal(t, "grafana-piechart-panel", response.Result.Spec.PluginJSON.Id)
|
require.Equal(t, "grafana-piechart-panel", response.Result.Spec.PluginJson.Id)
|
||||||
require.NotEmpty(t, response.Result.Spec.PluginJSON.Name)
|
require.NotEmpty(t, response.Result.Spec.PluginJson.Name)
|
||||||
require.NotEmpty(t, response.Result.Spec.PluginJSON.Type)
|
require.NotEmpty(t, response.Result.Spec.PluginJson.Type)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get plugin meta for non-existent plugin", func(t *testing.T) {
|
t.Run("get plugin meta for non-existent plugin", func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user