github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/appsec/rate_policy.go (about)

     1  package appsec
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  
     9  	validation "github.com/go-ozzo/ozzo-validation/v4"
    10  )
    11  
    12  type (
    13  	// The RatePolicy interface supports creating, retrieving, updating and removing rate policies.
    14  	RatePolicy interface {
    15  		// GetRatePolicies returns rate policies for a specific security configuration version.
    16  		//
    17  		// See: https://techdocs.akamai.com/application-security/reference/get-rate-policies
    18  		GetRatePolicies(ctx context.Context, params GetRatePoliciesRequest) (*GetRatePoliciesResponse, error)
    19  
    20  		// GetRatePolicy returns the specified rate policy.
    21  		//
    22  		// See: https://techdocs.akamai.com/application-security/reference/get-rate-policy
    23  		GetRatePolicy(ctx context.Context, params GetRatePolicyRequest) (*GetRatePolicyResponse, error)
    24  
    25  		// CreateRatePolicy creates a new rate policy for a specific configuration version.
    26  		//
    27  		// See: https://techdocs.akamai.com/application-security/reference/post-rate-policies
    28  		CreateRatePolicy(ctx context.Context, params CreateRatePolicyRequest) (*CreateRatePolicyResponse, error)
    29  
    30  		// UpdateRatePolicy updates details for a specific rate policy.
    31  		//
    32  		// See: https://techdocs.akamai.com/application-security/reference/put-rate-policy
    33  		UpdateRatePolicy(ctx context.Context, params UpdateRatePolicyRequest) (*UpdateRatePolicyResponse, error)
    34  
    35  		// RemoveRatePolicy deletes the specified rate policy.
    36  		//
    37  		// See: https://techdocs.akamai.com/application-security/reference/delete-rate-policy
    38  		RemoveRatePolicy(ctx context.Context, params RemoveRatePolicyRequest) (*RemoveRatePolicyResponse, error)
    39  	}
    40  
    41  	// CreateRatePolicyRequest is used to create a rate policy.
    42  	CreateRatePolicyRequest struct {
    43  		ID             int             `json:"-"`
    44  		ConfigID       int             `json:"configId"`
    45  		ConfigVersion  int             `json:"configVersion"`
    46  		JsonPayloadRaw json.RawMessage `json:"-"`
    47  	}
    48  
    49  	// CreateRatePolicyResponse is returned from a call to CreateRatePolicy.
    50  	CreateRatePolicyResponse struct {
    51  		ID                    int    `json:"id"`
    52  		ConfigID              int    `json:"configId"`
    53  		ConfigVersion         int    `json:"configVersion"`
    54  		MatchType             string `json:"matchType"`
    55  		Type                  string `json:"type"`
    56  		Name                  string `json:"name"`
    57  		Description           string `json:"description"`
    58  		AverageThreshold      int    `json:"averageThreshold"`
    59  		BurstThreshold        int    `json:"burstThreshold"`
    60  		BurstWindow           int    `json:"burstWindow"`
    61  		ClientIdentifier      string `json:"clientIdentifier"`
    62  		UseXForwardForHeaders bool   `json:"useXForwardForHeaders"`
    63  		RequestType           string `json:"requestType"`
    64  		SameActionOnIpv6      bool   `json:"sameActionOnIpv6"`
    65  		Path                  struct {
    66  			PositiveMatch bool     `json:"positiveMatch"`
    67  			Values        []string `json:"values"`
    68  		} `json:"path"`
    69  		PathMatchType        string `json:"pathMatchType"`
    70  		PathURIPositiveMatch bool   `json:"pathUriPositiveMatch"`
    71  		FileExtensions       struct {
    72  			PositiveMatch bool     `json:"positiveMatch"`
    73  			Values        []string `json:"values"`
    74  		} `json:"fileExtensions"`
    75  		Hosts                  *RatePoliciesHosts      `json:"hosts,omitempty"`
    76  		Hostnames              []string                `json:"hostnames"`
    77  		AdditionalMatchOptions []RatePolicyMatchOption `json:"additionalMatchOptions,omitempty"`
    78  		Condition              *RatePolicyCondition    `json:"condition,omitempty"`
    79  		QueryParameters        []struct {
    80  			Name          string   `json:"name"`
    81  			Values        []string `json:"values"`
    82  			PositiveMatch bool     `json:"positiveMatch"`
    83  			ValueInRange  bool     `json:"valueInRange"`
    84  		} `json:"queryParameters"`
    85  		CreateDate string          `json:"-"`
    86  		UpdateDate string          `json:"-"`
    87  		Used       json.RawMessage `json:"used"`
    88  	}
    89  
    90  	// UpdateRatePolicyRequest is used to modify an existing rate policy.
    91  	UpdateRatePolicyRequest struct {
    92  		RatePolicyID   int             `json:"id"`
    93  		ConfigID       int             `json:"configId"`
    94  		ConfigVersion  int             `json:"configVersion"`
    95  		JsonPayloadRaw json.RawMessage `json:"-"`
    96  	}
    97  
    98  	// UpdateRatePolicyResponse is returned from a call to UpdateRatePolicy.
    99  	UpdateRatePolicyResponse struct {
   100  		ID                    int    `json:"id"`
   101  		ConfigID              int    `json:"configId"`
   102  		ConfigVersion         int    `json:"configVersion"`
   103  		MatchType             string `json:"matchType"`
   104  		Type                  string `json:"type"`
   105  		Name                  string `json:"name"`
   106  		Description           string `json:"description"`
   107  		AverageThreshold      int    `json:"averageThreshold"`
   108  		BurstThreshold        int    `json:"burstThreshold"`
   109  		BurstWindow           int    `json:"burstWindow"`
   110  		ClientIdentifier      string `json:"clientIdentifier"`
   111  		UseXForwardForHeaders bool   `json:"useXForwardForHeaders"`
   112  		RequestType           string `json:"requestType"`
   113  		SameActionOnIpv6      bool   `json:"sameActionOnIpv6"`
   114  		Path                  struct {
   115  			PositiveMatch bool     `json:"positiveMatch"`
   116  			Values        []string `json:"values"`
   117  		} `json:"path"`
   118  		PathMatchType        string `json:"pathMatchType"`
   119  		PathURIPositiveMatch bool   `json:"pathUriPositiveMatch"`
   120  		FileExtensions       struct {
   121  			PositiveMatch bool     `json:"positiveMatch"`
   122  			Values        []string `json:"values"`
   123  		} `json:"fileExtensions"`
   124  		Hosts                  *RatePoliciesHosts      `json:"hosts,omitempty"`
   125  		Hostnames              []string                `json:"hostnames"`
   126  		AdditionalMatchOptions []RatePolicyMatchOption `json:"additionalMatchOptions,omitempty"`
   127  		Condition              *RatePolicyCondition    `json:"condition,omitempty"`
   128  		QueryParameters        []struct {
   129  			Name          string   `json:"name"`
   130  			Values        []string `json:"values"`
   131  			PositiveMatch bool     `json:"positiveMatch"`
   132  			ValueInRange  bool     `json:"valueInRange"`
   133  		} `json:"queryParameters"`
   134  		CreateDate string          `json:"-"`
   135  		UpdateDate string          `json:"-"`
   136  		Used       json.RawMessage `json:"used"`
   137  	}
   138  
   139  	// RemoveRatePolicyRequest is used to remove a rate policy.
   140  	RemoveRatePolicyRequest struct {
   141  		ConfigID      int `json:"configId"`
   142  		ConfigVersion int `json:"configVersion"`
   143  		RatePolicyID  int `json:"ratePolicyId"`
   144  	}
   145  
   146  	// RemoveRatePolicyResponse is returned from a call to RemoveRatePolicy.
   147  	RemoveRatePolicyResponse struct {
   148  		ID                    int    `json:"id"`
   149  		ConfigID              int    `json:"configId"`
   150  		ConfigVersion         int    `json:"configVersion"`
   151  		MatchType             string `json:"matchType"`
   152  		Type                  string `json:"type"`
   153  		Name                  string `json:"name"`
   154  		Description           string `json:"description"`
   155  		AverageThreshold      int    `json:"averageThreshold"`
   156  		BurstThreshold        int    `json:"burstThreshold"`
   157  		BurstWindow           int    `json:"burstWindow"`
   158  		ClientIdentifier      string `json:"clientIdentifier"`
   159  		UseXForwardForHeaders bool   `json:"useXForwardForHeaders"`
   160  		RequestType           string `json:"requestType"`
   161  		SameActionOnIpv6      bool   `json:"sameActionOnIpv6"`
   162  		Path                  struct {
   163  			PositiveMatch bool     `json:"positiveMatch"`
   164  			Values        []string `json:"values"`
   165  		} `json:"path"`
   166  		PathMatchType        string `json:"pathMatchType"`
   167  		PathURIPositiveMatch bool   `json:"pathUriPositiveMatch"`
   168  		FileExtensions       struct {
   169  			PositiveMatch bool     `json:"positiveMatch"`
   170  			Values        []string `json:"values"`
   171  		} `json:"fileExtensions"`
   172  		Hosts                  *RatePoliciesHosts      `json:"hosts,omitempty"`
   173  		Hostnames              []string                `json:"hostnames"`
   174  		AdditionalMatchOptions []RatePolicyMatchOption `json:"additionalMatchOptions,omitempty"`
   175  		Condition              *RatePolicyCondition    `json:"condition,omitempty"`
   176  		QueryParameters        []struct {
   177  			Name          string   `json:"name"`
   178  			Values        []string `json:"values"`
   179  			PositiveMatch bool     `json:"positiveMatch"`
   180  			ValueInRange  bool     `json:"valueInRange"`
   181  		} `json:"queryParameters"`
   182  		CreateDate string          `json:"-"`
   183  		UpdateDate string          `json:"-"`
   184  		Used       json.RawMessage `json:"used"`
   185  	}
   186  
   187  	// GetRatePoliciesRequest is used to retrieve the rate policies for a configuration.
   188  	GetRatePoliciesRequest struct {
   189  		ConfigID      int `json:"configId"`
   190  		ConfigVersion int `json:"configVersion"`
   191  		RatePolicyID  int `json:"ratePolicyId"`
   192  	}
   193  
   194  	// GetRatePoliciesResponse is returned from a call to GetRatePolicies.
   195  	GetRatePoliciesResponse struct {
   196  		RatePolicies []struct {
   197  			ID                     int                        `json:"id"`
   198  			ConfigID               int                        `json:"-"`
   199  			ConfigVersion          int                        `json:"-"`
   200  			MatchType              string                     `json:"matchType,omitempty"`
   201  			Type                   string                     `json:"type,omitempty"`
   202  			Name                   string                     `json:"name,omitempty"`
   203  			Description            string                     `json:"description,omitempty"`
   204  			AverageThreshold       int                        `json:"averageThreshold,omitempty"`
   205  			BurstThreshold         int                        `json:"burstThreshold,omitempty"`
   206  			BurstWindow            int                        `json:"burstWindow,omitempty"`
   207  			ClientIdentifier       string                     `json:"clientIdentifier,omitempty"`
   208  			UseXForwardForHeaders  bool                       `json:"useXForwardForHeaders"`
   209  			RequestType            string                     `json:"requestType,omitempty"`
   210  			SameActionOnIpv6       bool                       `json:"sameActionOnIpv6"`
   211  			Path                   *RatePolicyPath            `json:"path,omitempty"`
   212  			PathMatchType          string                     `json:"pathMatchType,omitempty"`
   213  			PathURIPositiveMatch   bool                       `json:"pathUriPositiveMatch"`
   214  			FileExtensions         *RatePolicyFileExtensions  `json:"fileExtensions,omitempty"`
   215  			Hosts                  *RatePoliciesHosts         `json:"hosts,omitempty"`
   216  			Hostnames              []string                   `json:"hostnames,omitempty"`
   217  			AdditionalMatchOptions []RatePolicyMatchOption    `json:"additionalMatchOptions,omitempty"`
   218  			Condition              *RatePolicyCondition       `json:"condition,omitempty"`
   219  			QueryParameters        *RatePolicyQueryParameters `json:"queryParameters,omitempty"`
   220  			CreateDate             string                     `json:"-"`
   221  			UpdateDate             string                     `json:"-"`
   222  			Used                   json.RawMessage            `json:"used"`
   223  			SameActionOnIpv        bool                       `json:"sameActionOnIpv"`
   224  			APISelectors           *RatePolicyAPISelectors    `json:"apiSelectors,omitempty"`
   225  			BodyParameters         *RatePolicyBodyParameters  `json:"bodyParameters,omitempty"`
   226  		} `json:"ratePolicies,omitempty"`
   227  	}
   228  
   229  	// GetRatePolicyRequest is used to retrieve information about a specific rate policy.
   230  	GetRatePolicyRequest struct {
   231  		ConfigID      int `json:"configId"`
   232  		ConfigVersion int `json:"configVersion"`
   233  		RatePolicyID  int `json:"ratePolicyId"`
   234  	}
   235  
   236  	// GetRatePolicyResponse is returned from a call to GetRatePolicy.
   237  	GetRatePolicyResponse struct {
   238  		ID                     int                        `json:"-"`
   239  		ConfigID               int                        `json:"-"`
   240  		ConfigVersion          int                        `json:"-"`
   241  		MatchType              string                     `json:"matchType,omitempty"`
   242  		Type                   string                     `json:"type,omitempty"`
   243  		Name                   string                     `json:"name,omitempty"`
   244  		Description            string                     `json:"description,omitempty"`
   245  		AverageThreshold       int                        `json:"averageThreshold,omitempty"`
   246  		BurstThreshold         int                        `json:"burstThreshold,omitempty"`
   247  		BurstWindow            int                        `json:"burstWindow,omitempty"`
   248  		ClientIdentifier       string                     `json:"clientIdentifier,omitempty"`
   249  		UseXForwardForHeaders  bool                       `json:"useXForwardForHeaders"`
   250  		RequestType            string                     `json:"requestType,omitempty"`
   251  		SameActionOnIpv6       bool                       `json:"sameActionOnIpv6"`
   252  		Path                   *RatePolicyPath            `json:"path,omitempty"`
   253  		PathMatchType          string                     `json:"pathMatchType,omitempty"`
   254  		PathURIPositiveMatch   bool                       `json:"pathUriPositiveMatch"`
   255  		FileExtensions         *RatePolicyFileExtensions  `json:"fileExtensions,omitempty"`
   256  		Hosts                  *RatePoliciesHosts         `json:"hosts,omitempty"`
   257  		Hostnames              []string                   `json:"hostnames,omitempty"`
   258  		AdditionalMatchOptions []RatePolicyMatchOption    `json:"additionalMatchOptions,omitempty"`
   259  		Condition              *RatePolicyCondition       `json:"condition,omitempty"`
   260  		QueryParameters        *RatePolicyQueryParameters `json:"queryParameters,omitempty"`
   261  		CreateDate             string                     `json:"-"`
   262  		UpdateDate             string                     `json:"-"`
   263  		Used                   bool                       `json:"-"`
   264  	}
   265  
   266  	// RatePolicyAPISelectors is used as part of a rate policy description.
   267  	RatePolicyAPISelectors []struct {
   268  		APIDefinitionID    int   `json:"apiDefinitionId,omitempty"`
   269  		DefinedResources   *bool `json:"definedResources,omitempty"`
   270  		ResourceIds        []int `json:"resourceIds"`
   271  		UndefinedResources *bool `json:"undefinedResources,omitempty"`
   272  	}
   273  
   274  	// RatePolicyBodyParameters is used as part of a rate policy description.
   275  	RatePolicyBodyParameters []struct {
   276  		Name          string   `json:"name,omitempty"`
   277  		Values        []string `json:"values,omitempty"`
   278  		PositiveMatch bool     `json:"positiveMatch"`
   279  		ValueInRange  bool     `json:"valueInRange"`
   280  	}
   281  
   282  	// RatePolicyPath is used as part of a rate policy description.
   283  	RatePolicyPath struct {
   284  		PositiveMatch bool     `json:"positiveMatch"`
   285  		Values        []string `json:"values,omitempty"`
   286  	}
   287  
   288  	// RatePolicyFileExtensions is used as part of a rate policy description.
   289  	RatePolicyFileExtensions struct {
   290  		PositiveMatch bool     `json:"positiveMatch"`
   291  		Values        []string `json:"values,omitempty"`
   292  	}
   293  
   294  	// RatePolicyMatchOption is used as part of a rate policy description.
   295  	RatePolicyMatchOption struct {
   296  		PositiveMatch bool     `json:"positiveMatch"`
   297  		Type          string   `json:"type,omitempty"`
   298  		Values        []string `json:"values,omitempty"`
   299  	}
   300  
   301  	// RatePolicyQueryParameters is used as part of a rate policy description.
   302  	RatePolicyQueryParameters []struct {
   303  		Name          string   `json:"name,omitempty"`
   304  		Values        []string `json:"values,omitempty"`
   305  		PositiveMatch bool     `json:"positiveMatch"`
   306  		ValueInRange  bool     `json:"valueInRange"`
   307  	}
   308  
   309  	// RatePoliciesHosts is used as part of a rate policy description.
   310  	RatePoliciesHosts struct {
   311  		Values        *[]string        `json:"values,omitempty"`
   312  		PositiveMatch *json.RawMessage `json:"positiveMatch,omitempty"`
   313  	}
   314  
   315  	// RatePolicyCondition is used as part of a rate policy description.
   316  	RatePolicyCondition struct {
   317  		AtomicConditions []struct {
   318  			Value            *json.RawMessage `json:"value,omitempty"`
   319  			ClassName        string           `json:"className"`
   320  			PositiveMatch    bool             `json:"positiveMatch"`
   321  			Name             []string         `json:"name,omitempty"`
   322  			NameCase         bool             `json:"nameCase,omitempty"`
   323  			NameWildcard     bool             `json:"nameWildcard,omitempty"`
   324  			ValueCase        bool             `json:"valueCase,omitempty"`
   325  			ValueWildcard    bool             `json:"valueWildcard,omitempty"`
   326  			SharedIpHandling string           `json:"sharedIpHandling,omitempty"`
   327  		} `json:"atomicConditions,omitempty"`
   328  	}
   329  )
   330  
   331  // Validate validates a GetRatePolicyRequest.
   332  func (v GetRatePolicyRequest) Validate() error {
   333  	return validation.Errors{
   334  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   335  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   336  		"RatePolicyID":  validation.Validate(v.RatePolicyID, validation.Required),
   337  	}.Filter()
   338  }
   339  
   340  // Validate validates a GetRatePoliciesRequest.
   341  func (v GetRatePoliciesRequest) Validate() error {
   342  	return validation.Errors{
   343  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   344  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   345  	}.Filter()
   346  }
   347  
   348  // Validate validates a CreateRatePolicyRequest.
   349  func (v CreateRatePolicyRequest) Validate() error {
   350  	return validation.Errors{
   351  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   352  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   353  	}.Filter()
   354  }
   355  
   356  // Validate validates an UpdateRatePolicyRequest.
   357  func (v UpdateRatePolicyRequest) Validate() error {
   358  	return validation.Errors{
   359  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   360  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   361  		"RatePolicyID":  validation.Validate(v.RatePolicyID, validation.Required),
   362  	}.Filter()
   363  }
   364  
   365  // Validate validates a RemoveRatePolicyRequest.
   366  func (v RemoveRatePolicyRequest) Validate() error {
   367  	return validation.Errors{
   368  		"ConfigID":      validation.Validate(v.ConfigID, validation.Required),
   369  		"ConfigVersion": validation.Validate(v.ConfigVersion, validation.Required),
   370  		"RatePolicyID":  validation.Validate(v.RatePolicyID, validation.Required),
   371  	}.Filter()
   372  }
   373  
   374  func (p *appsec) GetRatePolicy(ctx context.Context, params GetRatePolicyRequest) (*GetRatePolicyResponse, error) {
   375  	logger := p.Log(ctx)
   376  	logger.Debug("GetRatePolicy")
   377  
   378  	if err := params.Validate(); err != nil {
   379  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   380  	}
   381  
   382  	uri := fmt.Sprintf(
   383  		"/appsec/v1/configs/%d/versions/%d/rate-policies/%d",
   384  		params.ConfigID,
   385  		params.ConfigVersion,
   386  		params.RatePolicyID)
   387  
   388  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   389  	if err != nil {
   390  		return nil, fmt.Errorf("failed to create GetRatePolicy request: %w", err)
   391  	}
   392  
   393  	var result GetRatePolicyResponse
   394  	resp, err := p.Exec(req, &result)
   395  	if err != nil {
   396  		return nil, fmt.Errorf("get rate policy request failed: %w", err)
   397  	}
   398  	if resp.StatusCode != http.StatusOK {
   399  		return nil, p.Error(resp)
   400  	}
   401  
   402  	return &result, nil
   403  }
   404  
   405  func (p *appsec) GetRatePolicies(ctx context.Context, params GetRatePoliciesRequest) (*GetRatePoliciesResponse, error) {
   406  	logger := p.Log(ctx)
   407  	logger.Debug("GetRatePolicies")
   408  
   409  	if err := params.Validate(); err != nil {
   410  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   411  	}
   412  
   413  	uri := fmt.Sprintf(
   414  		"/appsec/v1/configs/%d/versions/%d/rate-policies",
   415  		params.ConfigID,
   416  		params.ConfigVersion,
   417  	)
   418  
   419  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   420  	if err != nil {
   421  		return nil, fmt.Errorf("failed to create GetRatePolicies request: %w", err)
   422  	}
   423  
   424  	var result GetRatePoliciesResponse
   425  	resp, err := p.Exec(req, &result)
   426  	if err != nil {
   427  		return nil, fmt.Errorf("get rate policies request failed: %w", err)
   428  	}
   429  	if resp.StatusCode != http.StatusOK {
   430  		return nil, p.Error(resp)
   431  	}
   432  
   433  	if params.RatePolicyID != 0 {
   434  		var filteredResult GetRatePoliciesResponse
   435  		for _, val := range result.RatePolicies {
   436  			if val.ID == params.RatePolicyID {
   437  				filteredResult.RatePolicies = append(filteredResult.RatePolicies, val)
   438  			}
   439  		}
   440  		return &filteredResult, nil
   441  	}
   442  
   443  	return &result, nil
   444  }
   445  
   446  func (p *appsec) UpdateRatePolicy(ctx context.Context, params UpdateRatePolicyRequest) (*UpdateRatePolicyResponse, error) {
   447  	logger := p.Log(ctx)
   448  	logger.Debug("UpdateRatePolicy")
   449  
   450  	if err := params.Validate(); err != nil {
   451  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   452  	}
   453  
   454  	uri := fmt.Sprintf(
   455  		"/appsec/v1/configs/%d/versions/%d/rate-policies/%d",
   456  		params.ConfigID,
   457  		params.ConfigVersion,
   458  		params.RatePolicyID,
   459  	)
   460  
   461  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   462  	if err != nil {
   463  		return nil, fmt.Errorf("failed to create UpdateRatePolicy request: %w", err)
   464  	}
   465  
   466  	var result UpdateRatePolicyResponse
   467  	req.Header.Set("Content-Type", "application/json")
   468  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   469  	if err != nil {
   470  		return nil, fmt.Errorf("update rate policy request failed: %w", err)
   471  	}
   472  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   473  		return nil, p.Error(resp)
   474  	}
   475  
   476  	return &result, nil
   477  }
   478  
   479  func (p *appsec) CreateRatePolicy(ctx context.Context, params CreateRatePolicyRequest) (*CreateRatePolicyResponse, error) {
   480  	logger := p.Log(ctx)
   481  	logger.Debug("CreateRatePolicy")
   482  
   483  	if err := params.Validate(); err != nil {
   484  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   485  	}
   486  
   487  	uri := fmt.Sprintf(
   488  		"/appsec/v1/configs/%d/versions/%d/rate-policies",
   489  		params.ConfigID,
   490  		params.ConfigVersion,
   491  	)
   492  
   493  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, nil)
   494  	if err != nil {
   495  		return nil, fmt.Errorf("failed to create CreateRatePolicy request: %w", err)
   496  	}
   497  
   498  	var result CreateRatePolicyResponse
   499  	req.Header.Set("Content-Type", "application/json")
   500  	resp, err := p.Exec(req, &result, params.JsonPayloadRaw)
   501  	if err != nil {
   502  		return nil, fmt.Errorf("create rate policy request failed: %w", err)
   503  	}
   504  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   505  		return nil, p.Error(resp)
   506  	}
   507  
   508  	return &result, nil
   509  }
   510  
   511  func (p *appsec) RemoveRatePolicy(ctx context.Context, params RemoveRatePolicyRequest) (*RemoveRatePolicyResponse, error) {
   512  	logger := p.Log(ctx)
   513  	logger.Debug("RemoveRatePolicy")
   514  
   515  	if err := params.Validate(); err != nil {
   516  		return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
   517  	}
   518  
   519  	uri := fmt.Sprintf("/appsec/v1/configs/%d/versions/%d/rate-policies/%d", params.ConfigID, params.ConfigVersion, params.RatePolicyID)
   520  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   521  	if err != nil {
   522  		return nil, fmt.Errorf("failed to create RemoveRatePolicy request: %w", err)
   523  	}
   524  
   525  	var result RemoveRatePolicyResponse
   526  	resp, err := p.Exec(req, &result)
   527  	if err != nil {
   528  		return nil, fmt.Errorf("remove rate policy request failed: %w", err)
   529  	}
   530  	if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
   531  		return nil, p.Error(resp)
   532  	}
   533  
   534  	return &result, nil
   535  }