Chore: Update to the latest grafana-plugin-sdk-go and more swagger fixes (#52445)

* Fix get legacy alert response

* Swagger: Fix get folder by UID response

* Fix conflicting swagger model Alert

Reanme legacy alerting swagger model to LegacyAlert to differentiate it
from the prometheus Alert

* Bump grafana-plugin-sdk-go

* Fix get folder response

* Use go-swagger command for merging the specifications and remove merge_specs script
This commit is contained in:
Sofia Papagiannaki 2022-07-20 16:09:42 +03:00 committed by GitHub
parent d3d8fdd878
commit f7c5eceb21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 916 additions and 962 deletions

View File

@ -39,8 +39,9 @@ NGALERT_SPEC_TARGET = pkg/services/ngalert/api/tooling/api.json
$(NGALERT_SPEC_TARGET):
+$(MAKE) -C pkg/services/ngalert/api/tooling api.json
$(MERGED_SPEC_TARGET): $(SPEC_TARGET) $(NGALERT_SPEC_TARGET) ## Merge generated and ngalert API specs
go run pkg/api/docs/merge/merge_specs.go -o=$(MERGED_SPEC_TARGET) $(<) $(NGALERT_SPEC_TARGET)
$(MERGED_SPEC_TARGET): $(SPEC_TARGET) $(NGALERT_SPEC_TARGET) $(SWAGGER) ## Merge generated and ngalert API specs
# known conflicts DsPermissionType, AddApiKeyCommand, Json, Duration (identical models referenced by both specs)
$(SWAGGER) mixin $(SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET)
--swagger-api-spec: $(API_DEFINITION_FILES) $(SWAGGER) ## Generate API Swagger specification
SWAGGER_GENERATE_EXTENSION=false $(SWAGGER) generate spec -m -w pkg/server -o public/api-spec.json \

2
go.mod
View File

@ -54,7 +54,7 @@ require (
github.com/grafana/cuetsy v0.0.3
github.com/grafana/grafana-aws-sdk v0.10.7
github.com/grafana/grafana-azure-sdk-go v1.3.0
github.com/grafana/grafana-plugin-sdk-go v0.138.0
github.com/grafana/grafana-plugin-sdk-go v0.139.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/hashicorp/go-hclog v1.0.0
github.com/hashicorp/go-plugin v1.4.3

2
go.sum
View File

@ -1345,6 +1345,8 @@ github.com/grafana/grafana-google-sdk-go v0.0.0-20211104130251-b190293eaf58/go.m
github.com/grafana/grafana-plugin-sdk-go v0.114.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
github.com/grafana/grafana-plugin-sdk-go v0.138.0 h1:uJWNwHL4RoQF3axoi3RDSwoNu/KHy5F+5dnrK0fx5yc=
github.com/grafana/grafana-plugin-sdk-go v0.138.0/go.mod h1:Y+Ps2sesZ62AyCnX+hzrYnyDQYe/ZZl+A8yKLOBm12c=
github.com/grafana/grafana-plugin-sdk-go v0.139.0 h1:2RQKM2QpSaWTtaGN6sK+R7LO7zykOeTYF0QkAMA7JsI=
github.com/grafana/grafana-plugin-sdk-go v0.139.0/go.mod h1:Y+Ps2sesZ62AyCnX+hzrYnyDQYe/ZZl+A8yKLOBm12c=
github.com/grafana/saml v0.0.0-20211007135653-aed1b2edd86b h1:YiSGp34F4V0G08HHx1cJBf2GVgwYAkXQjzuVs1t8jYk=
github.com/grafana/saml v0.0.0-20211007135653-aed1b2edd86b/go.mod h1:q83kyQoMD0vhy+RzFLlbw0UgHJ6TAihQpuXvdFmm4s4=
github.com/grafana/thema v0.0.0-20220523183731-72aebd14e751 h1:5PpsfN52XA0hxOjD/qQ0QNiEkp9Y9Tb+yz/Hj9fyL4M=

View File

@ -22,7 +22,7 @@ import (
// Get folder by uid.
//
// Responses:
// 200:
// 200: folderResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError

View File

@ -148,7 +148,7 @@ type GetAlertsResponse struct {
type GetAlertResponse struct {
// The response message
// in: body
Body []*models.Alert `json:"body"`
Body *models.Alert `json:"body"`
}
// swagger:response pauseAlertResponse

View File

@ -65,7 +65,7 @@ import (
// Get Team Members.
//
// Responses:
// 200: okResponse
// 200: getTeamMembersResponse
// 401: unauthorisedError
// 403: forbiddenError
// 404: notFoundError

View File

@ -1,147 +0,0 @@
package main
import (
"bytes"
_ "embed"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"reflect"
"strings"
"github.com/go-openapi/loads"
"github.com/go-openapi/spec"
)
func mergeVectors(a, b []string) []string {
for _, p := range b {
exist := false
for _, op := range a {
if op == p {
exist = true
break
}
}
if !exist {
a = append(a, p)
}
}
return a
}
func compareDefinition(a, b spec.Schema) bool {
return reflect.DeepEqual(a.Type, b.Type) && a.Format == b.Format && reflect.DeepEqual(a.Properties, b.Properties)
}
// mergeSpecs merges OSS API spec with one or more other OpenAPI specs
func mergeSpecs(output string, sources ...string) error {
if len(sources) < 2 {
return fmt.Errorf("no APIs to merge")
}
f, err := os.Open(sources[0])
if err != nil {
return err
}
specData, err := ioutil.ReadAll(f)
if err != nil {
return err
}
var specOSS spec.Swagger
if err := json.Unmarshal(specData, &specOSS); err != nil {
return fmt.Errorf("failed to unmarshal original spec: %w", err)
}
for _, s := range sources[1:] {
additionalSpec, err := loads.JSONSpec(s)
if err != nil {
return fmt.Errorf("failed to load spec from: %s: %w", s, err)
}
// Merge consumes
specOSS.SwaggerProps.Consumes = mergeVectors(specOSS.SwaggerProps.Consumes, additionalSpec.OrigSpec().Consumes)
// Merge produces
specOSS.SwaggerProps.Produces = mergeVectors(specOSS.SwaggerProps.Produces, additionalSpec.OrigSpec().Produces)
// Merge schemes
specOSS.SwaggerProps.Schemes = mergeVectors(specOSS.SwaggerProps.Schemes, additionalSpec.OrigSpec().Schemes)
//TODO: When there are conflict between definitions, we need to error out, but here we need to fix the existing conflict first
// there are false positives, we will have to fix those by regenerate alerting api spec
for k, ad := range additionalSpec.OrigSpec().SwaggerProps.Definitions {
if ossd, exists := specOSS.SwaggerProps.Definitions[k]; exists {
if !compareDefinition(ad, ossd) {
fmt.Printf("the definition of %s differs in specs!\n", k)
}
}
specOSS.SwaggerProps.Definitions[k] = ad
}
for k, ar := range additionalSpec.OrigSpec().SwaggerProps.Responses {
if ossr, exists := specOSS.SwaggerProps.Responses[k]; exists {
if !reflect.DeepEqual(ar, ossr) {
fmt.Printf("the definition of response %s differs in specs!\n", k)
}
}
specOSS.SwaggerProps.Responses[k] = ar
}
for k, p := range additionalSpec.OrigSpec().SwaggerProps.Parameters {
specOSS.SwaggerProps.Parameters[k] = p
}
paths := additionalSpec.OrigSpec().SwaggerProps.Paths
if paths != nil {
for k, pi := range paths.Paths {
kk := strings.TrimPrefix(k, specOSS.BasePath) // remove base path if exists
if specOSS.SwaggerProps.Paths == nil {
specOSS.SwaggerProps.Paths = &spec.Paths{
Paths: make(map[string]spec.PathItem),
}
}
specOSS.SwaggerProps.Paths.Paths[kk] = pi
}
}
specOSS.SwaggerProps.Tags = append(specOSS.SwaggerProps.Tags, additionalSpec.OrigSpec().SwaggerProps.Tags...)
}
// write result to file
newSpec, err := specOSS.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to marshal result spec: %w", err)
}
var prettyJSON bytes.Buffer
err = json.Indent(&prettyJSON, newSpec, "", "\t")
if err != nil {
return fmt.Errorf("failed to intend new spec: %w", err)
}
f, err = os.Create(output)
if err != nil {
return fmt.Errorf("failed to create file for new spec: %w", err)
}
_, err = f.Write(prettyJSON.Bytes())
if err != nil {
return fmt.Errorf("failed to write new spec: %w", err)
}
// validate result
return nil
}
func main() {
output := flag.String("o", "../../../swagger-ui/merged.json", "the output path")
flag.Parse()
err := mergeSpecs(*output, flag.Args()...)
if err != nil {
fmt.Printf("something went wrong: %s\n", err.Error())
}
}

View File

@ -63,6 +63,7 @@ func (s ExecutionErrorOption) ToAlertState() AlertStateType {
return AlertStateType(s)
}
// swagger:model LegacyAlert
type Alert struct {
Id int64
Version int64

File diff suppressed because it is too large Load Diff

View File

@ -2618,7 +2618,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UpdateDashboardAclCommand"
"$ref": "#/definitions/UpdateDashboardACLCommand"
}
},
{
@ -2976,7 +2976,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UpdateDashboardAclCommand"
"$ref": "#/definitions/UpdateDashboardACLCommand"
}
},
{
@ -4218,7 +4218,7 @@
],
"responses": {
"200": {
"description": ""
"$ref": "#/responses/folderResponse"
},
"401": {
"$ref": "#/responses/unauthorisedError"
@ -4368,7 +4368,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UpdateDashboardAclCommand"
"$ref": "#/definitions/UpdateDashboardACLCommand"
}
}
],
@ -7866,7 +7866,7 @@
],
"responses": {
"200": {
"$ref": "#/responses/okResponse"
"$ref": "#/responses/getTeamMembersResponse"
},
"401": {
"$ref": "#/responses/unauthorisedError"
@ -9120,82 +9120,6 @@
}
}
},
"Alert": {
"type": "object",
"properties": {
"Created": {
"type": "string",
"format": "date-time"
},
"DashboardId": {
"type": "integer",
"format": "int64"
},
"EvalData": {
"$ref": "#/definitions/Json"
},
"ExecutionError": {
"type": "string"
},
"For": {
"$ref": "#/definitions/Duration"
},
"Frequency": {
"type": "integer",
"format": "int64"
},
"Handler": {
"type": "integer",
"format": "int64"
},
"Id": {
"type": "integer",
"format": "int64"
},
"Message": {
"type": "string"
},
"Name": {
"type": "string"
},
"NewStateDate": {
"type": "string",
"format": "date-time"
},
"OrgId": {
"type": "integer",
"format": "int64"
},
"PanelId": {
"type": "integer",
"format": "int64"
},
"Settings": {
"$ref": "#/definitions/Json"
},
"Severity": {
"type": "string"
},
"Silenced": {
"type": "boolean"
},
"State": {
"$ref": "#/definitions/AlertStateType"
},
"StateChanges": {
"type": "integer",
"format": "int64"
},
"Updated": {
"type": "string",
"format": "date-time"
},
"Version": {
"type": "integer",
"format": "int64"
}
}
},
"AlertListItemDTO": {
"type": "object",
"properties": {
@ -9889,7 +9813,7 @@
}
}
},
"DashboardAclInfoDTO": {
"DashboardACLInfoDTO": {
"type": "object",
"properties": {
"created": {
@ -9964,7 +9888,7 @@
}
}
},
"DashboardAclUpdateItem": {
"DashboardACLUpdateItem": {
"type": "object",
"properties": {
"permission": {
@ -10078,6 +10002,9 @@
"publicDashboardAccessToken": {
"type": "string"
},
"publicDashboardEnabled": {
"type": "boolean"
},
"slug": {
"type": "string"
},
@ -10547,6 +10474,10 @@
}
}
},
"DataTopic": {
"type": "string",
"title": "DataTopic is used to identify which topic the frame should be assigned to."
},
"DeleteTokenCommand": {
"type": "object",
"properties": {
@ -10624,7 +10555,7 @@
"$ref": "#/definitions/FieldConfig"
},
"labels": {
"$ref": "#/definitions/Labels"
"$ref": "#/definitions/FrameLabels"
},
"name": {
"description": "Name is default identifier of the field. The name does not have to be unique, but the combination\nof name and Labels should be unique for proper behavior in all situations.",
@ -10835,6 +10766,13 @@
}
}
},
"FrameLabels": {
"description": "Labels are used to add metadata to an object. The JSON will always be sorted keys",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"FrameMeta": {
"description": "https://github.com/grafana/grafana/blob/master/packages/grafana-data/src/types/data.ts#L11\nNOTE -- in javascript this can accept any `[key: string]: any;` however\nthis interface only exposes the values we want to be exposed",
"type": "object",
@ -10848,6 +10786,9 @@
"description": "Custom datasource specific values.",
"type": "object"
},
"dataTopic": {
"$ref": "#/definitions/DataTopic"
},
"executedQueryString": {
"description": "ExecutedQueryString is the raw query sent to the underlying system. All macros and templating\nhave been applied. When metadata contains this value, it will be shown in the query inspector.",
"type": "string"
@ -11175,11 +11116,80 @@
"Json": {
"type": "object"
},
"Labels": {
"description": "Labels are used to add metadata to an object. The JSON will always be sorted keys",
"LegacyAlert": {
"type": "object",
"additionalProperties": {
"type": "string"
"properties": {
"Created": {
"type": "string",
"format": "date-time"
},
"DashboardId": {
"type": "integer",
"format": "int64"
},
"EvalData": {
"$ref": "#/definitions/Json"
},
"ExecutionError": {
"type": "string"
},
"For": {
"$ref": "#/definitions/Duration"
},
"Frequency": {
"type": "integer",
"format": "int64"
},
"Handler": {
"type": "integer",
"format": "int64"
},
"Id": {
"type": "integer",
"format": "int64"
},
"Message": {
"type": "string"
},
"Name": {
"type": "string"
},
"NewStateDate": {
"type": "string",
"format": "date-time"
},
"OrgId": {
"type": "integer",
"format": "int64"
},
"PanelId": {
"type": "integer",
"format": "int64"
},
"Settings": {
"$ref": "#/definitions/Json"
},
"Severity": {
"type": "string"
},
"Silenced": {
"type": "boolean"
},
"State": {
"$ref": "#/definitions/AlertStateType"
},
"StateChanges": {
"type": "integer",
"format": "int64"
},
"Updated": {
"type": "string",
"format": "date-time"
},
"Version": {
"type": "integer",
"format": "int64"
}
}
},
"LibraryElementConnectionDTO": {
@ -12270,7 +12280,7 @@
}
},
"Responses": {
"description": "The QueryData method the QueryDataHandler method will set the RefId\nproperty on the DataRespones' frames based on these RefIDs.",
"description": "The QueryData method the QueryDataHandler method will set the RefId\nproperty on the DataResponses' frames based on these RefIDs.",
"type": "object",
"title": "Responses is a map of RefIDs (Unique Query ID) to DataResponses.",
"additionalProperties": {
@ -13164,13 +13174,13 @@
}
}
},
"UpdateDashboardAclCommand": {
"UpdateDashboardACLCommand": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/DashboardAclUpdateItem"
"$ref": "#/definitions/DashboardACLUpdateItem"
}
}
}
@ -14058,10 +14068,7 @@
"getAlertResponse": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Alert"
}
"$ref": "#/definitions/LegacyAlert"
}
},
"getAlertsResponse": {
@ -14126,7 +14133,7 @@
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/DashboardAclInfoDTO"
"$ref": "#/definitions/DashboardACLInfoDTO"
}
}
},