1
1
package launchdarkly
2
2
3
3
import (
4
+ "context"
4
5
_ "embed"
5
6
"encoding/json"
6
7
"errors"
7
8
"fmt"
8
9
"io"
9
10
"net/http"
10
- "strings "
11
+ "strconv "
11
12
"sync"
12
13
"time"
13
14
)
14
15
16
+ const defaultTimeout = 5 * time .Second
17
+
15
18
var (
16
19
baseURL = "https://app.launchdarkly.com/api"
17
20
18
21
endpoints = map [string ]string {
22
+ // user information APIs
19
23
"callerIdentity" : "/v2/caller-identity" ,
20
24
"getToken" : "/v2/tokens/%s" , // require token id
21
25
"getRole" : "/v2/roles/%s" , // require role id
22
- applicationKey : "/v2/applications" ,
23
- repositoryKey : "/v2/code-refs/repositories" ,
24
- projectKey : "/v2/projects" ,
25
- environmentKey : "/v2/projects/%s/environments" , // require project key
26
- featureFlagsKey : "/v2/flags/%s" , // require project key
27
- experimentKey : "/v2/projects/%s/environments/%s/experiments" , // require project key and env key
28
- holdoutsKey : "/v2/projects/%s/environments/%s/holdouts" , // require project key and env key
29
- membersKey : "/v2/members" ,
30
- destinationsKey : "/v2/destinations" ,
31
- templatesKey : "/v2/templates" ,
32
- teamsKey : "/v2/teams" ,
33
- webhooksKey : "/v2/webhooks" ,
26
+ // resource APIs
27
+ applicationKey : "/v2/applications" ,
28
+ repositoryKey : "/v2/code-refs/repositories" ,
29
+ projectKey : "/v2/projects" ,
30
+ environmentKey : "/v2/projects/%s/environments" , // require project key
31
+ featureFlagsKey : "/v2/flags/%s" , // require project key
32
+ experimentKey : "/v2/projects/%s/environments/%s/experiments" , // require project key and env key
33
+ holdoutsKey : "/v2/projects/%s/environments/%s/holdouts" , // require project key and env key
34
+ membersKey : "/v2/members" ,
35
+ destinationsKey : "/v2/destinations" ,
36
+ templatesKey : "/v2/templates" ,
37
+ teamsKey : "/v2/teams" ,
38
+ webhooksKey : "/v2/webhooks" ,
34
39
/*
35
40
TODO:
36
41
release piplelines: https://launchdarkly.com/docs/api/release-pipelines-beta/get-all-release-pipelines (Beta)
@@ -169,8 +174,11 @@ type webhooksResponse struct {
169
174
170
175
// makeLaunchDarklyRequest send the HTTP GET API request to passed url with passed token and return response body and status code
171
176
func makeLaunchDarklyRequest (client * http.Client , endpoint , token string ) ([]byte , int , error ) {
177
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTimeout )
178
+ defer cancel ()
179
+
172
180
// create request
173
- req , err := http .NewRequest ( http .MethodGet , baseURL + endpoint , http .NoBody )
181
+ req , err := http .NewRequestWithContext ( ctx , http .MethodGet , baseURL + endpoint , http .NoBody )
174
182
if err != nil {
175
183
return nil , 0 , err
176
184
}
@@ -200,16 +208,15 @@ func makeLaunchDarklyRequest(client *http.Client, endpoint, token string) ([]byt
200
208
func CaptureResources (client * http.Client , token string , secretInfo * SecretInfo ) error {
201
209
var (
202
210
wg sync.WaitGroup
203
- errChan = make (chan error )
204
- aggregatedErrs = make ([]string , 0 )
211
+ aggregatedErrs = make ([]error , 0 )
205
212
)
206
213
207
214
wg .Add (1 )
208
215
go func () {
209
216
defer wg .Done ()
210
217
211
218
if err := captureApplications (client , token , secretInfo ); err != nil {
212
- errChan <- err
219
+ aggregatedErrs = append ( aggregatedErrs , err )
213
220
}
214
221
}()
215
222
@@ -218,7 +225,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
218
225
defer wg .Done ()
219
226
220
227
if err := captureRepositories (client , token , secretInfo ); err != nil {
221
- errChan <- err
228
+ aggregatedErrs = append ( aggregatedErrs , err )
222
229
}
223
230
}()
224
231
@@ -227,7 +234,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
227
234
defer wg .Done ()
228
235
229
236
if err := captureProjects (client , token , secretInfo ); err != nil {
230
- errChan <- err
237
+ aggregatedErrs = append ( aggregatedErrs , err )
231
238
}
232
239
233
240
// for each project capture it's flags, environments and other sub resources
@@ -237,15 +244,15 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
237
244
go func () {
238
245
defer wg .Done ()
239
246
if err := captureProjectFeatureFlags (client , token , project , secretInfo ); err != nil {
240
- errChan <- err
247
+ aggregatedErrs = append ( aggregatedErrs , err )
241
248
}
242
249
}()
243
250
244
251
wg .Add (1 )
245
252
go func () {
246
253
defer wg .Done ()
247
254
if err := captureProjectEnv (client , token , project , secretInfo ); err != nil {
248
- errChan <- err
255
+ aggregatedErrs = append ( aggregatedErrs , err )
249
256
}
250
257
}()
251
258
}
@@ -256,7 +263,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
256
263
defer wg .Done ()
257
264
258
265
if err := captureMembers (client , token , secretInfo ); err != nil {
259
- errChan <- err
266
+ aggregatedErrs = append ( aggregatedErrs , err )
260
267
}
261
268
}()
262
269
@@ -265,7 +272,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
265
272
defer wg .Done ()
266
273
267
274
if err := captureDestinations (client , token , secretInfo ); err != nil {
268
- errChan <- err
275
+ aggregatedErrs = append ( aggregatedErrs , err )
269
276
}
270
277
}()
271
278
@@ -274,7 +281,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
274
281
defer wg .Done ()
275
282
276
283
if err := captureTemplates (client , token , secretInfo ); err != nil {
277
- errChan <- err
284
+ aggregatedErrs = append ( aggregatedErrs , err )
278
285
}
279
286
}()
280
287
@@ -283,7 +290,7 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
283
290
defer wg .Done ()
284
291
285
292
if err := captureTeams (client , token , secretInfo ); err != nil {
286
- errChan <- err
293
+ aggregatedErrs = append ( aggregatedErrs , err )
287
294
}
288
295
}()
289
296
@@ -292,20 +299,14 @@ func CaptureResources(client *http.Client, token string, secretInfo *SecretInfo)
292
299
defer wg .Done ()
293
300
294
301
if err := captureWebhooks (client , token , secretInfo ); err != nil {
295
- errChan <- err
302
+ aggregatedErrs = append ( aggregatedErrs , err )
296
303
}
297
304
}()
298
305
299
306
wg .Wait ()
300
- close (errChan )
301
-
302
- // collect all errors
303
- for err := range errChan {
304
- aggregatedErrs = append (aggregatedErrs , err .Error ())
305
- }
306
307
307
308
if len (aggregatedErrs ) > 0 {
308
- return errors .New ( strings . Join (aggregatedErrs , ", " ) )
309
+ return errors .Join (aggregatedErrs ... )
309
310
}
310
311
311
312
return nil
@@ -331,12 +332,13 @@ func captureApplications(client *http.Client, token string, secretInfo *SecretIn
331
332
ID : fmt .Sprintf ("launchdarkly/app/%s" , application .Key ),
332
333
Name : application .Name ,
333
334
Type : applicationKey ,
335
+ MetaData : map [string ]string {
336
+ "Maintainer Email" : application .Maintainer .Email ,
337
+ "Kind" : application .Kind ,
338
+ MetadataKey : application .Key ,
339
+ },
334
340
}
335
341
336
- resource .updateResourceMetadata ("Maintainer Email" , application .Maintainer .Email )
337
- resource .updateResourceMetadata ("Kind" , application .Kind )
338
- resource .updateResourceMetadata (MetadataKey , application .Key )
339
-
340
342
secretInfo .appendResource (resource )
341
343
}
342
344
@@ -368,12 +370,14 @@ func captureRepositories(client *http.Client, token string, secretInfo *SecretIn
368
370
ID : fmt .Sprintf ("%s/repo/%s/%d" , repository .Type , repository .Name , repository .Version ), // no unique id exist, so we make one
369
371
Name : repository .Name ,
370
372
Type : repositoryKey ,
373
+ MetaData : map [string ]string {
374
+ "Default branch" : repository .DefaultBranch ,
375
+ "Version" : strconv .Itoa (repository .Version ),
376
+ "Source link" : repository .SourceLink ,
377
+ MetadataKey : repositoryKey ,
378
+ },
371
379
}
372
380
373
- resource .updateResourceMetadata ("Default branch" , repository .DefaultBranch )
374
- resource .updateResourceMetadata ("Version" , fmt .Sprintf ("%d" , repository .Version ))
375
- resource .updateResourceMetadata ("Source link" , repository .SourceLink )
376
-
377
381
secretInfo .appendResource (resource )
378
382
}
379
383
@@ -444,11 +448,12 @@ func captureProjectFeatureFlags(client *http.Client, token string, parent Resour
444
448
ID : fmt .Sprintf ("launchdarkly/proj/%s/flag/%s" , projectKey , flag .Key ),
445
449
Name : flag .Name ,
446
450
Type : featureFlagsKey ,
451
+ MetaData : map [string ]string {
452
+ "Kind" : flag .Kind ,
453
+ },
454
+ ParentResource : & parent ,
447
455
}
448
456
449
- resource .updateResourceMetadata ("Kind" , flag .Kind )
450
- resource .setParentResource (& resource , & parent )
451
-
452
457
secretInfo .appendResource (resource )
453
458
}
454
459
@@ -488,10 +493,9 @@ func captureProjectEnv(client *http.Client, token string, parent Resource, secre
488
493
MetaData : map [string ]string {
489
494
MetadataKey : env .Key ,
490
495
},
496
+ ParentResource : & parent ,
491
497
}
492
498
493
- resource .setParentResource (& resource , & parent )
494
-
495
499
secretInfo .appendResource (resource )
496
500
497
501
// capture project env child resources
@@ -538,14 +542,12 @@ func captureProjectEnvExperiments(client *http.Client, token string, projectKey
538
542
Name : exp .Name ,
539
543
Type : experimentKey ,
540
544
MetaData : map [string ]string {
541
- MetadataKey : exp .Key ,
545
+ MetadataKey : exp .Key ,
546
+ "Maintiner ID" : exp .MaintainerID ,
542
547
},
548
+ ParentResource : & parent ,
543
549
}
544
550
545
- resource .updateResourceMetadata (MetadataKey , exp .Key )
546
- resource .updateResourceMetadata ("Maintainer ID" , exp .MaintainerID )
547
-
548
- resource .setParentResource (& resource , & parent )
549
551
secretInfo .appendResource (resource )
550
552
}
551
553
@@ -585,13 +587,13 @@ func captureProjectHoldouts(client *http.Client, token string, projectKey string
585
587
ID : fmt .Sprintf ("launchdarkly/%s/env/%s/holdout/%s" , projectKey , envKey , holdout .ID ),
586
588
Name : holdout .Name ,
587
589
Type : holdoutsKey ,
590
+ MetaData : map [string ]string {
591
+ "Status" : holdout .Status ,
592
+ holdoutsKey : holdout .Key ,
593
+ },
594
+ ParentResource : & parent ,
588
595
}
589
596
590
- resource .updateResourceMetadata ("Status" , holdout .Status )
591
- resource .updateResourceMetadata (holdoutsKey , holdout .Key )
592
-
593
- resource .setParentResource (& resource , & parent )
594
-
595
597
secretInfo .appendResource (resource )
596
598
}
597
599
@@ -623,11 +625,12 @@ func captureMembers(client *http.Client, token string, secretInfo *SecretInfo) e
623
625
ID : fmt .Sprintf ("launchdarkly/member/%s" , member .ID ),
624
626
Name : member .FirstName + " " + member .LastName ,
625
627
Type : membersKey ,
628
+ MetaData : map [string ]string {
629
+ "Role" : member .Role ,
630
+ "Email" : member .Email ,
631
+ },
626
632
}
627
633
628
- resource .updateResourceMetadata ("Role" , member .Role )
629
- resource .updateResourceMetadata ("Email" , member .Email )
630
-
631
634
secretInfo .appendResource (resource )
632
635
}
633
636
@@ -659,11 +662,12 @@ func captureDestinations(client *http.Client, token string, secretInfo *SecretIn
659
662
ID : fmt .Sprintf ("launchdarkly/destination/%s" , destination .ID ),
660
663
Name : destination .Name ,
661
664
Type : destinationsKey ,
665
+ MetaData : map [string ]string {
666
+ "Kind" : destination .Kind ,
667
+ "Version" : strconv .Itoa (destination .Version ),
668
+ },
662
669
}
663
670
664
- resource .updateResourceMetadata ("Kind" , destination .Kind )
665
- resource .updateResourceMetadata ("Version" , fmt .Sprintf ("%d" , destination .Version ))
666
-
667
671
secretInfo .appendResource (resource )
668
672
}
669
673
@@ -728,12 +732,13 @@ func captureTeams(client *http.Client, token string, secretInfo *SecretInfo) err
728
732
ID : fmt .Sprintf ("launchdarkly/teams/%s" , team .Key ),
729
733
Name : team .Name ,
730
734
Type : teamsKey ,
735
+ MetaData : map [string ]string {
736
+ "Total Roles Count" : strconv .Itoa (team .Roles .TotalCount ),
737
+ "Total Memvers Count" : strconv .Itoa (team .Members .TotalCount ),
738
+ "Total Projects Count" : strconv .Itoa (team .Projects .TotalCount ),
739
+ },
731
740
}
732
741
733
- resource .updateResourceMetadata ("Total Roles Count" , fmt .Sprintf ("%d" , team .Roles .TotalCount ))
734
- resource .updateResourceMetadata ("Total Members Count" , fmt .Sprintf ("%d" , team .Members .TotalCount ))
735
- resource .updateResourceMetadata ("Total Projects Count" , fmt .Sprintf ("%d" , team .Projects .TotalCount ))
736
-
737
742
secretInfo .appendResource (resource )
738
743
}
739
744
0 commit comments