@@ -10,16 +10,34 @@ import (
1010	"net/http" 
1111	"os" 
1212	"strings" 
13+ 	"text/template" 
1314	"time" 
1415
1516	jsoniter "github.com/json-iterator/go" 
1617	"github.com/mattn/go-isatty" 
17- 	"github.com/sourcegraph/starlight" 
18- 	"github.com/sourcegraph/starlight/convert" 
19- 	"go.starlark.net/starlark" 
2018)
2119
22- type  validator  struct {
20+ type  validationSpec  struct  {
21+ 	FirstAdmin  struct  {
22+ 		Email     string 
23+ 		Username  string 
24+ 		Password  string 
25+ 	}
26+ 	WaitRepoCloned  struct  {
27+ 		Repo                      string 
28+ 		MaxTries                  int 
29+ 		SleepBetweenTriesSeconds  int 
30+ 	}
31+ 	SearchQuery      string 
32+ 	ExternalService  struct  {
33+ 		Kind            string 
34+ 		DisplayName     string 
35+ 		Config          * json.RawMessage 
36+ 		DeleteWhenDone  bool 
37+ 	}
38+ }
39+ 
40+ type  validator  struct  {
2341	client  * vdClient 
2442}
2543
4361
4462	var  (
4563		contextFlag  =  flagSet .String ("context" , "" , `Comma-separated list of key=value pairs to add to the script execution context` )
46- 		docFlag  =  flagSet .Bool ("doc" , false , `Show function  documentation` )
64+ 		docFlag       =  flagSet .Bool ("doc" , false , `Show documentation` )
4765		secretsFlag  =  flagSet .String ("secrets" , "" , "Path to a file containing key=value lines. The key value pairs will be added to the script context" )
4866	)
4967
93111}
94112
95113func  (vd  * validator ) printDocumentation () {
96- 	fmt .Println ("TODO(uwedeportivo): write function  documentation" )
114+ 	fmt .Println ("TODO(uwedeportivo): write documentation" )
97115}
98116
99117func  (vd  * validator ) parseKVPairs (val  string , pairSep  string ) map [string ]string  {
@@ -120,33 +138,63 @@ func (vd *validator) readSecrets(path string) (map[string]string, error) {
120138}
121139
122140func  (vd  * validator ) validate (script  []byte , scriptContext  map [string ]string ) error  {
123- 	globals  :=  map [string ]interface {}{
124- 		"src_list_external_services" : vd .listExternalServices ,
125- 		"src_add_external_service" : vd .addExternalService ,
126- 		"src_delete_external_service" : vd .deleteExternalService ,
127- 		"src_search_match_count" : vd .searchMatchCount ,
128- 		"src_list_cloned_repos" : vd .listClonedRepos ,
129- 		"src_wait_repo_cloned" : vd .waitRepoCloned ,
130- 		"src_sleep_seconds" : vd .sleepSeconds ,
131- 		"src_log" : vd .log ,
132- 		"src_run_graphql" : vd .runGraphQL ,
133- 		"src_context" : scriptContext ,
134- 		"src_create_first_admin" : vd .createFirstAdmin ,
135- 		"src_debug" : vd .debug ,
136- 	}
137- 
138- 	vals , err  :=  starlight .Eval (script , globals , nil )
141+ 	tpl , err  :=  template .New ("validate" ).Parse (string (script ))
142+ 	if  err  !=  nil  {
143+ 		return  err 
144+ 	}
145+ 	var  ts  bytes.Buffer 
146+ 	err  =  tpl .Execute (& ts , scriptContext )
139147	if  err  !=  nil  {
140148		return  err 
141149	}
142- 	if  passed , ok  :=  vals ["passed" ].(bool ); ! ok  ||  ! passed  {
143- 		return  errors .New ("failed" )
150+ 
151+ 	var  vspec  validationSpec 
152+ 	if  err  :=  json .Unmarshal (ts .Bytes (), & vspec ); err  !=  nil  {
153+ 		return  err 
154+ 	}
155+ 
156+ 	if  vspec .FirstAdmin .Username  !=  ""  {
157+ 		err  =  vd .createFirstAdmin (& vspec )
158+ 		if  err  !=  nil  {
159+ 			return  err 
160+ 		}
161+ 	}
162+ 
163+ 	if  vspec .ExternalService .DisplayName  !=  ""  {
164+ 		extSvcID , err  :=  vd .addExternalService (& vspec )
165+ 		if  err  !=  nil  {
166+ 			return  err 
167+ 		}
168+ 
169+ 		defer  func () {
170+ 			if  extSvcID  !=  ""  &&  vspec .ExternalService .DeleteWhenDone  {
171+ 				_  =  vd .deleteExternalService (extSvcID )
172+ 			}
173+ 		}()
144174	}
145- 	return  nil 
146- }
147175
148- func  (vd  * validator ) sleepSeconds (num  time.Duration ) {
149- 	time .Sleep (time .Second  *  num )
176+ 	if  vspec .WaitRepoCloned .Repo  !=  ""  {
177+ 		cloned , err  :=  vd .waitRepoCloned (vspec .WaitRepoCloned .Repo , vspec .WaitRepoCloned .SleepBetweenTriesSeconds ,
178+ 			vspec .WaitRepoCloned .MaxTries )
179+ 		if  err  !=  nil  {
180+ 			return  err 
181+ 		}
182+ 		if  ! cloned  {
183+ 			return  fmt .Errorf ("repo %s didn't clone" , vspec .WaitRepoCloned .Repo )
184+ 		}
185+ 	}
186+ 
187+ 	if  vspec .SearchQuery  !=  ""  {
188+ 		matchCount , err  :=  vd .searchMatchCount (vspec .SearchQuery )
189+ 		if  err  !=  nil  {
190+ 			return  err 
191+ 		}
192+ 		if  matchCount  ==  0  {
193+ 			return  fmt .Errorf ("search query %s returned no results" , vspec .SearchQuery )
194+ 		}
195+ 	}
196+ 
197+ 	return  nil 
150198}
151199
152200const  vdAddExternalServiceQuery  =  ` 
@@ -161,9 +209,8 @@ mutation AddExternalService($kind: ExternalServiceKind!, $displayName: String!,
161209  } 
162210}` 
163211
164- func  (vd  * validator ) addExternalService (kind , displayName , config  interface {}) (string , error ) {
165- 	dict  :=  vd .convertDict (config )
166- 	configJson , err  :=  json .MarshalIndent (dict , "" , "  " )
212+ func  (vd  * validator ) addExternalService (vspec  * validationSpec ) (string , error ) {
213+ 	configJson , err  :=  vspec .ExternalService .Config .MarshalJSON ()
167214	if  err  !=  nil  {
168215		return  "" , err 
169216	}
@@ -174,9 +221,9 @@ func (vd *validator) addExternalService(kind, displayName, config interface{}) (
174221	}
175222
176223	err  =  vd .graphQL (vdAddExternalServiceQuery , map [string ]interface {}{
177- 		"kind" : kind ,
178- 		"displayName" : displayName ,
179- 		"config" : string (configJson ),
224+ 		"kind" :         vspec . ExternalService . Kind ,
225+ 		"displayName" : vspec . ExternalService . DisplayName ,
226+ 		"config" :       string (configJson ),
180227	}, & resp )
181228
182229	return  resp .AddExternalService .ID , err 
@@ -190,7 +237,7 @@ mutation DeleteExternalService($id: ID!) {
190237}` 
191238
192239func  (vd  * validator ) deleteExternalService (id  string ) error  {
193- 	var  resp  struct   {}
240+ 	var  resp  struct {}
194241
195242	return  vd .graphQL (vdDeleteExternalServiceQuery , map [string ]interface {}{
196243		"id" : id ,
@@ -236,7 +283,7 @@ query ListRepos($cloneInProgress: Boolean!, $cloned: Boolean!, $notCloned: Boole
236283  } 
237284}` 
238285
239- func  (vd  * validator ) listClonedReposImpl (fs  []string ) ([]string , error ) {
286+ func  (vd  * validator ) listClonedRepos (fs  []string ) ([]string , error ) {
240287	var  resp  struct  {
241288		Repositories  struct  {
242289			Nodes  []struct  {
@@ -247,9 +294,9 @@ func (vd *validator) listClonedReposImpl(fs []string) ([]string, error) {
247294
248295	err  :=  vd .graphQL (vdListRepos , map [string ]interface {}{
249296		"cloneInProgress" : true ,
250- 		"cloned" : true ,
251- 		"notCloned" : true ,
252- 		"names" : fs ,
297+ 		"cloned" :           true ,
298+ 		"notCloned" :        true ,
299+ 		"names" :            fs ,
253300	}, & resp )
254301
255302	names  :=  make ([]string , 0 , len (resp .Repositories .Nodes ))
@@ -260,16 +307,11 @@ func (vd *validator) listClonedReposImpl(fs []string) ([]string, error) {
260307	return  names , err 
261308}
262309
263- func  (vd  * validator ) listClonedRepos (filterNames  interface {}) ([]string , error ) {
264- 	fs  :=  vd .convertStringList (filterNames )
265- 	return  vd .listClonedReposImpl (fs )
266- }
267- 
268310func  (vd  * validator ) waitRepoCloned (repoName  string , sleepSeconds  int , maxTries  int ) (bool , error ) {
269311	nameFilter  :=  []string {repoName }
270312
271313	for  i  :=  0 ; i  <  maxTries ; i ++  {
272- 		names , err  :=  vd .listClonedReposImpl (nameFilter )
314+ 		names , err  :=  vd .listClonedRepos (nameFilter )
273315		if  err  !=  nil  {
274316			return  false , err 
275317		}
@@ -281,140 +323,6 @@ func (vd *validator) waitRepoCloned(repoName string, sleepSeconds int, maxTries
281323	return  false , nil 
282324}
283325
284- func  (vd  * validator ) log (line  string ) {
285- 	fmt .Println (line )
286- }
287- 
288- func  (vd  * validator ) runGraphQL (query  string , vars  interface {}) (starlark.Value , error ) {
289- 	resp  :=  map [string ]interface {}{}
290- 	cvars  :=  vd .convertDict (vars )
291- 
292- 	err  :=  vd .graphQL (query , cvars , & resp )
293- 	if  err  !=  nil  {
294- 		return  nil , err 
295- 	}
296- 	return  vd .makeDict (resp )
297- }
298- 
299- const  vdListExternalServices  =  ` 
300- query ExternalServices { 
301-   externalServices { 
302-     nodes { 
303-       id 
304-       displayName 
305-     } 
306-   } 
307- }` 
308- 
309- func  (vd  * validator ) listExternalServices () ([]map [string ]string , error ) {
310- 	var  resp  struct  {
311- 		ExternalServices  struct  {
312- 			Nodes  []struct  {
313- 				DisplayName  string  `json:"displayName"` 
314- 				ID  string  `json:"id"` 
315- 			} `json:"nodes"` 
316- 		} `json:"externalServices"` 
317- 	}
318- 
319- 	err  :=  vd .graphQL (vdListExternalServices , map [string ]interface {}{}, & resp )
320- 	if  err  !=  nil  {
321- 		return  nil , err 
322- 	}
323- 
324- 	xs  :=  make ([]map [string ]string , 0 , len (resp .ExternalServices .Nodes ))
325- 	for  _ , es  :=  range  resp .ExternalServices .Nodes  {
326- 		xs  =  append (xs , map [string ]string {"id" : es .ID , "displayName" : es .DisplayName })
327- 	}
328- 
329- 	return  xs , nil 
330- }
331- 
332- func  (vd  * validator ) makeDict (m  map [string ]interface {}) (starlark.Value , error ) {
333- 	dict  :=  starlark.Dict {}
334- 
335- 	for  k , v  :=  range  m  {
336- 		var  sv  starlark.Value 
337- 		var  err  error 
338- 
339- 		sk  :=  starlark .String (k )
340- 		if  cv , ok  :=  v .(map [string ]interface {}); ok  {
341- 			sv , err  =  vd .makeDict (cv )
342- 			if  err  !=  nil  {
343- 				return  nil , err 
344- 			}
345- 		} else  {
346- 			sv , err  =  convert .ToValue (v )
347- 			if  err  !=  nil  {
348- 				return  nil , err 
349- 			}
350- 		}
351- 		err  =  dict .SetKey (sk , sv )
352- 		if  err  !=  nil  {
353- 			return  nil , err 
354- 		}
355- 	}
356- 	return  & dict , nil 
357- }
358- 
359- func  (vd  * validator ) convertDict (val  interface {}) map [string ]interface {} {
360- 	dict  :=  val .(map [interface {}]interface {})
361- 	res  :=  make (map [string ]interface {})
362- 
363- 	for  k , v  :=  range  dict  {
364- 		gk  :=  vd .fromStarLark (k ).(string )
365- 		gv  :=  vd .fromStarLark (v )
366- 
367- 		res [gk ] =  gv 
368- 	}
369- 	return  res 
370- }
371- 
372- func  (vd  * validator ) convertStringList (val  interface {}) []string  {
373- 	list  :=  val .([]interface {})
374- 	res  :=  make ([]string , 0 , len (list ))
375- 
376- 	for  _ , v  :=  range  list  {
377- 		gv  :=  v .(string )
378- 
379- 		res  =  append (res , gv )
380- 	}
381- 	return  res 
382- }
383- 
384- func  (vd  * validator ) fromStarLark (v  interface {}) interface {} {
385- 	switch  v  :=  v .(type ) {
386- 	case  starlark.Bool :
387- 		return  bool (v )
388- 	case  starlark.Int :
389- 		// starlark ints can be signed or unsigned 
390- 		if  i , ok  :=  v .Int64 (); ok  {
391- 			return  i 
392- 		}
393- 		if  i , ok  :=  v .Uint64 (); ok  {
394- 			return  i 
395- 		}
396- 		// buh... maybe > maxint64?  Dunno 
397- 		panic (fmt .Errorf ("can't convert starlark.Int %q to int" , v ))
398- 	case  starlark.Float :
399- 		return  float64 (v )
400- 	case  starlark.String :
401- 		return  string (v )
402- 	case  * starlark.List :
403- 		return  convert .FromList (v )
404- 	case  starlark.Tuple :
405- 		return  convert .FromTuple (v )
406- 	case  * starlark.Dict :
407- 		return  convert .FromDict (v )
408- 	case  * starlark.Set :
409- 		return  convert .FromSet (v )
410- 	default :
411- 		// dunno, hope it's a custom type that the receiver knows how to deal 
412- 		// with. This can happen with custom-written go types that implement 
413- 		// starlark.Value. 
414- 		return  v 
415- 	}
416- }
417- 
418326// SiteAdminInit initializes the instance with given admin account. 
419327// It returns an authenticated client as the admin for doing e2e testing. 
420328func  (vd  * validator ) siteAdminInit (baseURL , email , username , password  string ) (* vdClient , error ) {
@@ -662,10 +570,11 @@ func (c *vdClient) graphQL(token, query string, variables map[string]interface{}
662570	return  nil 
663571}
664572
665- func  (vd  * validator ) createFirstAdmin (email ,  username ,  password   string ) error  {
666- 	client , err  :=  vd .signIn (cfg .Endpoint , email ,  password )
573+ func  (vd  * validator ) createFirstAdmin (vspec   * validationSpec ) error  {
574+ 	client , err  :=  vd .signIn (cfg .Endpoint , vspec . FirstAdmin . Email ,  vspec . FirstAdmin . Password )
667575	if  err  !=  nil  {
668- 		client , err  =  vd .siteAdminInit (cfg .Endpoint , email , username , password )
576+ 		client , err  =  vd .siteAdminInit (cfg .Endpoint , vspec .FirstAdmin .Email , vspec .FirstAdmin .Username ,
577+ 			vspec .FirstAdmin .Password )
669578		if  err  !=  nil  {
670579			return  err 
671580		}
@@ -681,12 +590,8 @@ func (vd *validator) graphQL(query string, variables map[string]interface{}, tar
681590	}
682591
683592	return  (& apiRequest {
684- 		query : query ,
685- 		vars :variables ,
593+ 		query :   query ,
594+ 		vars :    variables ,
686595		result : target ,
687596	}).do ()
688597}
689- 
690- func  (vd  * validator ) debug (val  interface {}) {
691- 	fmt .Printf ("%+v\n " , val )
692- }
0 commit comments