github.com/pluralsh/plural-cli@v0.9.5/pkg/api/recipes.go (about) 1 package api 2 3 import ( 4 "fmt" 5 6 "github.com/pluralsh/gqlclient" 7 "github.com/pluralsh/gqlclient/pkg/utils" 8 9 "sigs.k8s.io/yaml" 10 ) 11 12 type RecipeInput struct { 13 Name string 14 Description string 15 Provider string 16 Restricted bool 17 Primary bool 18 Tests []RecipeTestInput `yaml:"tests" json:"tests,omitempty"` 19 Sections []RecipeSectionInput 20 Dependencies []DependencyInput 21 OidcSettings *OIDCSettings `yaml:"oidcSettings,omitempty"` 22 } 23 24 type DependencyInput struct { 25 Name string 26 Repo string 27 } 28 29 type RecipeTestInput struct { 30 Name string 31 Message string 32 Type string 33 Args []*TestArgInput 34 } 35 36 type TestArgInput struct { 37 Name string 38 Repo string 39 Key string 40 } 41 42 type RecipeSectionInput struct { 43 Name string 44 Items []RecipeItemInput 45 Configuration []ConfigurationItemInput 46 } 47 48 type RecipeItemInput struct { 49 Name string 50 Type string 51 } 52 53 type ConditionInput struct { 54 Field string 55 Value string 56 Operation string 57 } 58 59 type ValidationInput struct { 60 Type string 61 Regex string 62 Message string 63 } 64 65 type ConfigurationItemInput struct { 66 Name string 67 Default string 68 Type string 69 Documentation string 70 Placeholder string 71 Longform string 72 Optional bool 73 FunctionName string `yaml:"functionName,omitempty" json:"functionName,omitempty"` 74 Condition *ConditionInput 75 Validation *ValidationInput 76 } 77 78 type RecipeEdge struct { 79 Node *Recipe 80 } 81 82 func (client *client) CreateRecipe(repoName string, attrs gqlclient.RecipeAttributes) (string, error) { 83 if len(attrs.Tests) == 0 { 84 attrs.Tests = make([]*gqlclient.RecipeTestAttributes, 0) 85 } 86 resp, err := client.pluralClient.CreateRecipe(client.ctx, repoName, attrs) 87 if err != nil { 88 return "", err 89 } 90 91 return resp.CreateRecipe.ID, err 92 } 93 94 func (client *client) GetRecipe(repo, name string) (*Recipe, error) { 95 resp, err := client.pluralClient.GetRecipe(client.ctx, &repo, &name) 96 if err != nil { 97 return nil, err 98 } 99 100 r := &Recipe{ 101 Id: resp.Recipe.ID, 102 Name: resp.Recipe.Name, 103 Primary: resp.Recipe.Primary != nil && *resp.Recipe.Primary, 104 Restricted: resp.Recipe.Restricted != nil && *resp.Recipe.Restricted, 105 Provider: string(*resp.Recipe.Provider), 106 Description: utils.ConvertStringPointer(resp.Recipe.Description), 107 Tests: []*RecipeTest{}, 108 RecipeSections: []*RecipeSection{}, 109 RecipeDependencies: []*Recipe{}, 110 } 111 if resp.Recipe.OidcSettings != nil { 112 r.OidcSettings = &OIDCSettings{ 113 DomainKey: utils.ConvertStringPointer(resp.Recipe.OidcSettings.DomainKey), 114 UriFormat: utils.ConvertStringPointer(resp.Recipe.OidcSettings.URIFormat), 115 UriFormats: utils.ConvertStringArrayPointer(resp.Recipe.OidcSettings.URIFormats), 116 AuthMethod: string(resp.Recipe.OidcSettings.AuthMethod), 117 } 118 if resp.Recipe.OidcSettings.Subdomain != nil { 119 r.OidcSettings.Subdomain = *resp.Recipe.OidcSettings.Subdomain 120 } 121 } 122 if resp.Recipe.Repository != nil { 123 r.Repository = &Repository{ 124 Id: resp.Recipe.Repository.ID, 125 Name: resp.Recipe.Repository.Name, 126 } 127 } 128 129 for _, dep := range resp.Recipe.RecipeDependencies { 130 r.RecipeDependencies = append(r.RecipeDependencies, convertRecipe(dep)) 131 } 132 133 for _, section := range resp.Recipe.RecipeSections { 134 rs := &RecipeSection{ 135 Id: fmt.Sprint(section.Index), 136 Repository: &Repository{ 137 Id: section.Repository.ID, 138 Name: section.Repository.Name, 139 Description: utils.ConvertStringPointer(section.Repository.Description), 140 Icon: utils.ConvertStringPointer(section.Repository.Icon), 141 DarkIcon: utils.ConvertStringPointer(section.Repository.DarkIcon), 142 Notes: utils.ConvertStringPointer(section.Repository.Notes), 143 }, 144 RecipeItems: []*RecipeItem{}, 145 Configuration: []*ConfigurationItem{}, 146 } 147 for _, conf := range section.Configuration { 148 rs.Configuration = append(rs.Configuration, convertConfigurationItem(conf)) 149 } 150 for _, recipeItem := range section.RecipeItems { 151 rs.RecipeItems = append(rs.RecipeItems, convertRecipeItem(recipeItem)) 152 } 153 154 r.RecipeSections = append(r.RecipeSections, rs) 155 156 } 157 158 for _, test := range resp.Recipe.Tests { 159 t := &RecipeTest{ 160 Name: test.Name, 161 Type: string(test.Type), 162 Message: utils.ConvertStringPointer(test.Message), 163 Args: []*TestArgument{}, 164 } 165 for _, arg := range test.Args { 166 t.Args = append(t.Args, &TestArgument{ 167 Name: arg.Name, 168 Repo: arg.Repo, 169 Key: arg.Key, 170 }) 171 } 172 173 r.Tests = append(r.Tests, t) 174 } 175 176 return r, nil 177 } 178 179 func (client *client) GetRecipeByID(id string) (*Recipe, error) { 180 resp, err := client.pluralClient.GetRecipeByID(client.ctx, &id) 181 if err != nil { 182 return nil, err 183 } 184 185 r := &Recipe{ 186 Id: resp.Recipe.ID, 187 Name: resp.Recipe.Name, 188 Provider: string(*resp.Recipe.Provider), 189 Description: utils.ConvertStringPointer(resp.Recipe.Description), 190 Tests: []*RecipeTest{}, 191 RecipeSections: []*RecipeSection{}, 192 RecipeDependencies: []*Recipe{}, 193 } 194 if resp.Recipe.OidcSettings != nil { 195 r.OidcSettings = &OIDCSettings{ 196 DomainKey: utils.ConvertStringPointer(resp.Recipe.OidcSettings.DomainKey), 197 UriFormat: utils.ConvertStringPointer(resp.Recipe.OidcSettings.URIFormat), 198 UriFormats: utils.ConvertStringArrayPointer(resp.Recipe.OidcSettings.URIFormats), 199 AuthMethod: string(resp.Recipe.OidcSettings.AuthMethod), 200 } 201 if resp.Recipe.OidcSettings.Subdomain != nil { 202 r.OidcSettings.Subdomain = *resp.Recipe.OidcSettings.Subdomain 203 } 204 } 205 if resp.Recipe.Repository != nil { 206 r.Repository = &Repository{ 207 Id: resp.Recipe.Repository.ID, 208 Name: resp.Recipe.Repository.Name, 209 } 210 } 211 if resp.Recipe.Restricted != nil { 212 r.Restricted = *resp.Recipe.Restricted 213 } 214 215 for _, dep := range resp.Recipe.RecipeDependencies { 216 r.RecipeDependencies = append(r.RecipeDependencies, convertRecipe(dep)) 217 } 218 219 for _, section := range resp.Recipe.RecipeSections { 220 rs := &RecipeSection{ 221 Id: fmt.Sprint(section.Index), 222 Repository: &Repository{ 223 Id: section.Repository.ID, 224 Name: section.Repository.Name, 225 Description: utils.ConvertStringPointer(section.Repository.Description), 226 Icon: utils.ConvertStringPointer(section.Repository.Icon), 227 DarkIcon: utils.ConvertStringPointer(section.Repository.DarkIcon), 228 Notes: utils.ConvertStringPointer(section.Repository.Notes), 229 }, 230 RecipeItems: []*RecipeItem{}, 231 Configuration: []*ConfigurationItem{}, 232 } 233 for _, conf := range section.Configuration { 234 rs.Configuration = append(rs.Configuration, convertConfigurationItem(conf)) 235 } 236 for _, recipeItem := range section.RecipeItems { 237 rs.RecipeItems = append(rs.RecipeItems, convertRecipeItem(recipeItem)) 238 } 239 240 r.RecipeSections = append(r.RecipeSections, rs) 241 242 } 243 244 for _, test := range resp.Recipe.Tests { 245 t := &RecipeTest{ 246 Name: test.Name, 247 Type: string(test.Type), 248 Message: utils.ConvertStringPointer(test.Message), 249 Args: []*TestArgument{}, 250 } 251 for _, arg := range test.Args { 252 t.Args = append(t.Args, &TestArgument{ 253 Name: arg.Name, 254 Repo: arg.Repo, 255 Key: arg.Key, 256 }) 257 } 258 259 r.Tests = append(r.Tests, t) 260 } 261 262 return r, nil 263 } 264 265 func convertRecipeItem(item *gqlclient.RecipeItemFragment) *RecipeItem { 266 ri := &RecipeItem{ 267 Id: utils.ConvertStringPointer(item.ID), 268 Terraform: convertTerraform(item.Terraform), 269 } 270 for _, conf := range item.Configuration { 271 ri.Configuration = append(ri.Configuration, convertConfigurationItem(conf)) 272 } 273 if item.Chart != nil { 274 ri.Chart = &Chart{ 275 Id: utils.ConvertStringPointer(item.Chart.ID), 276 Name: item.Chart.Name, 277 Description: utils.ConvertStringPointer(item.Chart.Description), 278 LatestVersion: utils.ConvertStringPointer(item.Chart.LatestVersion), 279 } 280 } 281 282 return ri 283 } 284 285 func convertConfigurationItem(conf *gqlclient.RecipeConfigurationFragment) *ConfigurationItem { 286 confItem := &ConfigurationItem{ 287 Name: utils.ConvertStringPointer(conf.Name), 288 Default: utils.ConvertStringPointer(conf.Default), 289 Documentation: utils.ConvertStringPointer(conf.Documentation), 290 Placeholder: utils.ConvertStringPointer(conf.Placeholder), 291 FunctionName: utils.ConvertStringPointer(conf.FunctionName), 292 } 293 if conf.Optional != nil { 294 confItem.Optional = *conf.Optional 295 } 296 if conf.Type != nil { 297 confItem.Type = string(*conf.Type) 298 } 299 if conf.Condition != nil { 300 confItem.Condition = &Condition{ 301 Field: conf.Condition.Field, 302 Value: utils.ConvertStringPointer(conf.Condition.Value), 303 Operation: string(conf.Condition.Operation), 304 } 305 } 306 if conf.Validation != nil { 307 confItem.Validation = &Validation{ 308 Type: string(conf.Validation.Type), 309 Regex: utils.ConvertStringPointer(conf.Validation.Regex), 310 Message: conf.Validation.Message, 311 } 312 } 313 314 return confItem 315 } 316 317 func convertRecipe(rcp *gqlclient.RecipeFragment) *Recipe { 318 r := &Recipe{ 319 Id: rcp.ID, 320 Name: rcp.Name, 321 Primary: rcp.Primary != nil && *rcp.Primary, 322 Restricted: rcp.Restricted != nil && *rcp.Restricted, 323 Description: utils.ConvertStringPointer(rcp.Description), 324 Tests: []*RecipeTest{}, 325 RecipeSections: []*RecipeSection{}, 326 RecipeDependencies: []*Recipe{}, 327 } 328 if rcp.Repository != nil { 329 r.Repository = &Repository{ 330 Id: rcp.Repository.ID, 331 Name: rcp.Repository.Name, 332 } 333 } 334 if rcp.OidcSettings != nil { 335 r.OidcSettings = &OIDCSettings{ 336 DomainKey: utils.ConvertStringPointer(rcp.OidcSettings.DomainKey), 337 UriFormat: utils.ConvertStringPointer(rcp.OidcSettings.URIFormat), 338 UriFormats: utils.ConvertStringArrayPointer(rcp.OidcSettings.URIFormats), 339 AuthMethod: string(rcp.OidcSettings.AuthMethod), 340 } 341 } 342 343 if rcp.Provider != nil { 344 provider := *rcp.Provider 345 r.Provider = string(provider) 346 } 347 348 for _, test := range rcp.Tests { 349 t := &RecipeTest{ 350 Name: test.Name, 351 Type: string(test.Type), 352 Message: utils.ConvertStringPointer(test.Message), 353 Args: []*TestArgument{}, 354 } 355 for _, arg := range test.Args { 356 t.Args = append(t.Args, &TestArgument{ 357 Name: arg.Name, 358 Repo: arg.Repo, 359 Key: arg.Key, 360 }) 361 } 362 r.Tests = append(r.Tests, t) 363 } 364 365 for _, section := range rcp.RecipeSections { 366 rs := &RecipeSection{ 367 Id: fmt.Sprint(section.Index), 368 Repository: &Repository{ 369 Id: section.Repository.ID, 370 Name: section.Repository.Name, 371 Description: utils.ConvertStringPointer(section.Repository.Description), 372 Icon: utils.ConvertStringPointer(section.Repository.Icon), 373 DarkIcon: utils.ConvertStringPointer(section.Repository.DarkIcon), 374 Notes: utils.ConvertStringPointer(section.Repository.Notes), 375 }, 376 RecipeItems: []*RecipeItem{}, 377 Configuration: []*ConfigurationItem{}, 378 } 379 for _, conf := range section.Configuration { 380 rs.Configuration = append(rs.Configuration, convertConfigurationItem(conf)) 381 } 382 for _, recipeItem := range section.RecipeItems { 383 rs.RecipeItems = append(rs.RecipeItems, convertRecipeItem(recipeItem)) 384 } 385 386 r.RecipeSections = append(r.RecipeSections, rs) 387 388 } 389 390 return r 391 } 392 393 func convertStack(st *gqlclient.StackFragment) *Stack { 394 return &Stack{ 395 Name: st.Name, 396 Description: utils.ConvertStringPointer(st.Description), 397 Featured: *st.Featured, 398 } 399 } 400 401 func (client *client) ListRecipes(repo, provider string) ([]*Recipe, error) { 402 recipes := make([]*Recipe, 0) 403 404 if provider != "" { 405 p := gqlclient.Provider(ToGQLClientProvider(provider)) 406 resp, err := client.pluralClient.ListRecipes(client.ctx, &repo, &p) 407 if err != nil { 408 return nil, err 409 } 410 for _, edge := range resp.Recipes.Edges { 411 recipes = append(recipes, convertRecipe(edge.Node)) 412 } 413 } else { 414 resp, err := client.pluralClient.ListAllRecipes(client.ctx, &repo) 415 if err != nil { 416 return nil, err 417 } 418 for _, edge := range resp.Recipes.Edges { 419 recipes = append(recipes, convertRecipe(edge.Node)) 420 } 421 } 422 423 return recipes, nil 424 } 425 426 func (client *client) InstallRecipe(id string) error { 427 _, err := client.pluralClient.InstallRecipe(client.ctx, id) 428 if err != nil { 429 return err 430 } 431 return nil 432 } 433 434 func (client *client) GetStack(name, provider string) (*Stack, error) { 435 p := gqlclient.Provider(ToGQLClientProvider(provider)) 436 resp, err := client.pluralClient.GetStack(client.ctx, name, p) 437 if err != nil { 438 return nil, err 439 } 440 441 s := convertStack(resp.Stack) 442 s.Bundles = make([]*Recipe, 0) 443 for _, r := range resp.Stack.Bundles { 444 s.Bundles = append(s.Bundles, convertRecipe(r)) 445 } 446 447 return s, nil 448 } 449 450 func (client *client) ListStacks(featured bool) ([]*Stack, error) { 451 resp, err := client.pluralClient.ListStacks(client.ctx, &featured, nil) 452 if err != nil { 453 return nil, err 454 } 455 456 stacks := make([]*Stack, 0) 457 for _, edge := range resp.Stacks.Edges { 458 stacks = append(stacks, convertStack(edge.Node)) 459 } 460 461 return stacks, nil 462 } 463 464 func (client *client) CreateStack(attributes gqlclient.StackAttributes) (string, error) { 465 resp, err := client.pluralClient.CreateStack(client.ctx, attributes) 466 if err != nil { 467 return "", err 468 } 469 470 return resp.CreateStack.ID, err 471 } 472 473 func ConstructStack(marshalled []byte) (stack gqlclient.StackAttributes, err error) { 474 err = yaml.Unmarshal(marshalled, &stack) 475 return 476 } 477 478 func ConstructRecipe(marshalled []byte) (recipe gqlclient.RecipeAttributes, err error) { 479 err = yaml.Unmarshal(marshalled, &recipe) 480 return 481 }