@@ -3,33 +3,50 @@ package provider
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
+ "errors"
6
7
"fmt"
7
8
"path"
8
9
"strings"
9
10
10
11
"github.com/ansible/terraform-provider-aap/internal/provider/customtypes"
12
+ "github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator"
11
13
"github.com/hashicorp/terraform-plugin-framework/datasource"
12
14
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
13
15
"github.com/hashicorp/terraform-plugin-framework/diag"
16
+ tfpath "github.com/hashicorp/terraform-plugin-framework/path"
17
+ "github.com/hashicorp/terraform-plugin-framework/provider"
14
18
"github.com/hashicorp/terraform-plugin-framework/types"
15
19
)
16
20
21
+ // inventoryDataSourceModel maps the data source schema data.
22
+ type InventoryDataSourceModel struct {
23
+ Id types.Int64 `tfsdk:"id"`
24
+ Organization types.Int64 `tfsdk:"organization"`
25
+ OrganizationName types.String `tfsdk:"organization_name"`
26
+ Url types.String `tfsdk:"url"`
27
+ NamedUrl types.String `tfsdk:"named_url"`
28
+ Name types.String `tfsdk:"name"`
29
+ Description types.String `tfsdk:"description"`
30
+ Variables customtypes.AAPCustomStringValue `tfsdk:"variables"`
31
+ }
32
+
33
+ // InventoryDataSource is the data source implementation.
34
+ type InventoryDataSource struct {
35
+ client ProviderHTTPClient
36
+ }
37
+
17
38
// Ensure the implementation satisfies the expected interfaces.
18
39
var (
19
- _ datasource.DataSource = & InventoryDataSource {}
20
- _ datasource.DataSourceWithConfigure = & InventoryDataSource {}
40
+ _ datasource.DataSource = & InventoryDataSource {}
41
+ _ datasource.DataSourceWithConfigure = & InventoryDataSource {}
42
+ _ datasource.DataSourceWithConfigValidators = & InventoryDataSource {}
21
43
)
22
44
23
45
// NewInventoryDataSource is a helper function to simplify the provider implementation.
24
46
func NewInventoryDataSource () datasource.DataSource {
25
47
return & InventoryDataSource {}
26
48
}
27
49
28
- // inventoryDataSource is the data source implementation.
29
- type InventoryDataSource struct {
30
- client * AAPClient
31
- }
32
-
33
50
// Metadata returns the data source type name.
34
51
func (d * InventoryDataSource ) Metadata (_ context.Context , req datasource.MetadataRequest , resp * datasource.MetadataResponse ) {
35
52
resp .TypeName = req .ProviderTypeName + "_inventory"
@@ -40,16 +57,16 @@ func (d *InventoryDataSource) Schema(_ context.Context, _ datasource.SchemaReque
40
57
resp .Schema = schema.Schema {
41
58
Attributes : map [string ]schema.Attribute {
42
59
"id" : schema.Int64Attribute {
43
- Optional : true ,
60
+ Optional : true ,
44
61
Description : "Inventory id" ,
45
62
},
46
63
"organization" : schema.Int64Attribute {
47
- Computed : true ,
64
+ Computed : true ,
48
65
Description : "Identifier for the organization to which the inventory belongs" ,
49
66
},
50
67
"organization_name" : schema.StringAttribute {
51
- Computed : true ,
52
- Optional : true ,
68
+ Computed : true ,
69
+ Optional : true ,
53
70
Description : "The name for the organization to which the inventory belongs" ,
54
71
},
55
72
"url" : schema.StringAttribute {
@@ -62,7 +79,7 @@ func (d *InventoryDataSource) Schema(_ context.Context, _ datasource.SchemaReque
62
79
},
63
80
"name" : schema.StringAttribute {
64
81
Computed : true ,
65
- Optional : true ,
82
+ Optional : true ,
66
83
Description : "Name of the inventory" ,
67
84
},
68
85
"description" : schema.StringAttribute {
@@ -90,18 +107,9 @@ func (d *InventoryDataSource) Read(ctx context.Context, req datasource.ReadReque
90
107
return
91
108
}
92
109
93
- //Here is where we can get the "named" inventory, which is "Inventory Name"++"Organization Name" to derive uniqueness
94
- //we will take precedence if the Id is set to use that over the named_url attempt.
95
-
96
- resourceURL := ""
97
-
98
- if state .Id .String () != "<null>" {
99
- resourceURL = path .Join (d .client .getApiEndpoint (), "inventories" , state .Id .String ())
100
- } else if state .Name .String () != "<null>" && state .OrganizationName .String () != "<null>" {
101
- namedUrl := strings .Join ([]string {state .Name .String ()[1 : len (state .Name .String ()) - 1 ], "++" , state .OrganizationName .String ()[1 : len (state .OrganizationName .String ()) - 1 ]}, "" )
102
- resourceURL = path .Join (d .client .getApiEndpoint (), "inventories" , namedUrl )
103
- } else {
104
- resp .Diagnostics .AddError ("Minimal Data Not Supplied" , "Require [id] or [name and organization_name]" )
110
+ resourceURL , err := state .ResourceUrlFromParameters (d )
111
+ if err != nil {
112
+ resp .Diagnostics .AddError ("Minimal Data Not Supplied" , "Expected either [id] or [name + organization_name] pair" )
105
113
return
106
114
}
107
115
@@ -143,19 +151,55 @@ func (d *InventoryDataSource) Configure(_ context.Context, req datasource.Config
143
151
d .client = client
144
152
}
145
153
146
- // inventoryDataSourceModel maps the data source schema data.
147
- type InventoryDataSourceModel struct {
148
- Id types.Int64 `tfsdk:"id"`
149
- Organization types.Int64 `tfsdk:"organization"`
150
- OrganizationName types.String `tfsdk:"organization_name"`
151
- Url types.String `tfsdk:"url"`
152
- NamedUrl types.String `tfsdk:"named_url"`
153
- Name types.String `tfsdk:"name"`
154
- Description types.String `tfsdk:"description"`
155
- Variables customtypes.AAPCustomStringValue `tfsdk:"variables"`
154
+ func (d * InventoryDataSource ) ConfigValidators (_ context.Context ) []datasource.ConfigValidator {
155
+ // You have at least an id or a name + organization_name pair
156
+ return []datasource.ConfigValidator {
157
+ datasourcevalidator .Any (
158
+ datasourcevalidator .AtLeastOneOf (
159
+ tfpath .MatchRoot ("id" )),
160
+ datasourcevalidator .RequiredTogether (
161
+ tfpath .MatchRoot ("name" ),
162
+ tfpath .MatchRoot ("organization_name" )),
163
+ ),
164
+ }
165
+ }
166
+
167
+ func (d * InventoryDataSource ) ValidateConfig (ctx context.Context , req provider.ValidateConfigRequest , resp * provider.ValidateConfigResponse ) {
168
+ var data InventoryDataSourceModel
169
+
170
+ resp .Diagnostics .Append (req .Config .Get (ctx , & data )... )
171
+
172
+ if resp .Diagnostics .HasError () {
173
+ return
174
+ }
175
+
176
+ if ! data .Id .IsNull () {
177
+ return
178
+ }
179
+
180
+ if ! data .Name .IsNull () && ! data .OrganizationName .IsNull () {
181
+ return
182
+ }
183
+
184
+ if data .Id .IsNull () && data .Name .IsNull () {
185
+ resp .Diagnostics .AddAttributeWarning (
186
+ tfpath .Root ("id" ),
187
+ "Missing Atribute Configuration" ,
188
+ "Expected either [id] or [name + organization_name] pair" ,
189
+ )
190
+ }
191
+
192
+ if ! data .Name .IsNull () && data .OrganizationName .IsNull () {
193
+ resp .Diagnostics .AddAttributeWarning (
194
+ tfpath .Root ("organization_name" ),
195
+ "Missing Attribute Configuration" ,
196
+ "Expected organization_name to be configured with name." ,
197
+ )
198
+ }
199
+
156
200
}
157
201
158
- func (d * InventoryDataSourceModel ) ParseHttpResponse (body []byte ) diag.Diagnostics {
202
+ func (dm * InventoryDataSourceModel ) ParseHttpResponse (body []byte ) diag.Diagnostics {
159
203
var diags diag.Diagnostics
160
204
161
205
// Unmarshal the JSON response
@@ -167,14 +211,29 @@ func (d *InventoryDataSourceModel) ParseHttpResponse(body []byte) diag.Diagnosti
167
211
}
168
212
169
213
// Map response to the inventory datesource schema
170
- d .Id = types .Int64Value (apiInventory .Id )
171
- d .Organization = types .Int64Value (apiInventory .Organization )
172
- d .OrganizationName = types .StringValue (apiInventory .SummaryFields .Organization .Name )
173
- d .Url = types .StringValue (apiInventory .Url )
174
- d .NamedUrl = types .StringValue (apiInventory .Related .NamedUrl )
175
- d .Name = ParseStringValue (apiInventory .Name )
176
- d .Description = ParseStringValue (apiInventory .Description )
177
- d .Variables = ParseAAPCustomStringValue (apiInventory .Variables )
214
+ dm .Id = types .Int64Value (apiInventory .Id )
215
+ dm .Organization = types .Int64Value (apiInventory .Organization )
216
+ dm .OrganizationName = types .StringValue (apiInventory .SummaryFields .Organization .Name )
217
+ dm .Url = types .StringValue (apiInventory .Url )
218
+ dm .NamedUrl = types .StringValue (apiInventory .Related .NamedUrl )
219
+ dm .Name = ParseStringValue (apiInventory .Name )
220
+ dm .Description = ParseStringValue (apiInventory .Description )
221
+ dm .Variables = ParseAAPCustomStringValue (apiInventory .Variables )
178
222
179
223
return diags
180
224
}
225
+
226
+ // ResourceUrlFromParameters Given the provided parameters and return the appropriate resource url
227
+ func (dm * InventoryDataSourceModel ) ResourceUrlFromParameters (datasource * InventoryDataSource ) (string , error ) {
228
+ //Here is where we can get the "named" inventory, which is "Inventory Name"++"Organization Name" to derive uniqueness
229
+ //We will take precedence if the Id is set to use that over the named_url attempt.
230
+ if ! dm .Id .IsNull () {
231
+ return path .Join (datasource .client .getApiEndpoint (), "inventories" , dm .Id .String ()), nil
232
+ } else if ! dm .Name .IsNull () && ! dm .OrganizationName .IsNull () {
233
+ namedUrl := strings .Join ([]string {dm .Name .String ()[1 : len (dm .Name .String ())- 1 ], "++" , dm .OrganizationName .String ()[1 : len (dm .OrganizationName .String ())- 1 ]}, "" )
234
+ return path .Join (datasource .client .getApiEndpoint (), "inventories" , namedUrl ), nil
235
+ } else {
236
+ return types .StringNull ().String (), errors .New ("invalid inventory lookup parameters" )
237
+ }
238
+
239
+ }
0 commit comments