github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/imaging/policy.go (about)

     1  package imaging
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"net/http"
     9  
    10  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v2/pkg/edgegriderr"
    11  
    12  	validation "github.com/go-ozzo/ozzo-validation/v4"
    13  )
    14  
    15  // Code in this package is using autogenerated code located in the policy.gen.go file.
    16  // Generator code is located in `dxe-tools` repo, and it is using OpenApi schema from
    17  // https://git.source.akamai.com/users/eleclair/repos/terraform/browse/docs/schemas
    18  
    19  type (
    20  	// Policies is an Image and Video Manager API interface for Policy
    21  	//
    22  	// See: https://techdocs.akamai.com/ivm/reference/api
    23  	Policies interface {
    24  		// ListPolicies lists all Policies for the given network and an account
    25  		//
    26  		// See: https://techdocs.akamai.com/ivm/reference/get-policies
    27  		ListPolicies(context.Context, ListPoliciesRequest) (*ListPoliciesResponse, error)
    28  
    29  		// GetPolicy gets specific policy by PolicyID
    30  		GetPolicy(context.Context, GetPolicyRequest) (PolicyOutput, error)
    31  
    32  		// UpsertPolicy creates or updates the configuration for a policy
    33  		UpsertPolicy(context.Context, UpsertPolicyRequest) (*PolicyResponse, error)
    34  
    35  		// DeletePolicy deletes a policy
    36  		DeletePolicy(context.Context, DeletePolicyRequest) (*PolicyResponse, error)
    37  
    38  		// GetPolicyHistory retrieves history of changes for a policy
    39  		GetPolicyHistory(context.Context, GetPolicyHistoryRequest) (*GetPolicyHistoryResponse, error)
    40  
    41  		// RollbackPolicy reverts a policy to its previous version and deploys it to the network
    42  		RollbackPolicy(ctx context.Context, request RollbackPolicyRequest) (*PolicyResponse, error)
    43  	}
    44  
    45  	// ListPoliciesRequest describes the parameters of the ListPolicies request
    46  	ListPoliciesRequest struct {
    47  		Network     PolicyNetwork
    48  		ContractID  string
    49  		PolicySetID string
    50  	}
    51  
    52  	// ListPoliciesResponse is a response returned by ListPolicies operations
    53  	ListPoliciesResponse struct {
    54  		ItemKind   string        `json:"itemKind"`
    55  		Items      PolicyOutputs `json:"items"`
    56  		TotalItems int           `json:"totalItems"`
    57  	}
    58  
    59  	// GetPolicyRequest describes the parameters of the GetPolicy request
    60  	GetPolicyRequest policyRequest
    61  	// DeletePolicyRequest describes the parameters of the DeletePolicy request
    62  	DeletePolicyRequest policyRequest
    63  	// GetPolicyHistoryRequest describes the parameters of the GetHistoryPolicy request
    64  	GetPolicyHistoryRequest policyRequest
    65  	// RollbackPolicyRequest describes the parameters of the RollbackPolicy request
    66  	RollbackPolicyRequest policyRequest
    67  
    68  	// policyRequest describes the parameters of the various policy requests
    69  	policyRequest struct {
    70  		PolicyID    string
    71  		Network     PolicyNetwork
    72  		ContractID  string
    73  		PolicySetID string
    74  	}
    75  
    76  	// UpsertPolicyRequest describes the parameters of the UpsertPolicy request
    77  	UpsertPolicyRequest struct {
    78  		PolicyID    string
    79  		Network     PolicyNetwork
    80  		ContractID  string
    81  		PolicySetID string
    82  		PolicyInput
    83  	}
    84  
    85  	// PolicyResponse describes response of the UpsertPolicy, DeletePolicy and RollbackPolicy responses
    86  	PolicyResponse struct {
    87  		Description        string `json:"description"`
    88  		ID                 string `json:"id"`
    89  		OperationPerformed string `json:"operationPerformed"`
    90  	}
    91  
    92  	// GetPolicyHistoryResponse describes the parameters of the GetPolicyHistory response
    93  	GetPolicyHistoryResponse struct {
    94  		ItemKind   string              `json:"itemKind"`
    95  		TotalItems int                 `json:"totalItems"`
    96  		Items      []PolicyHistoryItem `json:"items"`
    97  	}
    98  
    99  	// PolicyHistoryItem describes items of the history for policy
   100  	PolicyHistoryItem struct {
   101  		ID          string `json:"id"`
   102  		DateCreated string `json:"dateCreated"`
   103  		Policy      string `json:"policy"`
   104  		Action      string `json:"action"`
   105  		User        string `json:"user"`
   106  		Version     int    `json:"version"`
   107  	}
   108  
   109  	// PolicyOutput is implemented by PolicyOutput types (image and video)
   110  	PolicyOutput interface {
   111  		policyOutputType() string
   112  	}
   113  
   114  	// PolicyInput is implemented by PolicyInput types (image and video)
   115  	PolicyInput interface {
   116  		policyInputType() string
   117  	}
   118  
   119  	// PolicyOutputs is an array of PolicyOutput types (image and video)
   120  	PolicyOutputs []PolicyOutput
   121  
   122  	// PolicyNetwork represents the network where policy set is stored
   123  	PolicyNetwork string
   124  
   125  	// PolicyInputImage Specifies details for each policy, such as transformations to apply and variations in image size and formats
   126  	PolicyInputImage struct {
   127  		// Breakpoints The breakpoint widths (in pixels) to use to create derivative images/videos
   128  		Breakpoints *Breakpoints `json:"breakpoints,omitempty"`
   129  		// Hosts Hosts that are allowed for image/video URLs within transformations or variables
   130  		Hosts []string `json:"hosts,omitempty"`
   131  		// Output Dictates the output quality (either `quality` or `perceptualQuality`) and formats that are created for each resized image If unspecified, image formats are created to support all browsers at the default quality level (`85`), which includes formats such as WEBP, JPEG2000 and JPEG-XR for specific browsers
   132  		Output *OutputImage `json:"output,omitempty"`
   133  		// PostBreakpointTransformations Post-processing Transformations are applied to the image after image and quality settings have been applied
   134  		PostBreakpointTransformations PostBreakpointTransformations `json:"postBreakpointTransformations,omitempty"`
   135  		// RolloutDuration The amount of time in seconds that the policy takes to rollout. During the rollout an increasing proportion of images/videos will begin to use the new policy instead of the cached images/videos from the previous version
   136  		RolloutDuration *int `json:"rolloutDuration,omitempty"`
   137  		// Transformations Set of image transformations to apply to the source image. If unspecified, no operations are performed
   138  		Transformations Transformations `json:"transformations,omitempty"`
   139  		// Variables Declares variables for use within the policy. Any variable declared here can be invoked throughout transformations as a [Variable](#variable) object, so that you don't have to specify values separately You can also pass in these variable names and values dynamically as query parameters in the image's request URL
   140  		Variables []Variable `json:"variables,omitempty"`
   141  	}
   142  
   143  	// PolicyInputVideo Specifies details for each policy such as video size
   144  	PolicyInputVideo struct {
   145  		// Breakpoints The breakpoint widths (in pixels) to use to create derivative images/videos
   146  		Breakpoints *Breakpoints `json:"breakpoints,omitempty"`
   147  		// Hosts Hosts that are allowed for image/video URLs within transformations or variables
   148  		Hosts []string `json:"hosts,omitempty"`
   149  		// Output Dictates the output quality that are created for each resized video
   150  		Output *OutputVideo `json:"output,omitempty"`
   151  		// RolloutDuration The amount of time in seconds that the policy takes to rollout. During the rollout an increasing proportion of images/videos will begin to use the new policy instead of the cached images/videos from the previous version
   152  		RolloutDuration *int `json:"rolloutDuration,omitempty"`
   153  		// Variables Declares variables for use within the policy. Any variable declared here can be invoked throughout transformations as a [Variable](#variable) object, so that you don't have to specify values separately You can also pass in these variable names and values dynamically as query parameters in the image's request URL
   154  		Variables []Variable `json:"variables,omitempty"`
   155  	}
   156  )
   157  
   158  const (
   159  	// PolicyNetworkStaging represents staging network
   160  	PolicyNetworkStaging PolicyNetwork = "staging"
   161  	// PolicyNetworkProduction represents production network
   162  	PolicyNetworkProduction PolicyNetwork = "production"
   163  )
   164  
   165  var (
   166  	// ErrUnmarshalPolicyOutputList represents an error while unmarshalling transformation list
   167  	ErrUnmarshalPolicyOutputList = errors.New("unmarshalling policy output list")
   168  
   169  	// ErrListPolicies is returned when ListPolicies fails
   170  	ErrListPolicies = errors.New("list policies")
   171  
   172  	// ErrGetPolicy is returned when GetPolicy fails
   173  	ErrGetPolicy = errors.New("get policy")
   174  
   175  	// ErrUpsertPolicy is returned when UpsertPolicy fails
   176  	ErrUpsertPolicy = errors.New("upsert policy")
   177  
   178  	// ErrDeletePolicy is returned when DeletePolicy fails
   179  	ErrDeletePolicy = errors.New("delete policy")
   180  
   181  	// ErrGetPolicyHistory is returned when GetPolicyHistory fails
   182  	ErrGetPolicyHistory = errors.New("get policy history")
   183  
   184  	// ErrRollbackPolicy is returned when RollbackPolicy fails
   185  	ErrRollbackPolicy = errors.New("rollback policy")
   186  )
   187  
   188  func (*PolicyOutputImage) policyOutputType() string {
   189  	return "Image"
   190  }
   191  
   192  func (*PolicyOutputVideo) policyOutputType() string {
   193  	return "Video"
   194  }
   195  
   196  func (*PolicyInputImage) policyInputType() string {
   197  	return "Image"
   198  }
   199  
   200  func (*PolicyInputVideo) policyInputType() string {
   201  	return "Video"
   202  }
   203  
   204  // Validate validates PolicyInputImage
   205  func (p *PolicyInputImage) Validate() error {
   206  	return validation.Errors{
   207  		"Breakpoints":                   validation.Validate(p.Breakpoints),
   208  		"Hosts":                         validation.Validate(p.Hosts, validation.Each()),
   209  		"Output":                        validation.Validate(p.Output),
   210  		"PostBreakpointTransformations": validation.Validate(p.PostBreakpointTransformations),
   211  		"RolloutDuration": validation.Validate(p.RolloutDuration,
   212  			validation.Min(3600),
   213  			validation.Max(604800),
   214  		),
   215  		"Transformations": validation.Validate(p.Transformations),
   216  		"Variables":       validation.Validate(p.Variables, validation.Each()),
   217  	}.Filter()
   218  }
   219  
   220  // Validate validates PolicyInputVideo
   221  func (p *PolicyInputVideo) Validate() error {
   222  	return validation.Errors{
   223  		"Breakpoints": validation.Validate(p.Breakpoints),
   224  		"Hosts":       validation.Validate(p.Hosts, validation.Each()),
   225  		"Output":      validation.Validate(p.Output),
   226  		"RolloutDuration": validation.Validate(p.RolloutDuration,
   227  			validation.Min(3600),
   228  			validation.Max(604800),
   229  		),
   230  		"Variables": validation.Validate(p.Variables, validation.Each()),
   231  	}.Filter()
   232  }
   233  
   234  var policyOutputHandlers = map[bool]func() PolicyOutput{
   235  	false: func() PolicyOutput { return &PolicyOutputImage{} },
   236  	true:  func() PolicyOutput { return &PolicyOutputVideo{} },
   237  }
   238  
   239  // UnmarshalJSON is a custom unmarshaler used to decode a slice of PolicyOutput interfaces
   240  func (po *PolicyOutputs) UnmarshalJSON(in []byte) error {
   241  	data := make([]map[string]interface{}, 0)
   242  	if err := json.Unmarshal(in, &data); err != nil {
   243  		return fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   244  	}
   245  	for _, policyOutput := range data {
   246  		p, err := unmarshallPolicyOutput(policyOutput)
   247  		if err != nil {
   248  			return err
   249  		}
   250  		*po = append(*po, p)
   251  	}
   252  	return nil
   253  }
   254  
   255  func unmarshallPolicyOutput(policyOutput map[string]interface{}) (PolicyOutput, error) {
   256  	video, ok := policyOutput["video"]
   257  	if !ok {
   258  		return nil, fmt.Errorf("%w: policyOutput should contain 'video' field", ErrUnmarshalPolicyOutputList)
   259  	}
   260  	isVideo, ok := video.(bool)
   261  	if !ok {
   262  		return nil, fmt.Errorf("%w: 'video' field on policyOutput entry should be a boolean", ErrUnmarshalPolicyOutputList)
   263  	}
   264  
   265  	bytes, err := json.Marshal(policyOutput)
   266  	if err != nil {
   267  		return nil, fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   268  	}
   269  
   270  	indicatedPolicyOutputType, ok := policyOutputHandlers[isVideo]
   271  	if !ok {
   272  		return nil, fmt.Errorf("%w: unsupported policyOutput type: %v", ErrUnmarshalPolicyOutputList, isVideo)
   273  	}
   274  	ipt := indicatedPolicyOutputType()
   275  	err = json.Unmarshal(bytes, ipt)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   278  	}
   279  	return ipt, nil
   280  }
   281  
   282  // Validate validates ListPoliciesRequest
   283  func (v ListPoliciesRequest) Validate() error {
   284  	errs := validation.Errors{
   285  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   286  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   287  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   288  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   289  	}
   290  	return edgegriderr.ParseValidationErrors(errs)
   291  }
   292  
   293  // Validate validates GetPolicyRequest
   294  func (v GetPolicyRequest) Validate() error {
   295  	errs := validation.Errors{
   296  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   297  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   298  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   299  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   300  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   301  	}
   302  	return edgegriderr.ParseValidationErrors(errs)
   303  }
   304  
   305  // Validate validates UpsertPolicyRequest
   306  func (v UpsertPolicyRequest) Validate() error {
   307  	errs := validation.Errors{
   308  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   309  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   310  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   311  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   312  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   313  		"Policy": validation.Validate(v.PolicyInput, validation.Required),
   314  		//Validate, Policy Input
   315  	}
   316  	return edgegriderr.ParseValidationErrors(errs)
   317  }
   318  
   319  // Validate validates DeletePolicyRequest
   320  func (v DeletePolicyRequest) Validate() error {
   321  	errs := validation.Errors{
   322  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   323  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   324  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   325  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   326  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   327  	}
   328  	return edgegriderr.ParseValidationErrors(errs)
   329  }
   330  
   331  // Validate validates GetPolicyHistoryRequest
   332  func (v GetPolicyHistoryRequest) Validate() error {
   333  	errs := validation.Errors{
   334  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   335  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   336  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   337  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   338  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   339  	}
   340  	return edgegriderr.ParseValidationErrors(errs)
   341  }
   342  
   343  // Validate validates RollbackPolicyRequest
   344  func (v RollbackPolicyRequest) Validate() error {
   345  	errs := validation.Errors{
   346  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   347  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   348  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   349  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   350  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   351  	}
   352  	return edgegriderr.ParseValidationErrors(errs)
   353  }
   354  
   355  func (i *imaging) ListPolicies(ctx context.Context, params ListPoliciesRequest) (*ListPoliciesResponse, error) {
   356  	logger := i.Log(ctx)
   357  	logger.Debug("ListPolicies")
   358  
   359  	if err := params.Validate(); err != nil {
   360  		return nil, fmt.Errorf("%s: %w:\n%s", ErrListPolicies, ErrStructValidation, err)
   361  	}
   362  
   363  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/", params.Network)
   364  
   365  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   366  	if err != nil {
   367  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrListPolicies, err)
   368  	}
   369  
   370  	req.Header.Set("Contract", params.ContractID)
   371  	req.Header.Set("Policy-Set", params.PolicySetID)
   372  
   373  	var result ListPoliciesResponse
   374  	resp, err := i.Exec(req, &result)
   375  	if err != nil {
   376  		return nil, fmt.Errorf("%w: request failed: %s", ErrListPolicies, err)
   377  	}
   378  
   379  	if resp.StatusCode != http.StatusOK {
   380  		return nil, fmt.Errorf("%s: %w", ErrListPolicies, i.Error(resp))
   381  	}
   382  
   383  	return &result, nil
   384  }
   385  
   386  func (i *imaging) GetPolicy(ctx context.Context, params GetPolicyRequest) (PolicyOutput, error) {
   387  	logger := i.Log(ctx)
   388  	logger.Debug("GetPolicy")
   389  
   390  	if err := params.Validate(); err != nil {
   391  		return nil, fmt.Errorf("%s: %w:\n%s", ErrGetPolicy, ErrStructValidation, err)
   392  	}
   393  
   394  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   395  
   396  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   397  	if err != nil {
   398  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPolicy, err)
   399  	}
   400  
   401  	req.Header.Set("Contract", params.ContractID)
   402  	req.Header.Set("Policy-Set", params.PolicySetID)
   403  
   404  	var result map[string]interface{}
   405  	resp, err := i.Exec(req, &result)
   406  	if err != nil {
   407  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPolicy, err)
   408  	}
   409  
   410  	if resp.StatusCode != http.StatusOK {
   411  		return nil, fmt.Errorf("%s: %w", ErrGetPolicy, i.Error(resp))
   412  	}
   413  
   414  	policyOutput, err := unmarshallPolicyOutput(result)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  
   419  	return policyOutput, nil
   420  }
   421  
   422  func (i *imaging) UpsertPolicy(ctx context.Context, params UpsertPolicyRequest) (*PolicyResponse, error) {
   423  	logger := i.Log(ctx)
   424  	logger.Debug("UpsertPolicy")
   425  
   426  	if err := params.Validate(); err != nil {
   427  		return nil, fmt.Errorf("%s: %w:\n%s", ErrUpsertPolicy, ErrStructValidation, err)
   428  	}
   429  
   430  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   431  
   432  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   433  	if err != nil {
   434  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpsertPolicy, err)
   435  	}
   436  
   437  	req.Header.Set("Contract", params.ContractID)
   438  	req.Header.Set("Policy-Set", params.PolicySetID)
   439  
   440  	var result PolicyResponse
   441  	resp, err := i.Exec(req, &result, params.PolicyInput)
   442  	if err != nil {
   443  		return nil, fmt.Errorf("%w: request failed: %s", ErrUpsertPolicy, err)
   444  	}
   445  
   446  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   447  		return nil, fmt.Errorf("%s: %w", ErrUpsertPolicy, i.Error(resp))
   448  	}
   449  
   450  	return &result, nil
   451  }
   452  
   453  func (i *imaging) DeletePolicy(ctx context.Context, params DeletePolicyRequest) (*PolicyResponse, error) {
   454  	logger := i.Log(ctx)
   455  	logger.Debug("DeletePolicy")
   456  
   457  	if err := params.Validate(); err != nil {
   458  		return nil, fmt.Errorf("%s: %w:\n%s", ErrDeletePolicy, ErrStructValidation, err)
   459  	}
   460  
   461  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   462  
   463  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   464  	if err != nil {
   465  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrDeletePolicy, err)
   466  	}
   467  
   468  	req.Header.Set("Contract", params.ContractID)
   469  	req.Header.Set("Policy-Set", params.PolicySetID)
   470  
   471  	var result PolicyResponse
   472  	resp, err := i.Exec(req, &result)
   473  	if err != nil {
   474  		return nil, fmt.Errorf("%w: request failed: %s", ErrDeletePolicy, err)
   475  	}
   476  
   477  	if resp.StatusCode != http.StatusOK {
   478  		return nil, fmt.Errorf("%s: %w", ErrDeletePolicy, i.Error(resp))
   479  	}
   480  
   481  	return &result, nil
   482  }
   483  
   484  func (i *imaging) GetPolicyHistory(ctx context.Context, params GetPolicyHistoryRequest) (*GetPolicyHistoryResponse, error) {
   485  	logger := i.Log(ctx)
   486  	logger.Debug("GetPolicyHistory")
   487  
   488  	if err := params.Validate(); err != nil {
   489  		return nil, fmt.Errorf("%s: %w:\n%s", ErrGetPolicyHistory, ErrStructValidation, err)
   490  	}
   491  
   492  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/history/%s", params.Network, params.PolicyID)
   493  
   494  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   495  	if err != nil {
   496  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPolicyHistory, err)
   497  	}
   498  
   499  	req.Header.Set("Contract", params.ContractID)
   500  	req.Header.Set("Policy-Set", params.PolicySetID)
   501  
   502  	var result GetPolicyHistoryResponse
   503  	resp, err := i.Exec(req, &result)
   504  	if err != nil {
   505  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPolicyHistory, err)
   506  	}
   507  
   508  	if resp.StatusCode != http.StatusOK {
   509  		return nil, fmt.Errorf("%s: %w", ErrGetPolicyHistory, i.Error(resp))
   510  	}
   511  
   512  	return &result, nil
   513  }
   514  
   515  func (i *imaging) RollbackPolicy(ctx context.Context, params RollbackPolicyRequest) (*PolicyResponse, error) {
   516  	logger := i.Log(ctx)
   517  	logger.Debug("RollbackPolicy")
   518  
   519  	if err := params.Validate(); err != nil {
   520  		return nil, fmt.Errorf("%s: %w:\n%s", ErrRollbackPolicy, ErrStructValidation, err)
   521  	}
   522  
   523  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/rollback/%s", params.Network, params.PolicyID)
   524  
   525  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   526  	if err != nil {
   527  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrRollbackPolicy, err)
   528  	}
   529  
   530  	req.Header.Set("Contract", params.ContractID)
   531  	req.Header.Set("Policy-Set", params.PolicySetID)
   532  
   533  	var result PolicyResponse
   534  	resp, err := i.Exec(req, &result)
   535  	if err != nil {
   536  		return nil, fmt.Errorf("%w: request failed: %s", ErrRollbackPolicy, err)
   537  	}
   538  
   539  	if resp.StatusCode != http.StatusOK {
   540  		return nil, fmt.Errorf("%s: %w", ErrRollbackPolicy, i.Error(resp))
   541  	}
   542  
   543  	return &result, nil
   544  }