github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.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/v8/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  		// ServeStaleDuration The amount of time in seconds that the policy will serve stale images. During the serve stale period realtime images will attempt to use the offline image from the previous policy version first if possible.
   138  		ServeStaleDuration *int `json:"serveStaleDuration,omitempty"`
   139  		// Transformations Set of image transformations to apply to the source image. If unspecified, no operations are performed
   140  		Transformations Transformations `json:"transformations,omitempty"`
   141  		// 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
   142  		Variables []Variable `json:"variables,omitempty"`
   143  	}
   144  
   145  	// PolicyInputVideo Specifies details for each policy such as video size
   146  	PolicyInputVideo struct {
   147  		// Breakpoints The breakpoint widths (in pixels) to use to create derivative images/videos
   148  		Breakpoints *Breakpoints `json:"breakpoints,omitempty"`
   149  		// Hosts Hosts that are allowed for image/video URLs within transformations or variables
   150  		Hosts []string `json:"hosts,omitempty"`
   151  		// Output Dictates the output quality that are created for each resized video
   152  		Output *OutputVideo `json:"output,omitempty"`
   153  		// 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
   154  		RolloutDuration *int `json:"rolloutDuration,omitempty"`
   155  		// 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
   156  		Variables []Variable `json:"variables,omitempty"`
   157  	}
   158  )
   159  
   160  const (
   161  	// PolicyNetworkStaging represents staging network
   162  	PolicyNetworkStaging PolicyNetwork = "staging"
   163  	// PolicyNetworkProduction represents production network
   164  	PolicyNetworkProduction PolicyNetwork = "production"
   165  )
   166  
   167  var (
   168  	// ErrUnmarshalPolicyOutputList represents an error while unmarshalling transformation list
   169  	ErrUnmarshalPolicyOutputList = errors.New("unmarshalling policy output list")
   170  
   171  	// ErrListPolicies is returned when ListPolicies fails
   172  	ErrListPolicies = errors.New("list policies")
   173  
   174  	// ErrGetPolicy is returned when GetPolicy fails
   175  	ErrGetPolicy = errors.New("get policy")
   176  
   177  	// ErrUpsertPolicy is returned when UpsertPolicy fails
   178  	ErrUpsertPolicy = errors.New("upsert policy")
   179  
   180  	// ErrDeletePolicy is returned when DeletePolicy fails
   181  	ErrDeletePolicy = errors.New("delete policy")
   182  
   183  	// ErrGetPolicyHistory is returned when GetPolicyHistory fails
   184  	ErrGetPolicyHistory = errors.New("get policy history")
   185  
   186  	// ErrRollbackPolicy is returned when RollbackPolicy fails
   187  	ErrRollbackPolicy = errors.New("rollback policy")
   188  )
   189  
   190  func (*PolicyOutputImage) policyOutputType() string {
   191  	return "Image"
   192  }
   193  
   194  func (*PolicyOutputVideo) policyOutputType() string {
   195  	return "Video"
   196  }
   197  
   198  func (*PolicyInputImage) policyInputType() string {
   199  	return "Image"
   200  }
   201  
   202  func (*PolicyInputVideo) policyInputType() string {
   203  	return "Video"
   204  }
   205  
   206  // Validate validates PolicyInputImage
   207  func (p *PolicyInputImage) Validate() error {
   208  	return validation.Errors{
   209  		"Breakpoints":                   validation.Validate(p.Breakpoints),
   210  		"Hosts":                         validation.Validate(p.Hosts, validation.Each()),
   211  		"Output":                        validation.Validate(p.Output),
   212  		"PostBreakpointTransformations": validation.Validate(p.PostBreakpointTransformations),
   213  		"RolloutDuration": validation.Validate(p.RolloutDuration,
   214  			validation.Min(3600),
   215  			validation.Max(604800),
   216  		),
   217  		"ServeStaleDuration": validation.Validate(p.ServeStaleDuration,
   218  			validation.Min(0),
   219  			validation.Max(2592000),
   220  		),
   221  		"Transformations": validation.Validate(p.Transformations),
   222  		"Variables":       validation.Validate(p.Variables, validation.Each()),
   223  	}.Filter()
   224  }
   225  
   226  // Validate validates PolicyInputVideo
   227  func (p *PolicyInputVideo) Validate() error {
   228  	return validation.Errors{
   229  		"Breakpoints": validation.Validate(p.Breakpoints),
   230  		"Hosts":       validation.Validate(p.Hosts, validation.Each()),
   231  		"Output":      validation.Validate(p.Output),
   232  		"RolloutDuration": validation.Validate(p.RolloutDuration,
   233  			validation.Min(3600),
   234  			validation.Max(604800),
   235  		),
   236  		"Variables": validation.Validate(p.Variables, validation.Each()),
   237  	}.Filter()
   238  }
   239  
   240  var policyOutputHandlers = map[bool]func() PolicyOutput{
   241  	false: func() PolicyOutput { return &PolicyOutputImage{} },
   242  	true:  func() PolicyOutput { return &PolicyOutputVideo{} },
   243  }
   244  
   245  // UnmarshalJSON is a custom unmarshaler used to decode a slice of PolicyOutput interfaces
   246  func (po *PolicyOutputs) UnmarshalJSON(in []byte) error {
   247  	data := make([]map[string]interface{}, 0)
   248  	if err := json.Unmarshal(in, &data); err != nil {
   249  		return fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   250  	}
   251  	for _, policyOutput := range data {
   252  		p, err := unmarshallPolicyOutput(policyOutput)
   253  		if err != nil {
   254  			return err
   255  		}
   256  		*po = append(*po, p)
   257  	}
   258  	return nil
   259  }
   260  
   261  func unmarshallPolicyOutput(policyOutput map[string]interface{}) (PolicyOutput, error) {
   262  	video, ok := policyOutput["video"]
   263  	if !ok {
   264  		return nil, fmt.Errorf("%w: policyOutput should contain 'video' field", ErrUnmarshalPolicyOutputList)
   265  	}
   266  	isVideo, ok := video.(bool)
   267  	if !ok {
   268  		return nil, fmt.Errorf("%w: 'video' field on policyOutput entry should be a boolean", ErrUnmarshalPolicyOutputList)
   269  	}
   270  
   271  	bytes, err := json.Marshal(policyOutput)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   274  	}
   275  
   276  	indicatedPolicyOutputType, ok := policyOutputHandlers[isVideo]
   277  	if !ok {
   278  		return nil, fmt.Errorf("%w: unsupported policyOutput type: %v", ErrUnmarshalPolicyOutputList, isVideo)
   279  	}
   280  	ipt := indicatedPolicyOutputType()
   281  	err = json.Unmarshal(bytes, ipt)
   282  	if err != nil {
   283  		return nil, fmt.Errorf("%w: %s", ErrUnmarshalPolicyOutputList, err)
   284  	}
   285  	return ipt, nil
   286  }
   287  
   288  // Validate validates ListPoliciesRequest
   289  func (v ListPoliciesRequest) Validate() error {
   290  	errs := validation.Errors{
   291  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   292  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   293  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   294  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   295  	}
   296  	return edgegriderr.ParseValidationErrors(errs)
   297  }
   298  
   299  // Validate validates GetPolicyRequest
   300  func (v GetPolicyRequest) Validate() error {
   301  	errs := validation.Errors{
   302  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   303  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   304  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   305  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   306  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   307  	}
   308  	return edgegriderr.ParseValidationErrors(errs)
   309  }
   310  
   311  // Validate validates UpsertPolicyRequest
   312  func (v UpsertPolicyRequest) Validate() error {
   313  	errs := validation.Errors{
   314  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   315  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   316  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   317  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   318  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   319  		"Policy": validation.Validate(v.PolicyInput, validation.Required),
   320  		//Validate, Policy Input
   321  	}
   322  	return edgegriderr.ParseValidationErrors(errs)
   323  }
   324  
   325  // Validate validates DeletePolicyRequest
   326  func (v DeletePolicyRequest) Validate() error {
   327  	errs := validation.Errors{
   328  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   329  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   330  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   331  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   332  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   333  	}
   334  	return edgegriderr.ParseValidationErrors(errs)
   335  }
   336  
   337  // Validate validates GetPolicyHistoryRequest
   338  func (v GetPolicyHistoryRequest) Validate() error {
   339  	errs := validation.Errors{
   340  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   341  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   342  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   343  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   344  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   345  	}
   346  	return edgegriderr.ParseValidationErrors(errs)
   347  }
   348  
   349  // Validate validates RollbackPolicyRequest
   350  func (v RollbackPolicyRequest) Validate() error {
   351  	errs := validation.Errors{
   352  		"PolicyID":    validation.Validate(v.PolicyID, validation.Required),
   353  		"ContractID":  validation.Validate(v.ContractID, validation.Required),
   354  		"PolicySetID": validation.Validate(v.PolicySetID, validation.Required),
   355  		"Network": validation.Validate(v.Network, validation.Required, validation.In(PolicyNetworkStaging, PolicyNetworkProduction).
   356  			Error(fmt.Sprintf("network has to be '%s', '%s'", PolicyNetworkStaging, PolicyNetworkProduction))),
   357  	}
   358  	return edgegriderr.ParseValidationErrors(errs)
   359  }
   360  
   361  func (i *imaging) ListPolicies(ctx context.Context, params ListPoliciesRequest) (*ListPoliciesResponse, error) {
   362  	logger := i.Log(ctx)
   363  	logger.Debug("ListPolicies")
   364  
   365  	if err := params.Validate(); err != nil {
   366  		return nil, fmt.Errorf("%s: %w:\n%s", ErrListPolicies, ErrStructValidation, err)
   367  	}
   368  
   369  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/", params.Network)
   370  
   371  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   372  	if err != nil {
   373  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrListPolicies, err)
   374  	}
   375  
   376  	req.Header.Set("Contract", params.ContractID)
   377  	req.Header.Set("Policy-Set", params.PolicySetID)
   378  
   379  	var result ListPoliciesResponse
   380  	resp, err := i.Exec(req, &result)
   381  	if err != nil {
   382  		return nil, fmt.Errorf("%w: request failed: %s", ErrListPolicies, err)
   383  	}
   384  
   385  	if resp.StatusCode != http.StatusOK {
   386  		return nil, fmt.Errorf("%s: %w", ErrListPolicies, i.Error(resp))
   387  	}
   388  
   389  	return &result, nil
   390  }
   391  
   392  func (i *imaging) GetPolicy(ctx context.Context, params GetPolicyRequest) (PolicyOutput, error) {
   393  	logger := i.Log(ctx)
   394  	logger.Debug("GetPolicy")
   395  
   396  	if err := params.Validate(); err != nil {
   397  		return nil, fmt.Errorf("%s: %w:\n%s", ErrGetPolicy, ErrStructValidation, err)
   398  	}
   399  
   400  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   401  
   402  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   403  	if err != nil {
   404  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPolicy, err)
   405  	}
   406  
   407  	req.Header.Set("Contract", params.ContractID)
   408  	req.Header.Set("Policy-Set", params.PolicySetID)
   409  
   410  	var result map[string]interface{}
   411  	resp, err := i.Exec(req, &result)
   412  	if err != nil {
   413  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPolicy, err)
   414  	}
   415  
   416  	if resp.StatusCode != http.StatusOK {
   417  		return nil, fmt.Errorf("%s: %w", ErrGetPolicy, i.Error(resp))
   418  	}
   419  
   420  	policyOutput, err := unmarshallPolicyOutput(result)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	return policyOutput, nil
   426  }
   427  
   428  func (i *imaging) UpsertPolicy(ctx context.Context, params UpsertPolicyRequest) (*PolicyResponse, error) {
   429  	logger := i.Log(ctx)
   430  	logger.Debug("UpsertPolicy")
   431  
   432  	if err := params.Validate(); err != nil {
   433  		return nil, fmt.Errorf("%s: %w:\n%s", ErrUpsertPolicy, ErrStructValidation, err)
   434  	}
   435  
   436  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   437  
   438  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   439  	if err != nil {
   440  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpsertPolicy, err)
   441  	}
   442  
   443  	req.Header.Set("Contract", params.ContractID)
   444  	req.Header.Set("Policy-Set", params.PolicySetID)
   445  
   446  	var result PolicyResponse
   447  	resp, err := i.Exec(req, &result, params.PolicyInput)
   448  	if err != nil {
   449  		return nil, fmt.Errorf("%w: request failed: %s", ErrUpsertPolicy, err)
   450  	}
   451  
   452  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   453  		return nil, fmt.Errorf("%s: %w", ErrUpsertPolicy, i.Error(resp))
   454  	}
   455  
   456  	return &result, nil
   457  }
   458  
   459  func (i *imaging) DeletePolicy(ctx context.Context, params DeletePolicyRequest) (*PolicyResponse, error) {
   460  	logger := i.Log(ctx)
   461  	logger.Debug("DeletePolicy")
   462  
   463  	if err := params.Validate(); err != nil {
   464  		return nil, fmt.Errorf("%s: %w:\n%s", ErrDeletePolicy, ErrStructValidation, err)
   465  	}
   466  
   467  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/%s", params.Network, params.PolicyID)
   468  
   469  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   470  	if err != nil {
   471  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrDeletePolicy, err)
   472  	}
   473  
   474  	req.Header.Set("Contract", params.ContractID)
   475  	req.Header.Set("Policy-Set", params.PolicySetID)
   476  
   477  	var result PolicyResponse
   478  	resp, err := i.Exec(req, &result)
   479  	if err != nil {
   480  		return nil, fmt.Errorf("%w: request failed: %s", ErrDeletePolicy, err)
   481  	}
   482  
   483  	if resp.StatusCode != http.StatusOK {
   484  		return nil, fmt.Errorf("%s: %w", ErrDeletePolicy, i.Error(resp))
   485  	}
   486  
   487  	return &result, nil
   488  }
   489  
   490  func (i *imaging) GetPolicyHistory(ctx context.Context, params GetPolicyHistoryRequest) (*GetPolicyHistoryResponse, error) {
   491  	logger := i.Log(ctx)
   492  	logger.Debug("GetPolicyHistory")
   493  
   494  	if err := params.Validate(); err != nil {
   495  		return nil, fmt.Errorf("%s: %w:\n%s", ErrGetPolicyHistory, ErrStructValidation, err)
   496  	}
   497  
   498  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/history/%s", params.Network, params.PolicyID)
   499  
   500  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   501  	if err != nil {
   502  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPolicyHistory, err)
   503  	}
   504  
   505  	req.Header.Set("Contract", params.ContractID)
   506  	req.Header.Set("Policy-Set", params.PolicySetID)
   507  
   508  	var result GetPolicyHistoryResponse
   509  	resp, err := i.Exec(req, &result)
   510  	if err != nil {
   511  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPolicyHistory, err)
   512  	}
   513  
   514  	if resp.StatusCode != http.StatusOK {
   515  		return nil, fmt.Errorf("%s: %w", ErrGetPolicyHistory, i.Error(resp))
   516  	}
   517  
   518  	return &result, nil
   519  }
   520  
   521  func (i *imaging) RollbackPolicy(ctx context.Context, params RollbackPolicyRequest) (*PolicyResponse, error) {
   522  	logger := i.Log(ctx)
   523  	logger.Debug("RollbackPolicy")
   524  
   525  	if err := params.Validate(); err != nil {
   526  		return nil, fmt.Errorf("%s: %w:\n%s", ErrRollbackPolicy, ErrStructValidation, err)
   527  	}
   528  
   529  	uri := fmt.Sprintf("/imaging/v2/network/%s/policies/rollback/%s", params.Network, params.PolicyID)
   530  
   531  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, nil)
   532  	if err != nil {
   533  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrRollbackPolicy, err)
   534  	}
   535  
   536  	req.Header.Set("Contract", params.ContractID)
   537  	req.Header.Set("Policy-Set", params.PolicySetID)
   538  
   539  	var result PolicyResponse
   540  	resp, err := i.Exec(req, &result)
   541  	if err != nil {
   542  		return nil, fmt.Errorf("%w: request failed: %s", ErrRollbackPolicy, err)
   543  	}
   544  
   545  	if resp.StatusCode != http.StatusOK {
   546  		return nil, fmt.Errorf("%s: %w", ErrRollbackPolicy, i.Error(resp))
   547  	}
   548  
   549  	return &result, nil
   550  }