github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/papi/rule.go (about)

     1  package papi
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"regexp"
     9  
    10  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/edgegriderr"
    11  	validation "github.com/go-ozzo/ozzo-validation/v4"
    12  )
    13  
    14  type (
    15  	// PropertyRules contains operations available on PropertyRule resource
    16  	// See: https://developer.akamai.com/api/core_features/property_manager/v1.html#propertyversionrulesgroup
    17  	PropertyRules interface {
    18  		// GetRuleTree lists all available CP codes
    19  		// See: https://developer.akamai.com/api/core_features/property_manager/v1.html#getpropertyversionrules
    20  		GetRuleTree(context.Context, GetRuleTreeRequest) (*GetRuleTreeResponse, error)
    21  
    22  		// UpdateRuleTree lists all available CP codes
    23  		// See: https://developer.akamai.com/api/core_features/property_manager/v1.html#putpropertyversionrules
    24  		UpdateRuleTree(context.Context, UpdateRulesRequest) (*UpdateRulesResponse, error)
    25  	}
    26  
    27  	// GetRuleTreeRequest contains path and query params necessary to perform GET /rules request
    28  	GetRuleTreeRequest struct {
    29  		PropertyID      string
    30  		PropertyVersion int
    31  		ContractID      string
    32  		GroupID         string
    33  		ValidateMode    string
    34  		ValidateRules   bool
    35  		RuleFormat      string
    36  	}
    37  
    38  	// GetRuleTreeResponse contains data returned by performing GET /rules request
    39  	GetRuleTreeResponse struct {
    40  		Response
    41  		PropertyID      string `json:"propertyId"`
    42  		PropertyVersion int    `json:"propertyVersion"`
    43  		Etag            string `json:"etag"`
    44  		RuleFormat      string `json:"ruleFormat"`
    45  		Rules           Rules  `json:"rules"`
    46  		Comments        string `json:"comments,omitempty"`
    47  	}
    48  
    49  	// Rules contains Rule object
    50  	Rules struct {
    51  		AdvancedOverride    string                  `json:"advancedOverride,omitempty"`
    52  		Behaviors           []RuleBehavior          `json:"behaviors,omitempty"`
    53  		Children            []Rules                 `json:"children,omitempty"`
    54  		Comments            string                  `json:"comments,omitempty"`
    55  		Criteria            []RuleBehavior          `json:"criteria,omitempty"`
    56  		CriteriaLocked      bool                    `json:"criteriaLocked,omitempty"`
    57  		CustomOverride      *RuleCustomOverride     `json:"customOverride,omitempty"`
    58  		Name                string                  `json:"name"`
    59  		Options             RuleOptions             `json:"options,omitempty"`
    60  		UUID                string                  `json:"uuid,omitempty"`
    61  		TemplateUuid        string                  `json:"templateUuid,omitempty"`
    62  		TemplateLink        string                  `json:"templateLink,omitempty"`
    63  		Variables           []RuleVariable          `json:"variables,omitempty"`
    64  		CriteriaMustSatisfy RuleCriteriaMustSatisfy `json:"criteriaMustSatisfy,omitempty"`
    65  	}
    66  
    67  	// RuleBehavior contains data for both rule behaviors and rule criteria
    68  	RuleBehavior struct {
    69  		Locked       bool           `json:"locked,omitempty"`
    70  		Name         string         `json:"name"`
    71  		Options      RuleOptionsMap `json:"options"`
    72  		UUID         string         `json:"uuid,omitempty"`
    73  		TemplateUuid string         `json:"templateUuid,omitempty"`
    74  	}
    75  
    76  	// RuleCustomOverride represents customOverride field from Rule resource
    77  	RuleCustomOverride struct {
    78  		Name       string `json:"name"`
    79  		OverrideID string `json:"overrideId"`
    80  	}
    81  
    82  	// RuleOptions represents options field from Rule resource
    83  	RuleOptions struct {
    84  		IsSecure bool `json:"is_secure,omitempty"`
    85  	}
    86  
    87  	// RuleVariable represents and entry in variables field from Rule resource
    88  	RuleVariable struct {
    89  		Description string `json:"description,omitempty"`
    90  		Hidden      bool   `json:"hidden"`
    91  		Name        string `json:"name"`
    92  		Sensitive   bool   `json:"sensitive"`
    93  		Value       string `json:"value,omitempty"`
    94  	}
    95  
    96  	// UpdateRulesRequest contains path and query params, as well as request body necessary to perform PUT /rules request
    97  	UpdateRulesRequest struct {
    98  		PropertyID      string
    99  		PropertyVersion int
   100  		ContractID      string
   101  		DryRun          bool
   102  		GroupID         string
   103  		ValidateMode    string
   104  		ValidateRules   bool
   105  		Rules           RulesUpdate
   106  	}
   107  
   108  	// RulesUpdate is a wrapper for the request body of PUT /rules request
   109  	RulesUpdate struct {
   110  		Comments string `json:"comments,omitempty"`
   111  		Rules    Rules  `json:"rules"`
   112  	}
   113  
   114  	// UpdateRulesResponse contains data returned by performing PUT /rules request
   115  	UpdateRulesResponse struct {
   116  		AccountID       string      `json:"accountId"`
   117  		ContractID      string      `json:"contractId"`
   118  		Comments        string      `json:"comments,omitempty"`
   119  		GroupID         string      `json:"groupId"`
   120  		PropertyID      string      `json:"propertyId"`
   121  		PropertyVersion int         `json:"propertyVersion"`
   122  		Etag            string      `json:"etag"`
   123  		RuleFormat      string      `json:"ruleFormat"`
   124  		Rules           Rules       `json:"rules"`
   125  		Errors          []RuleError `json:"errors"`
   126  	}
   127  
   128  	// RuleError represents and entry in error field from PUT /rules response body
   129  	RuleError struct {
   130  		Type         string `json:"type"`
   131  		Title        string `json:"title"`
   132  		Detail       string `json:"detail"`
   133  		Instance     string `json:"instance"`
   134  		BehaviorName string `json:"behaviorName"`
   135  	}
   136  
   137  	// RuleOptionsMap is a type wrapping map[string]interface{} used for adding rule options
   138  	RuleOptionsMap map[string]interface{}
   139  
   140  	// RuleCriteriaMustSatisfy represents criteriaMustSatisfy field values
   141  	RuleCriteriaMustSatisfy string
   142  )
   143  
   144  const (
   145  	// RuleValidateModeFast const
   146  	RuleValidateModeFast = "fast"
   147  	// RuleValidateModeFull const
   148  	RuleValidateModeFull = "full"
   149  
   150  	// RuleCriteriaMustSatisfyAll const
   151  	RuleCriteriaMustSatisfyAll RuleCriteriaMustSatisfy = "all"
   152  	//RuleCriteriaMustSatisfyAny const
   153  	RuleCriteriaMustSatisfyAny RuleCriteriaMustSatisfy = "any"
   154  )
   155  
   156  var validRuleFormat = regexp.MustCompile("^(latest|v\\d{4}-\\d{2}-\\d{2})$")
   157  
   158  // Validate validates GetRuleTreeRequest struct
   159  func (r GetRuleTreeRequest) Validate() error {
   160  	return validation.Errors{
   161  		"PropertyID":      validation.Validate(r.PropertyID, validation.Required),
   162  		"PropertyVersion": validation.Validate(r.PropertyVersion, validation.Required),
   163  		"ValidateMode":    validation.Validate(r.ValidateMode, validation.In(RuleValidateModeFast, RuleValidateModeFull)),
   164  		"RuleFormat":      validation.Validate(r.RuleFormat, validation.Match(validRuleFormat)),
   165  	}.Filter()
   166  }
   167  
   168  // Validate validates UpdateRulesRequest struct
   169  func (r UpdateRulesRequest) Validate() error {
   170  	errs := validation.Errors{
   171  		"PropertyID":      validation.Validate(r.PropertyID, validation.Required),
   172  		"PropertyVersion": validation.Validate(r.PropertyVersion, validation.Required),
   173  		"ValidateMode":    validation.Validate(r.ValidateMode, validation.In(RuleValidateModeFast, RuleValidateModeFull)),
   174  		"Rules":           validation.Validate(r.Rules),
   175  	}
   176  	return edgegriderr.ParseValidationErrors(errs)
   177  }
   178  
   179  // Validate validates RulesUpdate struct
   180  func (r RulesUpdate) Validate() error {
   181  	return validation.Errors{
   182  		"Rules":    validation.Validate(r.Rules),
   183  		"Comments": validation.Validate(r.Comments),
   184  	}.Filter()
   185  }
   186  
   187  // Validate validates Rules struct
   188  func (r Rules) Validate() error {
   189  	return validation.Errors{
   190  		"Behaviors":      validation.Validate(r.Behaviors),
   191  		"Name":           validation.Validate(r.Name, validation.Required),
   192  		"CustomOverride": validation.Validate(r.CustomOverride),
   193  		"Criteria":       validation.Validate(r.Criteria),
   194  		"Children":       validation.Validate(r.Children),
   195  		"Variables":      validation.Validate(r.Variables),
   196  		"Comments":       validation.Validate(r.Comments),
   197  	}.Filter()
   198  }
   199  
   200  // Validate validates RuleBehavior struct
   201  func (b RuleBehavior) Validate() error {
   202  	return validation.Errors{
   203  		"Name":    validation.Validate(b.Name),
   204  		"Options": validation.Validate(b.Options),
   205  	}.Filter()
   206  }
   207  
   208  // Validate validates RuleCustomOverride struct
   209  func (co RuleCustomOverride) Validate() error {
   210  	return validation.Errors{
   211  		"Name":       validation.Validate(co.Name, validation.Required),
   212  		"OverrideID": validation.Validate(co.OverrideID, validation.Required),
   213  	}.Filter()
   214  }
   215  
   216  // Validate validates RuleVariable struct
   217  func (v RuleVariable) Validate() error {
   218  	return validation.Errors{
   219  		"Name": validation.Validate(v.Name, validation.Required),
   220  	}.Filter()
   221  }
   222  
   223  var (
   224  	// ErrGetRuleTree represents error when fetching rule tree fails
   225  	ErrGetRuleTree = errors.New("fetching rule tree")
   226  	// ErrUpdateRuleTree represents error when updating rule tree fails
   227  	ErrUpdateRuleTree = errors.New("updating rule tree")
   228  )
   229  
   230  func (p *papi) GetRuleTree(ctx context.Context, params GetRuleTreeRequest) (*GetRuleTreeResponse, error) {
   231  	if err := params.Validate(); err != nil {
   232  		return nil, fmt.Errorf("%s: %w: %s", ErrGetRuleTree, ErrStructValidation, err)
   233  	}
   234  
   235  	logger := p.Log(ctx)
   236  	logger.Debug("GetRuleTree")
   237  
   238  	getURL := fmt.Sprintf(
   239  		"/papi/v1/properties/%s/versions/%d/rules?contractId=%s&groupId=%s",
   240  		params.PropertyID,
   241  		params.PropertyVersion,
   242  		params.ContractID,
   243  		params.GroupID,
   244  	)
   245  	if params.ValidateMode != "" {
   246  		getURL += fmt.Sprintf("&validateMode=%s", params.ValidateMode)
   247  	}
   248  	if !params.ValidateRules {
   249  		getURL += fmt.Sprintf("&validateRules=%t", params.ValidateRules)
   250  	}
   251  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   252  	if err != nil {
   253  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetRuleTree, err)
   254  	}
   255  
   256  	if params.RuleFormat != "" {
   257  		req.Header.Set("Accept", fmt.Sprintf("application/vnd.akamai.papirules.%s+json", params.RuleFormat))
   258  	}
   259  
   260  	var rules GetRuleTreeResponse
   261  	resp, err := p.Exec(req, &rules)
   262  	if err != nil {
   263  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetRuleTree, err)
   264  	}
   265  
   266  	if resp.StatusCode != http.StatusOK {
   267  		return nil, fmt.Errorf("%s: %w", ErrGetRuleTree, p.Error(resp))
   268  	}
   269  
   270  	return &rules, nil
   271  }
   272  
   273  func (p *papi) UpdateRuleTree(ctx context.Context, request UpdateRulesRequest) (*UpdateRulesResponse, error) {
   274  	if err := request.Validate(); err != nil {
   275  		return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateRuleTree, ErrStructValidation, err)
   276  	}
   277  
   278  	logger := p.Log(ctx)
   279  	logger.Debug("UpdateRuleTree")
   280  
   281  	putURL := fmt.Sprintf(
   282  		"/papi/v1/properties/%s/versions/%d/rules?contractId=%s&groupId=%s",
   283  		request.PropertyID,
   284  		request.PropertyVersion,
   285  		request.ContractID,
   286  		request.GroupID,
   287  	)
   288  	if request.ValidateMode != "" {
   289  		putURL += fmt.Sprintf("&validateMode=%s", request.ValidateMode)
   290  	}
   291  	if !request.ValidateRules {
   292  		putURL += fmt.Sprintf("&validateRules=%t", request.ValidateRules)
   293  	}
   294  	if request.DryRun {
   295  		putURL += fmt.Sprintf("&dryRun=%t", request.DryRun)
   296  	}
   297  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
   298  	if err != nil {
   299  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateRuleTree, err)
   300  	}
   301  
   302  	var versions UpdateRulesResponse
   303  	resp, err := p.Exec(req, &versions, request.Rules)
   304  	if err != nil {
   305  		return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateRuleTree, err)
   306  	}
   307  	if resp.StatusCode != http.StatusOK {
   308  		return nil, fmt.Errorf("%s: %w", ErrUpdateRuleTree, p.Error(resp))
   309  	}
   310  
   311  	return &versions, nil
   312  }