github.com/akamai/AkamaiOPEN-edgegrid-golang/v4@v4.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/v4/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 }