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 }