github.com/akamai/AkamaiOPEN-edgegrid-golang/v5@v5.0.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/v5/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,omitempty"`
    91  		Hidden      bool   `json:"hidden"`
    92  		Name        string `json:"name"`
    93  		Sensitive   bool   `json:"sensitive"`
    94  		Value       string `json:"value,omitempty"`
    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  	}
   128  
   129  	// RuleError represents and entry in error field from PUT /rules response body
   130  	RuleError struct {
   131  		Type         string `json:"type"`
   132  		Title        string `json:"title"`
   133  		Detail       string `json:"detail"`
   134  		Instance     string `json:"instance"`
   135  		BehaviorName string `json:"behaviorName"`
   136  	}
   137  
   138  	// RuleOptionsMap is a type wrapping map[string]interface{} used for adding rule options
   139  	RuleOptionsMap map[string]interface{}
   140  
   141  	// RuleCriteriaMustSatisfy represents criteriaMustSatisfy field values
   142  	RuleCriteriaMustSatisfy string
   143  )
   144  
   145  const (
   146  	// RuleValidateModeFast const
   147  	RuleValidateModeFast = "fast"
   148  	// RuleValidateModeFull const
   149  	RuleValidateModeFull = "full"
   150  
   151  	// RuleCriteriaMustSatisfyAll const
   152  	RuleCriteriaMustSatisfyAll RuleCriteriaMustSatisfy = "all"
   153  	//RuleCriteriaMustSatisfyAny const
   154  	RuleCriteriaMustSatisfyAny RuleCriteriaMustSatisfy = "any"
   155  )
   156  
   157  var validRuleFormat = regexp.MustCompile("^(latest|v\\d{4}-\\d{2}-\\d{2})$")
   158  
   159  // Validate validates GetRuleTreeRequest struct
   160  func (r GetRuleTreeRequest) Validate() error {
   161  	return validation.Errors{
   162  		"PropertyID":      validation.Validate(r.PropertyID, validation.Required),
   163  		"PropertyVersion": validation.Validate(r.PropertyVersion, validation.Required),
   164  		"ValidateMode":    validation.Validate(r.ValidateMode, validation.In(RuleValidateModeFast, RuleValidateModeFull)),
   165  		"RuleFormat":      validation.Validate(r.RuleFormat, validation.Match(validRuleFormat)),
   166  	}.Filter()
   167  }
   168  
   169  // Validate validates UpdateRulesRequest struct
   170  func (r UpdateRulesRequest) Validate() error {
   171  	errs := validation.Errors{
   172  		"PropertyID":      validation.Validate(r.PropertyID, validation.Required),
   173  		"PropertyVersion": validation.Validate(r.PropertyVersion, validation.Required),
   174  		"ValidateMode":    validation.Validate(r.ValidateMode, validation.In(RuleValidateModeFast, RuleValidateModeFull)),
   175  		"Rules":           validation.Validate(r.Rules),
   176  	}
   177  	return edgegriderr.ParseValidationErrors(errs)
   178  }
   179  
   180  // Validate validates RulesUpdate struct
   181  func (r RulesUpdate) Validate() error {
   182  	return validation.Errors{
   183  		"Rules":    validation.Validate(r.Rules),
   184  		"Comments": validation.Validate(r.Comments),
   185  	}.Filter()
   186  }
   187  
   188  // Validate validates Rules struct
   189  func (r Rules) Validate() error {
   190  	return validation.Errors{
   191  		"Behaviors":      validation.Validate(r.Behaviors),
   192  		"Name":           validation.Validate(r.Name, validation.Required),
   193  		"CustomOverride": validation.Validate(r.CustomOverride),
   194  		"Criteria":       validation.Validate(r.Criteria),
   195  		"Children":       validation.Validate(r.Children),
   196  		"Variables":      validation.Validate(r.Variables),
   197  		"Comments":       validation.Validate(r.Comments),
   198  	}.Filter()
   199  }
   200  
   201  // Validate validates RuleBehavior struct
   202  func (b RuleBehavior) Validate() error {
   203  	return validation.Errors{
   204  		"Name":    validation.Validate(b.Name),
   205  		"Options": validation.Validate(b.Options),
   206  	}.Filter()
   207  }
   208  
   209  // Validate validates RuleCustomOverride struct
   210  func (co RuleCustomOverride) Validate() error {
   211  	return validation.Errors{
   212  		"Name":       validation.Validate(co.Name, validation.Required),
   213  		"OverrideID": validation.Validate(co.OverrideID, validation.Required),
   214  	}.Filter()
   215  }
   216  
   217  // Validate validates RuleVariable struct
   218  func (v RuleVariable) Validate() error {
   219  	return validation.Errors{
   220  		"Name": validation.Validate(v.Name, validation.Required),
   221  	}.Filter()
   222  }
   223  
   224  var (
   225  	// ErrGetRuleTree represents error when fetching rule tree fails
   226  	ErrGetRuleTree = errors.New("fetching rule tree")
   227  	// ErrUpdateRuleTree represents error when updating rule tree fails
   228  	ErrUpdateRuleTree = errors.New("updating rule tree")
   229  )
   230  
   231  func (p *papi) GetRuleTree(ctx context.Context, params GetRuleTreeRequest) (*GetRuleTreeResponse, error) {
   232  	if err := params.Validate(); err != nil {
   233  		return nil, fmt.Errorf("%s: %w: %s", ErrGetRuleTree, ErrStructValidation, err)
   234  	}
   235  
   236  	logger := p.Log(ctx)
   237  	logger.Debug("GetRuleTree")
   238  
   239  	getURL := fmt.Sprintf(
   240  		"/papi/v1/properties/%s/versions/%d/rules?contractId=%s&groupId=%s",
   241  		params.PropertyID,
   242  		params.PropertyVersion,
   243  		params.ContractID,
   244  		params.GroupID,
   245  	)
   246  	if params.ValidateMode != "" {
   247  		getURL += fmt.Sprintf("&validateMode=%s", params.ValidateMode)
   248  	}
   249  	if !params.ValidateRules {
   250  		getURL += fmt.Sprintf("&validateRules=%t", params.ValidateRules)
   251  	}
   252  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   253  	if err != nil {
   254  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetRuleTree, err)
   255  	}
   256  
   257  	if params.RuleFormat != "" {
   258  		req.Header.Set("Accept", fmt.Sprintf("application/vnd.akamai.papirules.%s+json", params.RuleFormat))
   259  	}
   260  
   261  	var rules GetRuleTreeResponse
   262  	resp, err := p.Exec(req, &rules)
   263  	if err != nil {
   264  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetRuleTree, err)
   265  	}
   266  
   267  	if resp.StatusCode != http.StatusOK {
   268  		return nil, fmt.Errorf("%s: %w", ErrGetRuleTree, p.Error(resp))
   269  	}
   270  
   271  	return &rules, nil
   272  }
   273  
   274  func (p *papi) UpdateRuleTree(ctx context.Context, request UpdateRulesRequest) (*UpdateRulesResponse, error) {
   275  	if err := request.Validate(); err != nil {
   276  		return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateRuleTree, ErrStructValidation, err)
   277  	}
   278  
   279  	logger := p.Log(ctx)
   280  	logger.Debug("UpdateRuleTree")
   281  
   282  	putURL := fmt.Sprintf(
   283  		"/papi/v1/properties/%s/versions/%d/rules?contractId=%s&groupId=%s",
   284  		request.PropertyID,
   285  		request.PropertyVersion,
   286  		request.ContractID,
   287  		request.GroupID,
   288  	)
   289  	if request.ValidateMode != "" {
   290  		putURL += fmt.Sprintf("&validateMode=%s", request.ValidateMode)
   291  	}
   292  	if !request.ValidateRules {
   293  		putURL += fmt.Sprintf("&validateRules=%t", request.ValidateRules)
   294  	}
   295  	if request.DryRun {
   296  		putURL += fmt.Sprintf("&dryRun=%t", request.DryRun)
   297  	}
   298  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
   299  	if err != nil {
   300  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateRuleTree, err)
   301  	}
   302  
   303  	var versions UpdateRulesResponse
   304  	resp, err := p.Exec(req, &versions, request.Rules)
   305  	if err != nil {
   306  		return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateRuleTree, err)
   307  	}
   308  	if resp.StatusCode != http.StatusOK {
   309  		return nil, fmt.Errorf("%s: %w", ErrUpdateRuleTree, p.Error(resp))
   310  	}
   311  
   312  	return &versions, nil
   313  }