diff --git a/cspell.config.json b/cspell.config.json index 5f322013..be7c30ee 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -6,15 +6,19 @@ "mage_output_file.go", "package.json", "renovate.json", + "query.panel.schema.json", + "query.request.schema.json", + "query.types.json", "yarn.lock" ], - "ignoreRegExpList": [ - "import\\s*\\((.|[\r\n])*?\\)", - "import\\s*.*\".*?\"" - ], + "ignoreRegExpList": ["import\\s*\\((.|[\r\n])*?\\)", "import\\s*.*\".*?\""], "words": [ + "apiserver", + "dataplane", "araddon", "bmike", + "combobox", + "confg", "CTAV", "combobox", "Dataframe", @@ -26,6 +30,7 @@ "dompurify", "dserrors", "errorsource", + "featuretoggles", "ghinstallation", "githubclient", "githubv", @@ -36,6 +41,7 @@ "healthcheck", "httpclient", "HTMLURL", + "httpclient", "instancemgmt", "jackspeak", "kminehart", @@ -43,6 +49,9 @@ "Mergeable", "mjseaman", "nazzzzz", + "octocat", + "oldorg", + "oldrepo", "prismjs", "promop", "PTRACE", @@ -52,6 +61,8 @@ "querytype", "rgba", "RUBYGEMS", + "schemabuilder", + "schemads", "seccomp", "shurcoo", "stretchr", @@ -71,11 +82,6 @@ "vals", "vladimirdotk", "Wrapf", - "confg", - "octocat", - "schemads", - "featuretoggles", - "oldorg", - "oldrepo" + "yesoreyeram" ] } diff --git a/package.json b/package.json index 3d6602da..08b0bad7 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@changesets/cli": "2.30.0", "@grafana/e2e-selectors": "12.4.2", "@grafana/eslint-config": "9.0.0", - "@grafana/plugin-e2e": "3.4.13", + "@grafana/plugin-e2e": "3.5.1", "@grafana/plugin-meta-extractor": "0.12.2", "@grafana/tsconfig": "2.0.1", "@openfeature/web-sdk": "1.7.3", diff --git a/pkg/github/query_handler.go b/pkg/github/query_handler.go index 1bd49314..1b8a856d 100644 --- a/pkg/github/query_handler.go +++ b/pkg/github/query_handler.go @@ -4,11 +4,11 @@ import ( "context" "encoding/json" - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/pkg/errors" "github.com/grafana/github-datasource/pkg/models" + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" ) // QueryHandler is the main handler for datasource queries. @@ -42,29 +42,32 @@ func UnmarshalQuery(b []byte, v interface{}) *backend.DataResponse { // GetQueryHandlers creates the QueryTypeMux type for handling queries func GetQueryHandlers(s *QueryHandler) *datasource.QueryTypeMux { mux := datasource.NewQueryTypeMux() + register := func(qt models.QueryType, handler backend.QueryDataHandlerFunc) { + mux.HandleFunc(string(qt), handler) + } - mux.HandleFunc(models.QueryTypeCommits, s.HandleCommits) - mux.HandleFunc(models.QueryTypeIssues, s.HandleIssues) - mux.HandleFunc(models.QueryTypeContributors, s.HandleContributors) - mux.HandleFunc(models.QueryTypeLabels, s.HandleLabels) - mux.HandleFunc(models.QueryTypePullRequests, s.HandlePullRequests) - mux.HandleFunc(models.QueryTypePullRequestReviews, s.HandlePullRequestReviews) - mux.HandleFunc(models.QueryTypeReleases, s.HandleReleases) - mux.HandleFunc(models.QueryTypeTags, s.HandleTags) - mux.HandleFunc(models.QueryTypePackages, s.HandlePackages) - mux.HandleFunc(models.QueryTypeMilestones, s.HandleMilestones) - mux.HandleFunc(models.QueryTypeRepositories, s.HandleRepositories) - mux.HandleFunc(models.QueryTypeVulnerabilities, s.HandleVulnerabilities) - mux.HandleFunc(models.QueryTypeProjects, s.HandleProjects) - mux.HandleFunc(models.QueryTypeStargazers, s.HandleStargazers) - mux.HandleFunc(models.QueryTypeWorkflows, s.HandleWorkflows) - mux.HandleFunc(models.QueryTypeWorkflowUsage, s.HandleWorkflowUsage) - mux.HandleFunc(models.QueryTypeWorkflowRuns, s.HandleWorkflowRuns) - mux.HandleFunc(models.QueryTypeCodeScanning, s.HandleCodeScanning) - mux.HandleFunc(models.QueryTypeDeployments, s.HandleDeployments) - mux.HandleFunc(models.QueryTypeOrganizations, s.HandleOrganizations) - mux.HandleFunc(models.QueryTypeCommitFiles, s.HandleCommitFiles) - mux.HandleFunc(models.QueryTypePullRequestFiles, s.HandlePullRequestFiles) + register(models.QueryTypeCommits, s.HandleCommits) + register(models.QueryTypeIssues, s.HandleIssues) + register(models.QueryTypeContributors, s.HandleContributors) + register(models.QueryTypeLabels, s.HandleLabels) + register(models.QueryTypePullRequests, s.HandlePullRequests) + register(models.QueryTypePullRequestReviews, s.HandlePullRequestReviews) + register(models.QueryTypeReleases, s.HandleReleases) + register(models.QueryTypeTags, s.HandleTags) + register(models.QueryTypePackages, s.HandlePackages) + register(models.QueryTypeMilestones, s.HandleMilestones) + register(models.QueryTypeRepositories, s.HandleRepositories) + register(models.QueryTypeVulnerabilities, s.HandleVulnerabilities) + register(models.QueryTypeProjects, s.HandleProjects) + register(models.QueryTypeStargazers, s.HandleStargazers) + register(models.QueryTypeWorkflows, s.HandleWorkflows) + register(models.QueryTypeWorkflowUsage, s.HandleWorkflowUsage) + register(models.QueryTypeWorkflowRuns, s.HandleWorkflowRuns) + register(models.QueryTypeCodeScanning, s.HandleCodeScanning) + register(models.QueryTypeDeployments, s.HandleDeployments) + register(models.QueryTypeOrganizations, s.HandleOrganizations) + register(models.QueryTypeCommitFiles, s.HandleCommitFiles) + register(models.QueryTypePullRequestFiles, s.HandlePullRequestFiles) return mux } diff --git a/pkg/github/schema.go b/pkg/github/schema.go index b6cafc1f..511018a6 100644 --- a/pkg/github/schema.go +++ b/pkg/github/schema.go @@ -570,6 +570,6 @@ func timeFieldValuesForTable(tableName string) []string { return nil } -func normalizeTableNames(table string) string { - return strings.ToLower(strings.ReplaceAll(table, "_", "-")) +func normalizeTableNames(table models.QueryType) string { + return strings.ToLower(strings.ReplaceAll(string(table), "_", "-")) } diff --git a/pkg/github/sql.go b/pkg/github/sql.go index 3fbdacfc..4080d152 100644 --- a/pkg/github/sql.go +++ b/pkg/github/sql.go @@ -13,8 +13,8 @@ import ( // tableToQueryType maps normalized table names to their QueryType constants. // Built from the query type constants via normalizeTableNames so the schema // table definitions and this map stay in sync automatically. -var tableToQueryType = func() map[string]string { - qts := []string{ +var tableToQueryType = func() map[string]models.QueryType { + qts := []models.QueryType{ models.QueryTypeCommits, models.QueryTypeIssues, models.QueryTypePullRequests, @@ -37,7 +37,7 @@ var tableToQueryType = func() map[string]string { models.QueryTypeOrganizations, models.QueryTypeGraphQL, } - m := make(map[string]string, len(qts)) + m := make(map[string]models.QueryType, len(qts)) for _, qt := range qts { m[normalizeTableNames(qt)] = qt } @@ -84,7 +84,7 @@ func extractFilterValues(condition schemas.FilterCondition) []string { // applyFilters maps SQL filter predicates to GitHub API query options. // It modifies the options map in-place and returns a list of GitHub search // qualifiers for query types that use the search API. -func applyFilters(queryType string, options map[string]interface{}, filters []schemas.ColumnFilter) []string { +func applyFilters(queryType models.QueryType, options map[string]interface{}, filters []schemas.ColumnFilter) []string { var searchQualifiers []string opts, _ := options["options"].(map[string]interface{}) @@ -242,7 +242,7 @@ func applyFilters(queryType string, options map[string]interface{}, filters []sc return searchQualifiers } -func resolveTimeField(queryType, value string) (any, bool) { +func resolveTimeField(queryType models.QueryType, value string) (any, bool) { switch queryType { case models.QueryTypeIssues: switch value { @@ -275,7 +275,7 @@ func resolveTimeField(queryType, value string) (any, bool) { return 0, false } -func defaultTimeField(queryType string) int { +func defaultTimeField(queryType models.QueryType) int { switch queryType { case models.QueryTypePullRequests, models.QueryTypePullRequestReviews: return int(models.PullRequestCreatedAt) @@ -380,7 +380,7 @@ func normalizeGrafanaSQLRequest(req *backend.QueryDataRequest) *backend.QueryDat } queries = append(queries, backend.DataQuery{ RefID: q.RefID, - QueryType: queryType, + QueryType: string(queryType), MaxDataPoints: q.MaxDataPoints, Interval: q.Interval, TimeRange: q.TimeRange, diff --git a/pkg/github/sql_handler_test.go b/pkg/github/sql_handler_test.go index a239f3e8..6ffe9da8 100644 --- a/pkg/github/sql_handler_test.go +++ b/pkg/github/sql_handler_test.go @@ -18,7 +18,7 @@ func pluginCtxWithFeatureToggle() backend.PluginContext { } func TestTableToQueryTypeCoversAllTypes(t *testing.T) { - allQueryTypes := []string{ + allQueryTypes := []models.QueryType{ models.QueryTypeCommits, models.QueryTypeIssues, models.QueryTypePullRequests, models.QueryTypePullRequestReviews, models.QueryTypeRepositories, models.QueryTypeContributors, @@ -43,7 +43,7 @@ func TestNormalizeAllTableTypes(t *testing.T) { tests := []struct { name string table string - wantType string + wantType models.QueryType wantOwner string wantRepo string unchanged bool @@ -90,8 +90,8 @@ func TestNormalizeAllTableTypes(t *testing.T) { } return } - if q.QueryType != tt.wantType { - t.Errorf("queryType: got %q, want %q", q.QueryType, tt.wantType) + if q.QueryType != string(tt.wantType) { + t.Errorf("queryType: got %q, want %q", q.QueryType, string(tt.wantType)) } var raw map[string]interface{} if err := json.Unmarshal(q.JSON, &raw); err != nil { @@ -121,14 +121,14 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { t.Fatalf("expected one query, got %v", out) } q := out.Queries[0] - if q.QueryType != models.QueryTypePullRequests { + if q.QueryType != string(models.QueryTypePullRequests) { t.Errorf("queryType: got %q, want %q", q.QueryType, models.QueryTypePullRequests) } - var raw map[string]interface{} + var raw map[string]any if err := json.Unmarshal(q.JSON, &raw); err != nil { t.Fatal(err) } - if raw["queryType"] != models.QueryTypePullRequests { + if raw["queryType"] != string(models.QueryTypePullRequests) { t.Errorf("JSON queryType: got %v", raw["queryType"]) } if raw["owner"] != "grafana" || raw["repository"] != "grafana" { @@ -149,8 +149,8 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { t.Fatalf("expected one query") } q := out.Queries[0] - if q.QueryType != models.QueryTypeIssues { - t.Errorf("queryType: got %q, want %q", q.QueryType, models.QueryTypeIssues) + if q.QueryType != string(models.QueryTypeIssues) { + t.Errorf("queryType: got %q, want %q", q.QueryType, string(models.QueryTypeIssues)) } var raw map[string]interface{} if err := json.Unmarshal(q.JSON, &raw); err != nil { @@ -173,8 +173,8 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { if out == nil || len(out.Queries) != 1 { t.Fatalf("expected one query") } - if out.Queries[0].QueryType != models.QueryTypeCommits { - t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, models.QueryTypeCommits) + if out.Queries[0].QueryType != string(models.QueryTypeCommits) { + t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, string(models.QueryTypeCommits)) } }) @@ -190,8 +190,8 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { if out == nil || len(out.Queries) != 1 { t.Fatalf("expected one query") } - if out.Queries[0].QueryType != models.QueryTypeCodeScanning { - t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, models.QueryTypeCodeScanning) + if out.Queries[0].QueryType != string(models.QueryTypeCodeScanning) { + t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, string(models.QueryTypeCodeScanning)) } }) @@ -208,8 +208,8 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { t.Fatalf("expected one query") } q := out.Queries[0] - if q.QueryType != models.QueryTypeOrganizations { - t.Errorf("queryType: got %q, want %q", q.QueryType, models.QueryTypeOrganizations) + if q.QueryType != string(models.QueryTypeOrganizations) { + t.Errorf("queryType: got %q, want %q", q.QueryType, string(models.QueryTypeOrganizations)) } var raw map[string]interface{} if err := json.Unmarshal(q.JSON, &raw); err != nil { @@ -235,8 +235,8 @@ func TestNormalizeGrafanaSQLRequest(t *testing.T) { if out == nil || len(out.Queries) != 1 { t.Fatalf("expected one query") } - if out.Queries[0].QueryType != models.QueryTypeWorkflowRuns { - t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, models.QueryTypeWorkflowRuns) + if out.Queries[0].QueryType != string(models.QueryTypeWorkflowRuns) { + t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, string(models.QueryTypeWorkflowRuns)) } }) @@ -781,8 +781,8 @@ func TestNormalizeGrafanaSQLRequestWithFilters(t *testing.T) { if out == nil || len(out.Queries) != 1 { t.Fatalf("expected one query") } - if out.Queries[0].QueryType != models.QueryTypeIssues { - t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, models.QueryTypeIssues) + if out.Queries[0].QueryType != string(models.QueryTypeIssues) { + t.Errorf("queryType: got %q, want %q", out.Queries[0].QueryType, string(models.QueryTypeIssues)) } }) } diff --git a/pkg/models/query.go b/pkg/models/query.go index e062fe05..08f171de 100644 --- a/pkg/models/query.go +++ b/pkg/models/query.go @@ -1,54 +1,58 @@ package models +// QueryType defines the query operation +// +enum +type QueryType string + const ( // QueryTypeCommits is sent by the frontend when querying commits in a GitHub repository - QueryTypeCommits = "Commits" + QueryTypeCommits QueryType = "Commits" // QueryTypeIssues is used when querying issues in a GitHub repository - QueryTypeIssues = "Issues" + QueryTypeIssues QueryType = "Issues" // QueryTypeContributors is used when querying contributors in a GitHub repository - QueryTypeContributors = "Contributors" + QueryTypeContributors QueryType = "Contributors" // QueryTypeTags is used when querying tags in a GitHub repository - QueryTypeTags = "Tags" + QueryTypeTags QueryType = "Tags" // QueryTypeReleases is used when querying releases in a GitHub repository - QueryTypeReleases = "Releases" + QueryTypeReleases QueryType = "Releases" // QueryTypePullRequests is used when querying pull requests in a GitHub repository - QueryTypePullRequests = "Pull_Requests" + QueryTypePullRequests QueryType = "Pull_Requests" // QueryTypePullRequestReviews is used when querying pull request reviews in a GitHub repository QueryTypePullRequestReviews = "Pull_Request_Reviews" // QueryTypeLabels is used when querying labels in a GitHub repository - QueryTypeLabels = "Labels" + QueryTypeLabels QueryType = "Labels" // QueryTypeRepositories is used when querying for a GitHub repository - QueryTypeRepositories = "Repositories" + QueryTypeRepositories QueryType = "Repositories" // QueryTypeOrganizations is used when querying for GitHub organizations - QueryTypeOrganizations = "Organizations" + QueryTypeOrganizations QueryType = "Organizations" // QueryTypeGraphQL is used when sending an ad-hoc graphql query - QueryTypeGraphQL = "GraphQL" + QueryTypeGraphQL QueryType = "GraphQL" // QueryTypePackages is used when querying for NPM / Docker / etc packages - QueryTypePackages = "Packages" + QueryTypePackages QueryType = "Packages" // QueryTypeMilestones is used when querying for milestones in a repository - QueryTypeMilestones = "Milestones" + QueryTypeMilestones QueryType = "Milestones" // QueryTypeVulnerabilities is used when querying a vulnerability for a repository - QueryTypeVulnerabilities = "Vulnerabilities" + QueryTypeVulnerabilities QueryType = "Vulnerabilities" // QueryTypeProjects is used when querying projects for an organization - QueryTypeProjects = "Projects" + QueryTypeProjects QueryType = "Projects" // QueryTypeProjectItems is used when querying projects for an organization - QueryTypeProjectItems = "ProjectItems" + QueryTypeProjectItems QueryType = "ProjectItems" // QueryTypeStargazers is used when querying stargazers for a repository - QueryTypeStargazers = "Stargazers" + QueryTypeStargazers QueryType = "Stargazers" // QueryTypeWorkflows is used when querying workflows for an organization - QueryTypeWorkflows = "Workflows" + QueryTypeWorkflows QueryType = "Workflows" // QueryTypeWorkflowUsage is used when querying a specific workflow usage - QueryTypeWorkflowUsage = "Workflow_Usage" + QueryTypeWorkflowUsage QueryType = "Workflow_Usage" // QueryTypeWorkflowRuns is used when querying workflow runs for a repository - QueryTypeWorkflowRuns = "Workflow_Runs" + QueryTypeWorkflowRuns QueryType = "Workflow_Runs" // QueryTypeCodeScanning is used when querying code scanning alerts for a repository - QueryTypeCodeScanning = "Code_Scanning" + QueryTypeCodeScanning QueryType = "Code_Scanning" // QueryTypeDeployments is used when querying deployments for a repository - QueryTypeDeployments = "Deployments" + QueryTypeDeployments QueryType = "Deployments" // QueryTypeCommitFiles is used when querying files changed in a specific commit - QueryTypeCommitFiles = "Commit_Files" + QueryTypeCommitFiles QueryType = "Commit_Files" // QueryTypePullRequestFiles is used when querying files changed in a specific pull request - QueryTypePullRequestFiles = "Pull_Request_Files" + QueryTypePullRequestFiles QueryType = "Pull_Request_Files" ) // Query refers to the structure of a query built using the QueryEditor. diff --git a/yarn.lock b/yarn.lock index 3e90b528..b4197a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1751,14 +1751,14 @@ __metadata: languageName: node linkType: hard -"@grafana/e2e-selectors@npm:13.1.0-24236083202": - version: 13.1.0-24236083202 - resolution: "@grafana/e2e-selectors@npm:13.1.0-24236083202" +"@grafana/e2e-selectors@npm:13.1.0-24448182242": + version: 13.1.0-24448182242 + resolution: "@grafana/e2e-selectors@npm:13.1.0-24448182242" dependencies: semver: "npm:^7.7.0" tslib: "npm:2.8.1" typescript: "npm:5.9.2" - checksum: 10c0/de94b82628e2d2e400336c48de05b81f3232802653ec113dbf5480261e37b12522be341ecc38a8ace67ac863d98edd5b1d155d21b67a8aeba946a015864d794d + checksum: 10c0/248c9c689d943cfc5f5a5d5db23ac41dafbec8cd41b6ce2b9d125105ee1e42c2e4f6fa78115844e7ebf741ea712e66fbbd04dd087384fa53318f82a3fb2f63ff languageName: node linkType: hard @@ -1818,11 +1818,11 @@ __metadata: languageName: node linkType: hard -"@grafana/plugin-e2e@npm:3.4.13": - version: 3.4.13 - resolution: "@grafana/plugin-e2e@npm:3.4.13" +"@grafana/plugin-e2e@npm:3.5.1": + version: 3.5.1 + resolution: "@grafana/plugin-e2e@npm:3.5.1" dependencies: - "@grafana/e2e-selectors": "npm:13.1.0-24236083202" + "@grafana/e2e-selectors": "npm:13.1.0-24448182242" semver: "npm:^7.5.4" uuid: "npm:^13.0.0" yaml: "npm:^2.3.4" @@ -1832,7 +1832,7 @@ __metadata: peerDependenciesMeta: "@axe-core/playwright": optional: true - checksum: 10c0/9f3dc34d48b04779cba3745f19f484927ce12b29aea094abbe4cb005b548ef5e937d0545724de3d0ca371b0d3326a8902890de14e671bddb920b0c753c329477 + checksum: 10c0/ecb8ce9d6a18203c0e84a227eff35ddea82c31a0558d85dc0d3204ad8884aeb28971a1854be5951c15f2d3ddf2048ccbd7d847e4a9a647dd181e7ecbf3bd4b6e languageName: node linkType: hard @@ -7825,7 +7825,7 @@ __metadata: "@grafana/e2e-selectors": "npm:12.4.2" "@grafana/eslint-config": "npm:9.0.0" "@grafana/i18n": "npm:12.4.2" - "@grafana/plugin-e2e": "npm:3.4.13" + "@grafana/plugin-e2e": "npm:3.5.1" "@grafana/plugin-meta-extractor": "npm:0.12.2" "@grafana/plugin-ui": "npm:0.13.1" "@grafana/runtime": "npm:12.4.2"