@@ -2,7 +2,10 @@ package client
2
2
3
3
import (
4
4
"context"
5
+ "encoding/json"
6
+ "errors"
5
7
"fmt"
8
+ "strings"
6
9
7
10
"github.com/hashicorp/terraform-plugin-log/tflog"
8
11
)
@@ -60,13 +63,55 @@ func (c *Client) CreateEnvironmentVariable(ctx context.Context, request CreateEn
60
63
}
61
64
62
65
if err != nil {
66
+ // Try to parse detailed failure information from the API error response
67
+ var apiErr APIError
68
+ if errors .As (err , & apiErr ) && len (apiErr .RawMessage ) > 0 {
69
+ var parsed struct {
70
+ Error struct {
71
+ Code string `json:"code"`
72
+ Message string `json:"message"`
73
+ } `json:"error"`
74
+ Failed []FailedItem `json:"failed"`
75
+ }
76
+ if json .Unmarshal (apiErr .RawMessage , & parsed ) == nil && len (parsed .Failed ) > 0 {
77
+ // Prefer the first failed item for single create
78
+ f := parsed .Failed [0 ]
79
+ // If it's a conflict, try to locate the conflicting env ID for a better message
80
+ if f .Error .Code == "ENV_CONFLICT" {
81
+ envs , errList := c .GetEnvironmentVariables (ctx , request .ProjectID , request .TeamID )
82
+ if errList == nil {
83
+ id , found := findConflictingEnvID (request .TeamID , request .ProjectID , EnvConflictError {
84
+ Key : derefString (f .Error .EnvVarKey ),
85
+ Target : f .Error .Target ,
86
+ GitBranch : f .Error .GitBranch ,
87
+ }, envs )
88
+ if found {
89
+ return e , fmt .Errorf ("failed to create environment variable, %s, conflicting environment variable ID is %s" , f .Error .Message , id )
90
+ }
91
+ }
92
+ }
93
+ // Fallback to the message returned by the API for clarity
94
+ msg := f .Error .Message
95
+ if f .Error .Link != nil && * f .Error .Link != "" {
96
+ msg = fmt .Sprintf ("%s (see %s)" , msg , * f .Error .Link )
97
+ }
98
+ return e , fmt .Errorf ("failed to create environment variable, %s" , msg )
99
+ }
100
+ }
63
101
return e , fmt .Errorf ("%w - %s" , err , payload )
64
102
}
65
103
response .Created .Value = request .EnvironmentVariable .Value
66
104
response .Created .TeamID = c .TeamID (request .TeamID )
67
105
return response .Created , err
68
106
}
69
107
108
+ func derefString (p * string ) string {
109
+ if p == nil {
110
+ return ""
111
+ }
112
+ return * p
113
+ }
114
+
70
115
func overlaps (s []string , e []string ) bool {
71
116
set := make (map [string ]struct {}, len (s ))
72
117
for _ , a := range s {
@@ -159,37 +204,102 @@ func (c *Client) CreateEnvironmentVariables(ctx context.Context, request CreateE
159
204
body : payload ,
160
205
}, & response )
161
206
if err != nil {
207
+ // Attempt to parse detailed failure reasons from API error body
208
+ var apiErr APIError
209
+ if errors .As (err , & apiErr ) && len (apiErr .RawMessage ) > 0 {
210
+ var parsed struct {
211
+ Error struct {
212
+ Code string `json:"code"`
213
+ Message string `json:"message"`
214
+ } `json:"error"`
215
+ Failed []FailedItem `json:"failed"`
216
+ }
217
+ if json .Unmarshal (apiErr .RawMessage , & parsed ) == nil && len (parsed .Failed ) > 0 {
218
+ // Optionally fetch envs once to augment conflict errors with IDs
219
+ var envs []EnvironmentVariable
220
+ var listErr error
221
+ needsList := false
222
+ for _ , f := range parsed .Failed {
223
+ if f .Error .Code == "ENV_CONFLICT" {
224
+ needsList = true
225
+ break
226
+ }
227
+ }
228
+ if needsList {
229
+ envs , listErr = c .GetEnvironmentVariables (ctx , request .ProjectID , request .TeamID )
230
+ }
231
+ msgs := make ([]string , 0 , len (parsed .Failed ))
232
+ for _ , f := range parsed .Failed {
233
+ msg := f .Error .Message
234
+ if f .Error .Code == "ENV_CONFLICT" && listErr == nil {
235
+ id , found := findConflictingEnvID (request .TeamID , request .ProjectID , EnvConflictError {
236
+ Key : derefString (f .Error .EnvVarKey ),
237
+ Target : f .Error .Target ,
238
+ GitBranch : f .Error .GitBranch ,
239
+ }, envs )
240
+ if found {
241
+ msg = fmt .Sprintf ("%s, conflicting environment variable ID is %s" , msg , id )
242
+ }
243
+ }
244
+ if f .Error .Link != nil && * f .Error .Link != "" {
245
+ msg = fmt .Sprintf ("%s (see %s)" , msg , * f .Error .Link )
246
+ }
247
+ msgs = append (msgs , msg )
248
+ }
249
+ // de-duplicate while preserving order
250
+ seen := make (map [string ]struct {}, len (msgs ))
251
+ uniq := make ([]string , 0 , len (msgs ))
252
+ for _ , m := range msgs {
253
+ if _ , ok := seen [m ]; ! ok {
254
+ seen [m ] = struct {}{}
255
+ uniq = append (uniq , m )
256
+ }
257
+ }
258
+ return nil , fmt .Errorf ("failed to create environment variables, %s" , strings .Join (uniq , "; " ))
259
+ }
260
+ }
162
261
return nil , fmt .Errorf ("%w - %s" , err , payload )
163
262
}
164
263
165
264
decrypted := false
166
- for i := 0 ; i < len ( response .Created ); i ++ {
265
+ for i := range response .Created {
167
266
// When env vars are created, their values are encrypted
168
267
response .Created [i ].Decrypted = & decrypted
169
268
}
170
269
171
270
if len (response .Failed ) > 0 {
172
- envs , err := c .GetEnvironmentVariables (ctx , request .ProjectID , request .TeamID )
173
- if err != nil {
174
- return response .Created , fmt .Errorf ("failed to create environment variables. error detecting conflicting environment variables: %w" , err )
271
+ envs , listErr := c .GetEnvironmentVariables (ctx , request .ProjectID , request .TeamID )
272
+ if listErr != nil {
273
+ return response .Created , fmt .Errorf ("failed to create environment variables. error detecting conflicting environment variables: %w" , listErr )
175
274
}
275
+ msgs := make ([]string , 0 , len (response .Failed ))
176
276
for _ , failed := range response .Failed {
277
+ msg := failed .Error .Message
177
278
if failed .Error .Code == "ENV_CONFLICT" {
178
279
id , found := findConflictingEnvID (request .TeamID , request .ProjectID , EnvConflictError {
179
- Key : * failed .Error .EnvVarKey ,
280
+ Key : derefString ( failed .Error .EnvVarKey ) ,
180
281
Target : failed .Error .Target ,
181
282
GitBranch : failed .Error .GitBranch ,
182
283
}, envs )
183
284
if found {
184
- err = fmt .Errorf ("%w, conflicting environment variable ID is %s" , err , id )
185
- } else {
186
- err = fmt .Errorf ("failed to create environment variables, %s" , failed .Error .Message )
285
+ msg = fmt .Sprintf ("%s, conflicting environment variable ID is %s" , msg , id )
187
286
}
188
- } else {
189
- err = fmt .Errorf ("failed to create environment variables, %s" , failed .Error .Message )
287
+ }
288
+ if failed .Error .Link != nil && * failed .Error .Link != "" {
289
+ msg = fmt .Sprintf ("%s (see %s)" , msg , * failed .Error .Link )
290
+ }
291
+ msgs = append (msgs , msg )
292
+ }
293
+ // de-duplicate while preserving order
294
+ seen := make (map [string ]struct {}, len (msgs ))
295
+ uniq := make ([]string , 0 , len (msgs ))
296
+ for _ , m := range msgs {
297
+ if _ , ok := seen [m ]; ! ok {
298
+ seen [m ] = struct {}{}
299
+ uniq = append (uniq , m )
190
300
}
191
301
}
192
- return response .Created , err
302
+ return response .Created , fmt . Errorf ( "failed to create environment variables, %s" , strings . Join ( uniq , "; " ))
193
303
}
194
304
195
305
return response .Created , err
0 commit comments