Skip to content

Commit a63c247

Browse files
Project Components DataSource (#143)
* Added initial dependencytrack_components data source. * Fix issues with dependencytrack_components datasource, and add initial tests. * Added example for dependencytrack_components data source.
1 parent 40ba020 commit a63c247

File tree

6 files changed

+418
-0
lines changed

6 files changed

+418
-0
lines changed

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,11 @@ linters:
109109
- "^github.com/hashicorp/terraform-plugin-framework/resource/schema\\.SingleNestedAttribute$"
110110
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.Schema$"
111111
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.StringAttribute$"
112+
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.BoolAttribute$"
112113
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.ListAttribute$"
113114
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.ListNestedAttribute$"
114115
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.NestedAttributeObject$"
116+
- "^github.com/hashicorp/terraform-plugin-framework/datasource/schema\\.SingleNestedAttribute$"
115117
- "^github.com/hashicorp/terraform-plugin-framework/providerserver\\.ServeOpts$"
116118
- "^github.com/hashicorp/terraform-plugin-framework/provider/schema\\.Schema$"
117119
- "^github.com/hashicorp/terraform-plugin-framework/provider/schema\\.StringAttribute$"

docs/data-sources/components.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "dependencytrack_components Data Source - dependencytrack"
4+
subcategory: ""
5+
description: |-
6+
Fetch components for a project.
7+
---
8+
9+
# dependencytrack_components (Data Source)
10+
11+
Fetch components for a project.
12+
13+
## Example Usage
14+
15+
```terraform
16+
data "dependencytrack_project" "example" {
17+
name = "Example"
18+
version = "v1"
19+
}
20+
21+
// All Components
22+
data "dependencytrack_components" "example" {
23+
project = data.dependencytrack_project.example.id
24+
}
25+
26+
// Filtered
27+
data "dependencytrack_components" "filtered" {
28+
project = data.dependencytrack_project.example.id
29+
only_direct = true
30+
only_outdated = true
31+
}
32+
```
33+
34+
<!-- schema generated by tfplugindocs -->
35+
## Schema
36+
37+
### Required
38+
39+
- `project` (String) UUID of the Project for which to retrieve components.
40+
41+
### Optional
42+
43+
- `only_direct` (Boolean) Filter for only direct components of the project.
44+
- `only_outdated` (Boolean) Filter for only outdated components of the project.
45+
46+
### Read-Only
47+
48+
- `components` (Attributes List) Components within the project. (see [below for nested schema](#nestedatt--components))
49+
50+
<a id="nestedatt--components"></a>
51+
### Nested Schema for `components`
52+
53+
Read-Only:
54+
55+
- `author` (String) Author of the Component.
56+
- `classifier` (String) Classifier of the Component.
57+
- `copyright` (String) Copyright of the Component.
58+
- `cpe` (String) Common Platform Enumeration of the Component. Standardised format v2.2 / v2.3 from MITRE / NIST.
59+
- `description` (String) Description of the Component.
60+
- `extension` (String) Filename Extension of the Component.
61+
- `filename` (String) Filename of the Component.
62+
- `group` (String) Group Name of the Component.
63+
- `hashes` (Attributes) Hashes of the Component. (see [below for nested schema](#nestedatt--components--hashes))
64+
- `id` (String) UUID of the Component.
65+
- `license` (String) License of the Component.
66+
- `name` (String) Name of the Component.
67+
- `notes` (String) Notes of the Component.
68+
- `project` (String) Project of the Component.
69+
- `publisher` (String) Publisher of the Component.
70+
- `purl` (String) Package URL of the Component, in standardised form.
71+
- `swid` (String) SWID Tag ID. ISO/IEC 19770-2:2015.
72+
- `version` (String) Version of the Component.
73+
74+
<a id="nestedatt--components--hashes"></a>
75+
### Nested Schema for `components.hashes`
76+
77+
Read-Only:
78+
79+
- `blake2b_256` (String) BLAKE2b-256 hash of the Component.
80+
- `blake2b_384` (String) BLAKE2b-384 hash of the Component.
81+
- `blake2b_512` (String) BLAKE2b-512 hash of the Component.
82+
- `blake3` (String) BLAKE3 hash of the Component.
83+
- `md5` (String) MD5 hash of the Component.
84+
- `sha1` (String) SHA1 hash of the Component.
85+
- `sha256` (String) SHA256 hash of the Component.
86+
- `sha384` (String) SHA384 hash of the Component.
87+
- `sha3_256` (String) SHA3-256 hash of the Component.
88+
- `sha3_384` (String) SHA3-384 hash of the Component.
89+
- `sha3_512` (String) SHA3-512 hash of the Component.
90+
- `sha512` (String) SHA512 hash of the Component.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
data "dependencytrack_project" "example" {
2+
name = "Example"
3+
version = "v1"
4+
}
5+
6+
// All Components
7+
data "dependencytrack_components" "example" {
8+
project = data.dependencytrack_project.example.id
9+
}
10+
11+
// Filtered
12+
data "dependencytrack_components" "filtered" {
13+
project = data.dependencytrack_project.example.id
14+
only_direct = true
15+
only_outdated = true
16+
}
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
dtrack "github.com/DependencyTrack/client-go"
8+
"github.com/hashicorp/terraform-plugin-framework/datasource"
9+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
10+
"github.com/hashicorp/terraform-plugin-framework/path"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
"github.com/hashicorp/terraform-plugin-log/tflog"
13+
)
14+
15+
// Interface impl check.
16+
var (
17+
_ datasource.DataSource = &componentsDataSource{}
18+
_ datasource.DataSourceWithConfigure = &componentsDataSource{}
19+
)
20+
21+
type (
22+
componentsDataSource struct {
23+
client *dtrack.Client
24+
semver *Semver
25+
}
26+
27+
componentsDataSourceModel struct {
28+
Project types.String `tfsdk:"project"`
29+
Components []componentResourceModel `tfsdk:"components"`
30+
OnlyDirect types.Bool `tfsdk:"only_direct"`
31+
OnlyOutdated types.Bool `tfsdk:"only_outdated"`
32+
}
33+
)
34+
35+
func NewComponentsDataSource() datasource.DataSource {
36+
return &componentsDataSource{}
37+
}
38+
39+
func (*componentsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
40+
resp.TypeName = req.ProviderTypeName + "_components"
41+
}
42+
43+
func (*componentsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
44+
resp.Schema = schema.Schema{
45+
Description: "Fetch components for a project.",
46+
Attributes: map[string]schema.Attribute{
47+
"project": schema.StringAttribute{
48+
Description: "UUID of the Project for which to retrieve components.",
49+
Required: true,
50+
},
51+
"only_direct": schema.BoolAttribute{
52+
Description: "Filter for only direct components of the project.",
53+
Optional: true,
54+
},
55+
"only_outdated": schema.BoolAttribute{
56+
Description: "Filter for only outdated components of the project.",
57+
Optional: true,
58+
},
59+
"components": schema.ListNestedAttribute{
60+
Description: "Components within the project.",
61+
Computed: true,
62+
NestedObject: schema.NestedAttributeObject{
63+
Attributes: map[string]schema.Attribute{
64+
"id": schema.StringAttribute{
65+
Description: "UUID of the Component.",
66+
Computed: true,
67+
},
68+
"project": schema.StringAttribute{
69+
Description: "Project of the Component.",
70+
Computed: true,
71+
},
72+
"author": schema.StringAttribute{
73+
Description: "Author of the Component.",
74+
Computed: true,
75+
},
76+
"publisher": schema.StringAttribute{
77+
Description: "Publisher of the Component.",
78+
Computed: true,
79+
},
80+
"group": schema.StringAttribute{
81+
Description: "Group Name of the Component.",
82+
Computed: true,
83+
},
84+
"name": schema.StringAttribute{
85+
Description: "Name of the Component.",
86+
Computed: true,
87+
},
88+
"version": schema.StringAttribute{
89+
Description: "Version of the Component.",
90+
Computed: true,
91+
},
92+
"classifier": schema.StringAttribute{
93+
Description: "Classifier of the Component.",
94+
Computed: true,
95+
},
96+
"filename": schema.StringAttribute{
97+
Description: "Filename of the Component.",
98+
Computed: true,
99+
},
100+
"extension": schema.StringAttribute{
101+
Description: "Filename Extension of the Component.",
102+
Computed: true,
103+
},
104+
"cpe": schema.StringAttribute{
105+
Description: "Common Platform Enumeration of the Component. Standardised format v2.2 / v2.3 from MITRE / NIST.",
106+
Computed: true,
107+
},
108+
"purl": schema.StringAttribute{
109+
Description: "Package URL of the Component, in standardised form.",
110+
Computed: true,
111+
},
112+
"swid": schema.StringAttribute{
113+
Description: "SWID Tag ID. ISO/IEC 19770-2:2015.",
114+
Computed: true,
115+
},
116+
"description": schema.StringAttribute{
117+
Description: "Description of the Component.",
118+
Computed: true,
119+
},
120+
"copyright": schema.StringAttribute{
121+
Description: "Copyright of the Component.",
122+
Computed: true,
123+
},
124+
"license": schema.StringAttribute{
125+
Description: "License of the Component.",
126+
Computed: true,
127+
},
128+
"notes": schema.StringAttribute{
129+
Description: "Notes of the Component.",
130+
Computed: true,
131+
},
132+
"hashes": schema.SingleNestedAttribute{
133+
Description: "Hashes of the Component.",
134+
Computed: true,
135+
Attributes: map[string]schema.Attribute{
136+
"md5": schema.StringAttribute{
137+
Description: "MD5 hash of the Component.",
138+
Computed: true,
139+
},
140+
"sha1": schema.StringAttribute{
141+
Description: "SHA1 hash of the Component.",
142+
Computed: true,
143+
},
144+
"sha256": schema.StringAttribute{
145+
Description: "SHA256 hash of the Component.",
146+
Computed: true,
147+
},
148+
"sha384": schema.StringAttribute{
149+
Description: "SHA384 hash of the Component.",
150+
Computed: true,
151+
},
152+
"sha512": schema.StringAttribute{
153+
Description: "SHA512 hash of the Component.",
154+
Computed: true,
155+
},
156+
"sha3_256": schema.StringAttribute{
157+
Description: "SHA3-256 hash of the Component.",
158+
Computed: true,
159+
},
160+
"sha3_384": schema.StringAttribute{
161+
Description: "SHA3-384 hash of the Component.",
162+
Computed: true,
163+
},
164+
"sha3_512": schema.StringAttribute{
165+
Description: "SHA3-512 hash of the Component.",
166+
Computed: true,
167+
},
168+
"blake2b_256": schema.StringAttribute{
169+
Description: "BLAKE2b-256 hash of the Component.",
170+
Computed: true,
171+
},
172+
"blake2b_384": schema.StringAttribute{
173+
Description: "BLAKE2b-384 hash of the Component.",
174+
Computed: true,
175+
},
176+
"blake2b_512": schema.StringAttribute{
177+
Description: "BLAKE2b-512 hash of the Component.",
178+
Computed: true,
179+
},
180+
"blake3": schema.StringAttribute{
181+
Description: "BLAKE3 hash of the Component.",
182+
Computed: true,
183+
},
184+
},
185+
},
186+
},
187+
},
188+
},
189+
},
190+
}
191+
}
192+
193+
func (d *componentsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
194+
var state componentsDataSourceModel
195+
diags := req.Config.Get(ctx, &state)
196+
resp.Diagnostics.Append(diags...)
197+
if resp.Diagnostics.HasError() {
198+
return
199+
}
200+
201+
project, diagnostic := TryParseUUID(state.Project, LifecycleRead, path.Root("project"))
202+
onlyDirect := state.OnlyDirect.ValueBool()
203+
onlyOutdated := state.OnlyOutdated.ValueBool()
204+
diags.Append(diagnostic)
205+
if diags.HasError() {
206+
return
207+
}
208+
209+
tflog.Debug(ctx, "Reading Project Components", map[string]any{
210+
"project": project.String(),
211+
"only_direct": onlyDirect,
212+
"only_outdated": onlyOutdated,
213+
})
214+
215+
components, err := dtrack.FetchAll(func(po dtrack.PageOptions) (dtrack.Page[dtrack.Component], error) {
216+
return d.client.Component.GetAll(ctx, project, po, dtrack.ComponentFilterOptions{
217+
OnlyOutdated: onlyOutdated,
218+
OnlyDirect: onlyDirect,
219+
})
220+
})
221+
if err != nil {
222+
resp.Diagnostics.AddError(
223+
"Within Read, unable to fetch Components",
224+
"Error from: "+err.Error(),
225+
)
226+
return
227+
}
228+
229+
state = componentsDataSourceModel{
230+
OnlyDirect: types.BoolValue(onlyDirect),
231+
OnlyOutdated: types.BoolValue(onlyOutdated),
232+
Project: types.StringValue(project.String()),
233+
Components: Map(components, componentToModel),
234+
}
235+
236+
diags = resp.State.Set(ctx, &state)
237+
resp.Diagnostics.Append(diags...)
238+
if resp.Diagnostics.HasError() {
239+
return
240+
}
241+
tflog.Debug(ctx, "Read Project Components", map[string]any{
242+
"project": project.String(),
243+
"count": len(state.Components),
244+
})
245+
}
246+
247+
func (d *componentsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
248+
if req.ProviderData == nil {
249+
return
250+
}
251+
clientInfoData, ok := req.ProviderData.(clientInfo)
252+
if !ok {
253+
resp.Diagnostics.AddError(
254+
"Unexpected Configure Type",
255+
fmt.Sprintf("Expected provider.clientInfo, got %T. Please report this issue to the provider developers.", req.ProviderData),
256+
)
257+
return
258+
}
259+
d.client = clientInfoData.client
260+
d.semver = clientInfoData.semver
261+
}

0 commit comments

Comments
 (0)