Skip to content

Commit 41094f0

Browse files
committed
feat: add explorer API query endpoint
1 parent f9d7888 commit 41094f0

File tree

5 files changed

+492
-0
lines changed

5 files changed

+492
-0
lines changed

errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@ var (
236236
ErrInvalidAccessToken = errors.New("invalid value for access token")
237237

238238
ErrInvalidTaskResultsCallbackStatus = fmt.Errorf("invalid value for task result status. Must be either `%s`, `%s`, or `%s`", TaskFailed, TaskPassed, TaskRunning)
239+
240+
ErrInvalidExplorerQueryFilterIndex = errors.New("invalid query filter index, must be greater than or equal to 0")
241+
242+
ErrInvalidExplorerViewType = errors.New("invalid explorer query view type, must be one of workspaces, tf_versions, providers, or modules")
239243
)
240244

241245
var (

explorer.go

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package tfe
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/url"
10+
"time"
11+
)
12+
13+
// Compile-time proof of interface implementation.
14+
var _ Explorer = (*explorer)(nil)
15+
16+
// Explorer describes all the explorer related methods that the Terraform Enterprise API supports.
17+
//
18+
// TFE API docs: https://developer.hashicorp.com/terraform/cloud-docs/api-docs/explorer
19+
type Explorer interface {
20+
// Query information about workspaces within an organization.
21+
QueryWorkspaces(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerWorkspaceViewList, error)
22+
// Query information about module version usage within an organization.
23+
QueryModules(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerModuleViewList, error)
24+
// Query information about provider version usage within an organization.
25+
QueryProviders(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerProviderViewList, error)
26+
// Query information about Terraform version usage within an organization.
27+
QueryTerraformVersions(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerTerraformVersionViewList, error)
28+
}
29+
30+
type explorer struct {
31+
client *Client
32+
}
33+
34+
// ExplorerViewType represents the view types the Explorer API supports
35+
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/explorer#view-types
36+
type ExplorerViewType string
37+
38+
const (
39+
WorkspacesViewType ExplorerViewType = "workspaces"
40+
ProvidersViewType ExplorerViewType = "providers"
41+
ModulesViewType ExplorerViewType = "modules"
42+
TerraformVersionsViewType ExplorerViewType = "tf_versions"
43+
)
44+
45+
// ExplorerQueryFilterOperator represents the supported operations for filtering.
46+
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/explorer#filter-operators
47+
type ExplorerQueryFilterOperator string
48+
49+
const (
50+
OpIs ExplorerQueryFilterOperator = "is"
51+
OpIsNot ExplorerQueryFilterOperator = "is_not"
52+
OpContains ExplorerQueryFilterOperator = "contains"
53+
OpDoesNotContain ExplorerQueryFilterOperator = "does_not_contain"
54+
OpIsEmpty ExplorerQueryFilterOperator = "is_empty"
55+
OpIsNotEmpty ExplorerQueryFilterOperator = "is_not_empty"
56+
OpGreaterThan ExplorerQueryFilterOperator = "gt"
57+
OpLessThan ExplorerQueryFilterOperator = "lt"
58+
OpGreaterThanOrEqual ExplorerQueryFilterOperator = "gteq"
59+
OpLessThanOrEqual ExplorerQueryFilterOperator = "lteq"
60+
OpIsBefore ExplorerQueryFilterOperator = "is_before"
61+
OpIsAfter ExplorerQueryFilterOperator = "is_after"
62+
)
63+
64+
// ExplorerQueryFilter represents a filter query parameter for the query endpoint.
65+
type ExplorerQueryFilter struct {
66+
Index int
67+
Name string
68+
Operator ExplorerQueryFilterOperator
69+
Value string
70+
}
71+
72+
func (eqf *ExplorerQueryFilter) toQueryParam() (string, string) {
73+
key := fmt.Sprintf("filter[%d][%s][%s][0]", eqf.Index, eqf.Name, eqf.Operator)
74+
return key, eqf.Value
75+
}
76+
77+
// ExplorerQueryOptions represents the parameter options for querying the Explorer API
78+
type ExplorerQueryOptions struct {
79+
ListOptions
80+
81+
View ExplorerViewType `url:"type"`
82+
Sort string `url:"sort,omitempty"`
83+
Fields string `url:"fields,omitempty"`
84+
85+
Filters []*ExplorerQueryFilter `url:"-"`
86+
}
87+
88+
// WorkspaceView represents information about a workspace in the target
89+
// organization and any current runs associated with that workspace.
90+
type WorkspaceView struct {
91+
Type string `jsonapi:"primary,visibility-workspace"`
92+
AllChecksSucceeded bool `jsonapi:"attr,all-checks-succeeded"`
93+
ChecksErrored int `jsonapi:"attr,checks-errored"`
94+
ChecksFailed int `jsonapi:"attr,checks-failed"`
95+
ChecksPassed int `jsonapi:"attr,checks-passed"`
96+
ChecksUnknown int `jsonapi:"attr,checks-unknown"`
97+
CurrentRunAppliedAt time.Time `jsonapi:"attr,current-run-applied-at,rfc3339"`
98+
CurrentRunExternalID string `jsonapi:"attr,current-run-external-id"`
99+
CurrentRunStatus RunStatus `jsonapi:"attr,current-run-status"`
100+
Drifted bool `jsonapi:"attr,drifted"`
101+
ExternalID string `jsonapi:"attr,external-id"`
102+
ModuleCount int `jsonapi:"attr,module-count"`
103+
Modules interface{} `jsonapi:"attr,modules"`
104+
OrganizationName string `jsonapi:"attr,organization-name"`
105+
ProjectExternalID string `jsonapi:"attr,project-external-id"`
106+
ProjectName string `jsonapi:"attr,project-name"`
107+
ProviderCount int `jsonapi:"attr,provider-count"`
108+
Providers interface{} `jsonapi:"attr,providers"`
109+
ResourcesDrifted int `jsonapi:"attr,resources-drifted"`
110+
ResourcesUndrifted int `jsonapi:"attr,resources-undrifted"`
111+
StateVersionTerraformVersion string `jsonapi:"attr,state-version-terraform-version"`
112+
VCSRepoIdentifier *string `jsonapi:"attr,vcs-repo-identifier"`
113+
WorkspaceCreatedAt time.Time `jsonapi:"attr,workspace-created-at,rfc3339"`
114+
WorkspaceName string `jsonapi:"attr,workspace-name"`
115+
WorkspaceTerraformVersion string `jsonapi:"attr,workspace-terraform-version"`
116+
WorkspaceUpdatedAt time.Time `jsonapi:"attr,workspace-updated-at,rfc3339"`
117+
}
118+
119+
// ModuleView represents information about a Terraform module version used by
120+
// an organization.
121+
type ModuleView struct {
122+
Type string `jsonapi:"primary,visibility-module-version"`
123+
Name string `jsonapi:"attr,name"`
124+
Source string `jsonapi:"attr,source"`
125+
Version string `jsonapi:"attr,version"`
126+
WorkspaceCount int `jsonapi:"attr,workspace-count"`
127+
Workspaces string `jsonapi:"attr,workspaces"`
128+
}
129+
130+
// ProviderView represents information about a Terraform provider version used
131+
// by an organization.
132+
type ProviderView struct {
133+
Type string `jsonapi:"primary,visibility-provider-version"`
134+
Name string `jsonapi:"attr,name"`
135+
Source string `jsonapi:"attr,source"`
136+
Version string `jsonapi:"attr,version"`
137+
WorkspaceCount int `jsonapi:"attr,workspace-count"`
138+
Workspaces string `jsonapi:"attr,workspaces"`
139+
}
140+
141+
// TerraformVersionView represents information about a Terraform version used
142+
// by workspaces in an organization.
143+
type TerraformVersionView struct {
144+
Type string `jsonapi:"primary,visibility-tf-version"`
145+
Version string `jsonapi:"attr,version"`
146+
WorkspaceCount int `jsonapi:"attr,workspace-count"`
147+
Workspaces string `jsonapi:"attr,workspaces"`
148+
}
149+
150+
// ExplorerWorkspaceViewList represents a list of workspace views
151+
type ExplorerWorkspaceViewList struct {
152+
*Pagination
153+
Items []*WorkspaceView
154+
}
155+
156+
// ExplorerModuleViewList represents a list of module views
157+
type ExplorerModuleViewList struct {
158+
*Pagination
159+
Items []*ModuleView
160+
}
161+
162+
// ExplorerProviderViewList represents a list of provider views
163+
type ExplorerProviderViewList struct {
164+
*Pagination
165+
Items []*ProviderView
166+
}
167+
168+
// ExplorerTerraformVersionViewList represents a list of Terraform version views
169+
type ExplorerTerraformVersionViewList struct {
170+
*Pagination
171+
Items []*TerraformVersionView
172+
}
173+
174+
// QueryWorkspaces invokes the Explorer's Query endpoint to return information
175+
// about workspaces and their associated runs in the specified organization.
176+
func (e *explorer) QueryWorkspaces(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerWorkspaceViewList, error) {
177+
// Force the correct view type
178+
options.View = WorkspacesViewType
179+
180+
req, err := e.buildExplorerQueryRequest(organization, options)
181+
if err != nil {
182+
return nil, err
183+
}
184+
185+
eql := &ExplorerWorkspaceViewList{}
186+
err = req.Do(ctx, eql)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
return eql, nil
192+
}
193+
194+
// QueryModules invokes the Explorer's Query endpoint to return information
195+
// about module versions in use across the specified organization.
196+
func (e *explorer) QueryModules(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerModuleViewList, error) {
197+
// Force the correct view type
198+
options.View = ModulesViewType
199+
200+
req, err := e.buildExplorerQueryRequest(organization, options)
201+
if err != nil {
202+
return nil, err
203+
}
204+
205+
eql := &ExplorerModuleViewList{}
206+
err = req.Do(ctx, eql)
207+
if err != nil {
208+
return nil, err
209+
}
210+
211+
return eql, nil
212+
}
213+
214+
// QueryProviders invokes the Explorer's Query endpoint to return information
215+
// about provider versions in use across the specified organization.
216+
func (e *explorer) QueryProviders(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerProviderViewList, error) {
217+
// Force the correct view type
218+
options.View = ProvidersViewType
219+
220+
req, err := e.buildExplorerQueryRequest(organization, options)
221+
if err != nil {
222+
return nil, err
223+
}
224+
225+
eql := &ExplorerProviderViewList{}
226+
err = req.Do(ctx, eql)
227+
if err != nil {
228+
return nil, err
229+
}
230+
231+
return eql, nil
232+
}
233+
234+
// QueryTerraformVersions invokes the Explorer's Query endpoint to return information
235+
// about Terraform versions in use across the specified organization.
236+
func (e *explorer) QueryTerraformVersions(ctx context.Context, organization string, options ExplorerQueryOptions) (*ExplorerTerraformVersionViewList, error) {
237+
// Force the correct view type
238+
options.View = TerraformVersionsViewType
239+
240+
req, err := e.buildExplorerQueryRequest(organization, options)
241+
if err != nil {
242+
return nil, err
243+
}
244+
245+
eql := &ExplorerTerraformVersionViewList{}
246+
err = req.Do(ctx, eql)
247+
if err != nil {
248+
return nil, err
249+
}
250+
251+
return eql, nil
252+
}
253+
254+
func (e *explorer) buildExplorerQueryRequest(organization string, options ExplorerQueryOptions) (*ClientRequest, error) {
255+
filterParams := make(map[string][]string)
256+
u := fmt.Sprintf("organizations/%s/explorer", url.QueryEscape(organization))
257+
258+
for _, filter := range options.Filters {
259+
if filter != nil {
260+
k, v := filter.toQueryParam()
261+
filterParams[k] = []string{v}
262+
}
263+
}
264+
265+
return e.client.NewRequestWithAdditionalQueryParams("GET", u, options, filterParams)
266+
}

0 commit comments

Comments
 (0)