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