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  }