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

     1  package papi
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"net/http"
     9  	"net/url"
    10  	"strconv"
    11  
    12  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegriderr"
    13  	validation "github.com/go-ozzo/ozzo-validation/v4"
    14  )
    15  
    16  type (
    17  	// PropertyVersions contains operations available on PropertyVersions resource
    18  	PropertyVersions interface {
    19  		// GetPropertyVersions fetches available property versions
    20  		//
    21  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-versions
    22  		GetPropertyVersions(context.Context, GetPropertyVersionsRequest) (*GetPropertyVersionsResponse, error)
    23  
    24  		// GetPropertyVersion fetches specific property version
    25  		//
    26  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-version
    27  		GetPropertyVersion(context.Context, GetPropertyVersionRequest) (*GetPropertyVersionsResponse, error)
    28  
    29  		// CreatePropertyVersion creates a new property version and returns location and number for the new version
    30  		//
    31  		// See: https://techdocs.akamai.com/property-mgr/reference/post-property-versions
    32  		CreatePropertyVersion(context.Context, CreatePropertyVersionRequest) (*CreatePropertyVersionResponse, error)
    33  
    34  		// GetLatestVersion fetches the latest property version
    35  		//
    36  		// See: https://techdocs.akamai.com/property-mgr/reference/get-latest-property-version
    37  		GetLatestVersion(context.Context, GetLatestVersionRequest) (*GetPropertyVersionsResponse, error)
    38  
    39  		// GetAvailableBehaviors fetches a list of available behaviors for given property version
    40  		//
    41  		// See: https://techdocs.akamai.com/property-mgr/reference/get-available-behaviors
    42  		GetAvailableBehaviors(context.Context, GetAvailableBehaviorsRequest) (*GetBehaviorsResponse, error)
    43  
    44  		// GetAvailableCriteria fetches a list of available criteria for given property version
    45  		//
    46  		// See: https://techdocs.akamai.com/property-mgr/reference/get-available-criteria
    47  		GetAvailableCriteria(context.Context, GetAvailableCriteriaRequest) (*GetCriteriaResponse, error)
    48  
    49  		// ListAvailableIncludes lists external resources that can be applied within a property version's rules
    50  		//
    51  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-version-external-resources
    52  		ListAvailableIncludes(context.Context, ListAvailableIncludesRequest) (*ListAvailableIncludesResponse, error)
    53  
    54  		// ListReferencedIncludes lists referenced includes for parent property
    55  		//
    56  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-version-includes
    57  		ListReferencedIncludes(context.Context, ListReferencedIncludesRequest) (*ListReferencedIncludesResponse, error)
    58  	}
    59  
    60  	// GetPropertyVersionsRequest contains path and query params used for listing property versions
    61  	GetPropertyVersionsRequest struct {
    62  		PropertyID string
    63  		ContractID string
    64  		GroupID    string
    65  		Limit      int
    66  		Offset     int
    67  	}
    68  
    69  	// GetPropertyVersionsResponse contains GET response returned while fetching property versions or specific version
    70  	GetPropertyVersionsResponse struct {
    71  		PropertyID   string               `json:"propertyId"`
    72  		PropertyName string               `json:"propertyName"`
    73  		AccountID    string               `json:"accountId"`
    74  		ContractID   string               `json:"contractId"`
    75  		GroupID      string               `json:"groupId"`
    76  		AssetID      string               `json:"assetId"`
    77  		Versions     PropertyVersionItems `json:"versions"`
    78  		Version      PropertyVersionGetItem
    79  	}
    80  
    81  	// PropertyVersionItems contains collection of property version details
    82  	PropertyVersionItems struct {
    83  		Items []PropertyVersionGetItem `json:"items"`
    84  	}
    85  
    86  	// PropertyVersionGetItem contains detailed information about specific property version returned in GET
    87  	PropertyVersionGetItem struct {
    88  		Etag             string        `json:"etag"`
    89  		Note             string        `json:"note"`
    90  		ProductID        string        `json:"productId"`
    91  		ProductionStatus VersionStatus `json:"productionStatus"`
    92  		PropertyVersion  int           `json:"propertyVersion"`
    93  		RuleFormat       string        `json:"ruleFormat"`
    94  		StagingStatus    VersionStatus `json:"stagingStatus"`
    95  		UpdatedByUser    string        `json:"updatedByUser"`
    96  		UpdatedDate      string        `json:"updatedDate"`
    97  	}
    98  
    99  	// GetPropertyVersionRequest contains path and query params used for fetching specific property version
   100  	GetPropertyVersionRequest struct {
   101  		PropertyID      string
   102  		PropertyVersion int
   103  		ContractID      string
   104  		GroupID         string
   105  	}
   106  
   107  	// CreatePropertyVersionRequest contains path and query params, as well as request body required to execute POST /versions request
   108  	CreatePropertyVersionRequest struct {
   109  		PropertyID string
   110  		ContractID string
   111  		GroupID    string
   112  		Version    PropertyVersionCreate
   113  	}
   114  
   115  	// PropertyVersionCreate contains request body used in POST /versions request
   116  	PropertyVersionCreate struct {
   117  		CreateFromVersion     int    `json:"createFromVersion"`
   118  		CreateFromVersionEtag string `json:"createFromVersionEtag,omitempty"`
   119  	}
   120  
   121  	// CreatePropertyVersionResponse contains a link returned after creating new property version and version number of this version
   122  	CreatePropertyVersionResponse struct {
   123  		VersionLink     string `json:"versionLink"`
   124  		PropertyVersion int
   125  	}
   126  
   127  	// GetLatestVersionRequest contains path and query params required to fetch latest property version
   128  	GetLatestVersionRequest struct {
   129  		PropertyID  string
   130  		ActivatedOn string
   131  		ContractID  string
   132  		GroupID     string
   133  	}
   134  
   135  	// GetAvailableItemsRequest contains path and query params required to fetch available behaviors or criteria for a property
   136  	GetAvailableItemsRequest struct {
   137  		PropertyID      string
   138  		PropertyVersion int
   139  		ContractID      string
   140  		GroupID         string
   141  	}
   142  
   143  	// GetAvailableBehaviorsRequest contains path and query params required to fetch available behaviors for a property
   144  	GetAvailableBehaviorsRequest GetAvailableItemsRequest
   145  
   146  	// GetAvailableCriteriaRequest contains path and query params required to fetch available criteria for a property
   147  	GetAvailableCriteriaRequest GetAvailableItemsRequest
   148  
   149  	// GetBehaviorsResponse represents a response object returned by GetAvailableBehaviors
   150  	GetBehaviorsResponse AvailableBehaviorsResponse
   151  
   152  	// GetCriteriaResponse represents a response object returned by GetAvailableCriteria
   153  	GetCriteriaResponse AvailableCriteriaResponse
   154  
   155  	// VersionStatus represents ProductionVersion and StagingVersion of a Version struct
   156  	VersionStatus string
   157  
   158  	// ListAvailableIncludesRequest contains path and query params required to fetch list of available includes
   159  	ListAvailableIncludesRequest ListAvailableReferencedIncludesRequest
   160  
   161  	// ListReferencedIncludesRequest contains path and query params required to fetch  list of referenced includes
   162  	ListReferencedIncludesRequest ListAvailableReferencedIncludesRequest
   163  
   164  	//ListAvailableReferencedIncludesRequest common request struct for ListReferencedIncludesRequest and ListAvailableIncludesRequest
   165  	ListAvailableReferencedIncludesRequest struct {
   166  		PropertyID      string
   167  		PropertyVersion int
   168  		ContractID      string
   169  		GroupID         string
   170  	}
   171  
   172  	// ListAvailableIncludesResponse contains response received when fetching list of available includes
   173  	ListAvailableIncludesResponse struct {
   174  		AvailableIncludes []ExternalIncludeData
   175  	}
   176  
   177  	// ListReferencedIncludesResponse contains response received when fetching list of referenced includes
   178  	// The response from the API is a map, but we convert it to the array for better usability.
   179  	ListReferencedIncludesResponse struct {
   180  		Includes IncludeItems `json:"includes"`
   181  	}
   182  
   183  	// ExternalIncludeData contains data for a specific include from AvailableIncludes
   184  	ExternalIncludeData struct {
   185  		IncludeID   string      `json:"id"`
   186  		IncludeName string      `json:"name"`
   187  		IncludeType IncludeType `json:"includeType"`
   188  		FileName    string      `json:"fileName"`
   189  		ProductName string      `json:"productName"`
   190  		RuleFormat  string      `json:"ruleFormat"`
   191  	}
   192  )
   193  
   194  const (
   195  	// VersionStatusActive const
   196  	VersionStatusActive VersionStatus = "ACTIVE"
   197  	// VersionStatusInactive const
   198  	VersionStatusInactive VersionStatus = "INACTIVE"
   199  	// VersionStatusPending const
   200  	VersionStatusPending VersionStatus = "PENDING"
   201  	// VersionStatusDeactivated const
   202  	VersionStatusDeactivated VersionStatus = "DEACTIVATED"
   203  	// VersionProduction const
   204  	VersionProduction = "PRODUCTION"
   205  	// VersionStaging const
   206  	VersionStaging = "STAGING"
   207  )
   208  
   209  // Validate validates GetPropertyVersionsRequest
   210  func (v GetPropertyVersionsRequest) Validate() error {
   211  	return validation.Errors{
   212  		"PropertyID": validation.Validate(v.PropertyID, validation.Required),
   213  	}.Filter()
   214  }
   215  
   216  // Validate validates GetPropertyVersionRequest
   217  func (v GetPropertyVersionRequest) Validate() error {
   218  	return validation.Errors{
   219  		"PropertyID":      validation.Validate(v.PropertyID, validation.Required),
   220  		"PropertyVersion": validation.Validate(v.PropertyVersion, validation.Required),
   221  	}.Filter()
   222  }
   223  
   224  // Validate validates CreatePropertyVersionRequest
   225  func (v CreatePropertyVersionRequest) Validate() error {
   226  	errs := validation.Errors{
   227  		"PropertyID": validation.Validate(v.PropertyID, validation.Required),
   228  		"Version":    validation.Validate(v.Version),
   229  	}
   230  	return edgegriderr.ParseValidationErrors(errs)
   231  }
   232  
   233  // Validate validates PropertyVersionCreate
   234  func (v PropertyVersionCreate) Validate() error {
   235  	return validation.Errors{
   236  		"CreateFromVersion": validation.Validate(v.CreateFromVersion, validation.Required),
   237  	}.Filter()
   238  }
   239  
   240  // Validate validates GetLatestVersionRequest
   241  func (v GetLatestVersionRequest) Validate() error {
   242  	return validation.Errors{
   243  		"PropertyID":  validation.Validate(v.PropertyID, validation.Required),
   244  		"ActivatedOn": validation.Validate(v.ActivatedOn, validation.In(VersionProduction, VersionStaging)),
   245  	}.Filter()
   246  }
   247  
   248  // Validate validates GetAvailableBehaviorsRequest
   249  func (v GetAvailableBehaviorsRequest) Validate() error {
   250  	return validation.Errors{
   251  		"PropertyID":      validation.Validate(v.PropertyID, validation.Required),
   252  		"PropertyVersion": validation.Validate(v.PropertyVersion, validation.Required),
   253  	}.Filter()
   254  }
   255  
   256  // Validate validates GetAvailableCriteriaRequest
   257  func (v GetAvailableCriteriaRequest) Validate() error {
   258  	return validation.Errors{
   259  		"PropertyID":      validation.Validate(v.PropertyID, validation.Required),
   260  		"PropertyVersion": validation.Validate(v.PropertyVersion, validation.Required),
   261  	}.Filter()
   262  }
   263  
   264  // Validate validates ListAvailableIncludesRequest
   265  func (v ListAvailableIncludesRequest) Validate() error {
   266  	return validation.Errors{
   267  		"PropertyID":      validation.Validate(v.PropertyID, validation.Required),
   268  		"PropertyVersion": validation.Validate(v.PropertyVersion, validation.Required),
   269  	}.Filter()
   270  }
   271  
   272  // Validate validates ListReferencedIncludesRequest
   273  func (v ListReferencedIncludesRequest) Validate() error {
   274  	return validation.Errors{
   275  		"PropertyID":      validation.Validate(v.PropertyID, validation.Required),
   276  		"PropertyVersion": validation.Validate(v.PropertyVersion, validation.Required),
   277  		"GroupID":         validation.Validate(v.GroupID, validation.Required),
   278  		"ContractID":      validation.Validate(v.ContractID, validation.Required),
   279  	}.Filter()
   280  }
   281  
   282  var (
   283  	// ErrGetPropertyVersions represents error when fetching property versions fails
   284  	ErrGetPropertyVersions = errors.New("fetching property versions")
   285  	// ErrGetPropertyVersion represents error when fetching property version fails
   286  	ErrGetPropertyVersion = errors.New("fetching property version")
   287  	// ErrGetLatestVersion represents error when fetching latest property version fails
   288  	ErrGetLatestVersion = errors.New("fetching latest property version")
   289  	// ErrCreatePropertyVersion represents error when creating property version fails
   290  	ErrCreatePropertyVersion = errors.New("creating property version")
   291  	// ErrGetAvailableBehaviors represents error when fetching available behaviors fails
   292  	ErrGetAvailableBehaviors = errors.New("fetching available behaviors")
   293  	// ErrGetAvailableCriteria represents error when fetching available criteria fails
   294  	ErrGetAvailableCriteria = errors.New("fetching available criteria")
   295  	// ErrListAvailableIncludes represents error when fetching available includes
   296  	ErrListAvailableIncludes = errors.New("fetching available includes")
   297  	// ErrListReferencedIncludes represents error when fetching referenced includes
   298  	ErrListReferencedIncludes = errors.New("fetching referenced includes")
   299  )
   300  
   301  func (p *papi) GetPropertyVersions(ctx context.Context, params GetPropertyVersionsRequest) (*GetPropertyVersionsResponse, error) {
   302  	if err := params.Validate(); err != nil {
   303  		return nil, fmt.Errorf("%s: %w: %s", ErrGetPropertyVersions, ErrStructValidation, err)
   304  	}
   305  
   306  	logger := p.Log(ctx)
   307  	logger.Debug("GetPropertyVersions")
   308  
   309  	getURL := fmt.Sprintf(
   310  		"/papi/v1/properties/%s/versions?contractId=%s&groupId=%s",
   311  		params.PropertyID,
   312  		params.ContractID,
   313  		params.GroupID,
   314  	)
   315  	if params.Limit != 0 {
   316  		getURL += fmt.Sprintf("&limit=%d", params.Limit)
   317  	}
   318  	if params.Offset != 0 {
   319  		getURL += fmt.Sprintf("&offset=%d", params.Offset)
   320  	}
   321  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   322  	if err != nil {
   323  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPropertyVersions, err)
   324  	}
   325  
   326  	var versions GetPropertyVersionsResponse
   327  	resp, err := p.Exec(req, &versions)
   328  	if err != nil {
   329  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPropertyVersions, err)
   330  	}
   331  
   332  	if resp.StatusCode != http.StatusOK {
   333  		return nil, fmt.Errorf("%s: %w", ErrGetPropertyVersions, p.Error(resp))
   334  	}
   335  
   336  	return &versions, nil
   337  }
   338  
   339  func (p *papi) GetLatestVersion(ctx context.Context, params GetLatestVersionRequest) (*GetPropertyVersionsResponse, error) {
   340  	if err := params.Validate(); err != nil {
   341  		return nil, fmt.Errorf("%s: %w: %s", ErrGetLatestVersion, ErrStructValidation, err)
   342  	}
   343  
   344  	logger := p.Log(ctx)
   345  	logger.Debug("GetLatestVersion")
   346  
   347  	getURL := fmt.Sprintf(
   348  		"/papi/v1/properties/%s/versions/latest?contractId=%s&groupId=%s",
   349  		params.PropertyID,
   350  		params.ContractID,
   351  		params.GroupID,
   352  	)
   353  	if params.ActivatedOn != "" {
   354  		getURL += fmt.Sprintf("&activatedOn=%s", params.ActivatedOn)
   355  	}
   356  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   357  	if err != nil {
   358  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetLatestVersion, err)
   359  	}
   360  
   361  	var version GetPropertyVersionsResponse
   362  	resp, err := p.Exec(req, &version)
   363  	if err != nil {
   364  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetLatestVersion, err)
   365  	}
   366  
   367  	if resp.StatusCode != http.StatusOK {
   368  		return nil, fmt.Errorf("%s: %w", ErrGetLatestVersion, p.Error(resp))
   369  	}
   370  	if len(version.Versions.Items) == 0 {
   371  		return nil, fmt.Errorf("%s: %w: latest version for PropertyID: %s", ErrGetLatestVersion, ErrNotFound, params.PropertyID)
   372  	}
   373  	version.Version = version.Versions.Items[0]
   374  	return &version, nil
   375  }
   376  
   377  func (p *papi) GetPropertyVersion(ctx context.Context, params GetPropertyVersionRequest) (*GetPropertyVersionsResponse, error) {
   378  	if err := params.Validate(); err != nil {
   379  		return nil, fmt.Errorf("%s: %w: %s", ErrGetPropertyVersion, ErrStructValidation, err)
   380  	}
   381  
   382  	logger := p.Log(ctx)
   383  	logger.Debug("GetPropertyVersion")
   384  
   385  	getURL := fmt.Sprintf(
   386  		"/papi/v1/properties/%s/versions/%d?contractId=%s&groupId=%s",
   387  		params.PropertyID,
   388  		params.PropertyVersion,
   389  		params.ContractID,
   390  		params.GroupID,
   391  	)
   392  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   393  	if err != nil {
   394  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetPropertyVersion, err)
   395  	}
   396  
   397  	var versions GetPropertyVersionsResponse
   398  	resp, err := p.Exec(req, &versions)
   399  	if err != nil {
   400  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetPropertyVersion, err)
   401  	}
   402  
   403  	if resp.StatusCode != http.StatusOK {
   404  		return nil, fmt.Errorf("%s: %w", ErrGetPropertyVersion, p.Error(resp))
   405  	}
   406  	if len(versions.Versions.Items) == 0 {
   407  		return nil, fmt.Errorf("%s: %w: Version %d for PropertyID: %s", ErrGetPropertyVersion, ErrNotFound, params.PropertyVersion, params.PropertyID)
   408  	}
   409  	versions.Version = versions.Versions.Items[0]
   410  	return &versions, nil
   411  }
   412  
   413  func (p *papi) CreatePropertyVersion(ctx context.Context, request CreatePropertyVersionRequest) (*CreatePropertyVersionResponse, error) {
   414  	if err := request.Validate(); err != nil {
   415  		return nil, fmt.Errorf("%s: %w:\n%s", ErrCreatePropertyVersion, ErrStructValidation, err)
   416  	}
   417  
   418  	logger := p.Log(ctx)
   419  	logger.Debug("CreatePropertyVersion")
   420  
   421  	getURL := fmt.Sprintf(
   422  		"/papi/v1/properties/%s/versions?contractId=%s&groupId=%s",
   423  		request.PropertyID,
   424  		request.ContractID,
   425  		request.GroupID,
   426  	)
   427  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, getURL, nil)
   428  	if err != nil {
   429  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreatePropertyVersion, err)
   430  	}
   431  
   432  	var version CreatePropertyVersionResponse
   433  	resp, err := p.Exec(req, &version, request.Version)
   434  	if err != nil {
   435  		return nil, fmt.Errorf("%w: request failed: %s", ErrCreatePropertyVersion, err)
   436  	}
   437  
   438  	if resp.StatusCode != http.StatusCreated {
   439  		return nil, fmt.Errorf("%s: %w", ErrCreatePropertyVersion, p.Error(resp))
   440  	}
   441  	propertyVersion, err := ResponseLinkParse(version.VersionLink)
   442  	if err != nil {
   443  		return nil, fmt.Errorf("%s: %w: %s", ErrCreatePropertyVersion, ErrInvalidResponseLink, err)
   444  	}
   445  	versionNumber, err := strconv.Atoi(propertyVersion)
   446  	if err != nil {
   447  		return nil, fmt.Errorf("%s: %w: %s: %s", ErrCreatePropertyVersion, ErrInvalidResponseLink, "version should be a number", propertyVersion)
   448  	}
   449  	version.PropertyVersion = versionNumber
   450  	return &version, nil
   451  }
   452  
   453  func (p *papi) GetAvailableBehaviors(ctx context.Context, params GetAvailableBehaviorsRequest) (*GetBehaviorsResponse, error) {
   454  	if err := params.Validate(); err != nil {
   455  		return nil, fmt.Errorf("%s: %w: %s", ErrGetAvailableBehaviors, ErrStructValidation, err)
   456  	}
   457  
   458  	logger := p.Log(ctx)
   459  	logger.Debug("GetAvailableBehaviors")
   460  
   461  	uri, err := url.Parse(fmt.Sprintf("/papi/v1/properties/%s/versions/%d/available-behaviors", params.PropertyID, params.PropertyVersion))
   462  	if err != nil {
   463  		return nil, fmt.Errorf("%w: failed to parse uri: %s", ErrGetAvailableBehaviors, err)
   464  	}
   465  
   466  	q := uri.Query()
   467  	if params.ContractID != "" {
   468  		q.Add("contractId", params.ContractID)
   469  	}
   470  	if params.GroupID != "" {
   471  		q.Add("groupId", params.GroupID)
   472  	}
   473  	uri.RawQuery = q.Encode()
   474  
   475  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   476  	if err != nil {
   477  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetAvailableBehaviors, err)
   478  	}
   479  
   480  	var behaviors GetBehaviorsResponse
   481  	resp, err := p.Exec(req, &behaviors)
   482  	if err != nil {
   483  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetAvailableBehaviors, err)
   484  	}
   485  
   486  	if resp.StatusCode != http.StatusOK {
   487  		return nil, fmt.Errorf("%s: %w", ErrGetAvailableBehaviors, p.Error(resp))
   488  	}
   489  
   490  	return &behaviors, nil
   491  }
   492  
   493  func (p *papi) GetAvailableCriteria(ctx context.Context, params GetAvailableCriteriaRequest) (*GetCriteriaResponse, error) {
   494  	if err := params.Validate(); err != nil {
   495  		return nil, fmt.Errorf("%s: %w: %s", ErrGetAvailableCriteria, ErrStructValidation, err)
   496  	}
   497  
   498  	logger := p.Log(ctx)
   499  	logger.Debug("GetAvailableCriteria")
   500  
   501  	uri, err := url.Parse(fmt.Sprintf("/papi/v1/properties/%s/versions/%d/available-criteria", params.PropertyID, params.PropertyVersion))
   502  	if err != nil {
   503  		return nil, fmt.Errorf("%w: failed to parse uri: %s", ErrGetAvailableCriteria, err)
   504  	}
   505  
   506  	q := uri.Query()
   507  	if params.ContractID != "" {
   508  		q.Add("contractId", params.ContractID)
   509  	}
   510  	if params.GroupID != "" {
   511  		q.Add("groupId", params.GroupID)
   512  	}
   513  	uri.RawQuery = q.Encode()
   514  
   515  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   516  	if err != nil {
   517  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetAvailableCriteria, err)
   518  	}
   519  
   520  	var criteria GetCriteriaResponse
   521  	resp, err := p.Exec(req, &criteria)
   522  	if err != nil {
   523  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetAvailableCriteria, err)
   524  	}
   525  
   526  	if resp.StatusCode != http.StatusOK {
   527  		return nil, fmt.Errorf("%s: %w", ErrGetAvailableCriteria, p.Error(resp))
   528  	}
   529  
   530  	return &criteria, nil
   531  }
   532  
   533  func (p *papi) ListAvailableIncludes(ctx context.Context, params ListAvailableIncludesRequest) (*ListAvailableIncludesResponse, error) {
   534  	logger := p.Log(ctx)
   535  	logger.Debug("ListAvailableIncludes")
   536  
   537  	if err := params.Validate(); err != nil {
   538  		return nil, fmt.Errorf("%s: %w: %s", ErrListAvailableIncludes, ErrStructValidation, err)
   539  	}
   540  
   541  	uri, err := url.Parse(fmt.Sprintf("/papi/v1/properties/%s/versions/%d/external-resources", params.PropertyID, params.PropertyVersion))
   542  	if err != nil {
   543  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrListAvailableIncludes, err)
   544  	}
   545  
   546  	q := uri.Query()
   547  	if params.ContractID != "" {
   548  		q.Add("contractId", params.ContractID)
   549  	}
   550  	if params.GroupID != "" {
   551  		q.Add("groupId", params.GroupID)
   552  	}
   553  	uri.RawQuery = q.Encode()
   554  
   555  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   556  	if err != nil {
   557  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrListAvailableIncludes, err)
   558  	}
   559  
   560  	var result ListAvailableIncludesResponse
   561  	resp, err := p.Exec(req, &result)
   562  	if err != nil {
   563  		return nil, fmt.Errorf("%w: request failed: %s", ErrListAvailableIncludes, err)
   564  	}
   565  
   566  	if resp.StatusCode != http.StatusOK {
   567  		return nil, fmt.Errorf("%s: %w", ErrListAvailableIncludes, p.Error(resp))
   568  	}
   569  
   570  	return &result, nil
   571  }
   572  
   573  func (p *papi) ListReferencedIncludes(ctx context.Context, params ListReferencedIncludesRequest) (*ListReferencedIncludesResponse, error) {
   574  	logger := p.Log(ctx)
   575  	logger.Debug("ListReferencedIncludes")
   576  
   577  	if err := params.Validate(); err != nil {
   578  		return nil, fmt.Errorf("%s: %w: %s", ErrListReferencedIncludes, ErrStructValidation, err)
   579  	}
   580  
   581  	uri, err := url.Parse(fmt.Sprintf("/papi/v1/properties/%s/versions/%d/includes", params.PropertyID, params.PropertyVersion))
   582  	if err != nil {
   583  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrListReferencedIncludes, err)
   584  	}
   585  
   586  	q := uri.Query()
   587  	if params.ContractID != "" {
   588  		q.Add("contractId", params.ContractID)
   589  	}
   590  	if params.GroupID != "" {
   591  		q.Add("groupId", params.GroupID)
   592  	}
   593  	uri.RawQuery = q.Encode()
   594  
   595  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   596  	if err != nil {
   597  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrListReferencedIncludes, err)
   598  	}
   599  
   600  	var result ListReferencedIncludesResponse
   601  	resp, err := p.Exec(req, &result)
   602  	if err != nil {
   603  		return nil, fmt.Errorf("%w: request failed: %s", ErrListReferencedIncludes, err)
   604  	}
   605  
   606  	if resp.StatusCode != http.StatusOK {
   607  		return nil, fmt.Errorf("%s: %w", ErrListReferencedIncludes, p.Error(resp))
   608  	}
   609  
   610  	return &result, nil
   611  }
   612  
   613  // UnmarshalJSON reads a ListAvailableIncludesResponse struct from its data argument and transform map of includes into array for better usability.
   614  func (r *ListAvailableIncludesResponse) UnmarshalJSON(data []byte) error {
   615  	var response struct {
   616  		ExternalResources struct {
   617  			ExternalIncludes map[string]ExternalIncludeData `json:"include"`
   618  		} `json:"externalResources"`
   619  	}
   620  
   621  	if err := json.Unmarshal(data, &response); err != nil {
   622  		return err
   623  	}
   624  
   625  	r.AvailableIncludes = make([]ExternalIncludeData, 0, len(response.ExternalResources.ExternalIncludes))
   626  	for _, include := range response.ExternalResources.ExternalIncludes {
   627  		r.AvailableIncludes = append(r.AvailableIncludes, include)
   628  	}
   629  
   630  	return nil
   631  }