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

     1  package papi
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  
    10  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegriderr"
    11  	validation "github.com/go-ozzo/ozzo-validation/v4"
    12  )
    13  
    14  type (
    15  	// Properties contains operations available on Property resource
    16  	Properties interface {
    17  		// GetProperties lists properties available for the current contract and group
    18  		//
    19  		// https://techdocs.akamai.com/property-mgr/reference/get-properties
    20  		GetProperties(ctx context.Context, r GetPropertiesRequest) (*GetPropertiesResponse, error)
    21  
    22  		// CreateProperty creates a new property from scratch or bases one on another property's rule tree and optionally its set of assigned hostnames
    23  		//
    24  		// https://techdocs.akamai.com/property-mgr/reference/post-properties
    25  		CreateProperty(ctx context.Context, params CreatePropertyRequest) (*CreatePropertyResponse, error)
    26  
    27  		// GetProperty gets a specific property
    28  		//
    29  		// https://techdocs.akamai.com/property-mgr/reference/get-property
    30  		GetProperty(ctx context.Context, params GetPropertyRequest) (*GetPropertyResponse, error)
    31  
    32  		// RemoveProperty removes a specific property, which you can only do if none of its versions are currently active
    33  		//
    34  		// https://techdocs.akamai.com/property-mgr/reference/delete-property
    35  		RemoveProperty(ctx context.Context, params RemovePropertyRequest) (*RemovePropertyResponse, error)
    36  	}
    37  
    38  	// PropertyCloneFrom optionally identifies another property instance to clone when making a POST request to create a new property
    39  	PropertyCloneFrom struct {
    40  		CloneFromVersionEtag string `json:"cloneFromVersionEtag,omitempty"`
    41  		CopyHostnames        bool   `json:"copyHostnames,omitempty"`
    42  		PropertyID           string `json:"propertyId"`
    43  		Version              int    `json:"version"`
    44  	}
    45  
    46  	// Property contains configuration data to apply to edge content.
    47  	Property struct {
    48  		AccountID         string `json:"accountId"`
    49  		AssetID           string `json:"assetId"`
    50  		ContractID        string `json:"contractId"`
    51  		GroupID           string `json:"groupId"`
    52  		LatestVersion     int    `json:"latestVersion"`
    53  		Note              string `json:"note"`
    54  		ProductID         string `json:"productId"`
    55  		ProductionVersion *int   `json:"productionVersion,omitempty"`
    56  		PropertyID        string `json:"propertyId"`
    57  		PropertyName      string `json:"propertyName"`
    58  		RuleFormat        string `json:"ruleFormat"`
    59  		StagingVersion    *int   `json:"stagingVersion,omitempty"`
    60  	}
    61  
    62  	// PropertiesItems is an array of properties
    63  	PropertiesItems struct {
    64  		Items []*Property `json:"items"`
    65  	}
    66  
    67  	// GetPropertiesRequest is the argument for GetProperties
    68  	GetPropertiesRequest struct {
    69  		ContractID string
    70  		GroupID    string
    71  	}
    72  
    73  	// GetPropertiesResponse is the response for GetProperties
    74  	GetPropertiesResponse struct {
    75  		Properties PropertiesItems `json:"properties"`
    76  	}
    77  
    78  	// CreatePropertyRequest is passed to CreateProperty
    79  	CreatePropertyRequest struct {
    80  		ContractID string
    81  		GroupID    string
    82  		Property   PropertyCreate
    83  	}
    84  
    85  	// PropertyCreate represents a POST /property request body
    86  	PropertyCreate struct {
    87  		CloneFrom    *PropertyCloneFrom `json:"cloneFrom,omitempty"`
    88  		ProductID    string             `json:"productId"`
    89  		PropertyName string             `json:"propertyName"`
    90  		RuleFormat   string             `json:"ruleFormat,omitempty"`
    91  	}
    92  
    93  	// CreatePropertyResponse is returned by CreateProperty
    94  	CreatePropertyResponse struct {
    95  		Response
    96  		PropertyID   string
    97  		PropertyLink string `json:"propertyLink"`
    98  	}
    99  
   100  	// GetPropertyRequest is the argument for GetProperty
   101  	GetPropertyRequest struct {
   102  		ContractID string
   103  		GroupID    string
   104  		PropertyID string
   105  	}
   106  
   107  	// GetPropertyResponse is the response for GetProperty
   108  	GetPropertyResponse struct {
   109  		Response
   110  		Properties PropertiesItems `json:"properties"`
   111  		Property   *Property       `json:"-"`
   112  	}
   113  
   114  	// RemovePropertyRequest is the argument for RemoveProperty
   115  	RemovePropertyRequest struct {
   116  		PropertyID string
   117  		ContractID string
   118  		GroupID    string
   119  	}
   120  
   121  	// RemovePropertyResponse is the response for GetProperties
   122  	RemovePropertyResponse struct {
   123  		Message string `json:"message"`
   124  	}
   125  )
   126  
   127  // Validate validates GetPropertiesRequest
   128  func (v GetPropertiesRequest) Validate() error {
   129  	return validation.Errors{
   130  		"ContractID": validation.Validate(v.ContractID, validation.Required),
   131  		"GroupID":    validation.Validate(v.GroupID, validation.Required),
   132  	}.Filter()
   133  }
   134  
   135  // Validate validates CreatePropertyRequest
   136  func (v CreatePropertyRequest) Validate() error {
   137  	errs := validation.Errors{
   138  		"ContractID": validation.Validate(v.ContractID, validation.Required),
   139  		"GroupID":    validation.Validate(v.GroupID, validation.Required),
   140  		"Property":   validation.Validate(v.Property),
   141  	}
   142  	return edgegriderr.ParseValidationErrors(errs)
   143  }
   144  
   145  // Validate validates PropertyCreate
   146  func (p PropertyCreate) Validate() error {
   147  	return validation.Errors{
   148  		"ProductID":    validation.Validate(p.ProductID, validation.Required),
   149  		"PropertyName": validation.Validate(p.PropertyName, validation.Required),
   150  		"CloneFrom":    validation.Validate(p.CloneFrom),
   151  	}.Filter()
   152  }
   153  
   154  // Validate validates PropertyCloneFrom
   155  func (c PropertyCloneFrom) Validate() error {
   156  	return validation.Errors{
   157  		"PropertyID": validation.Validate(c.PropertyID),
   158  		"Version":    validation.Validate(c.Version),
   159  	}.Filter()
   160  }
   161  
   162  // Validate validates GetPropertyRequest
   163  func (v GetPropertyRequest) Validate() error {
   164  	return validation.Errors{
   165  		"PropertyID": validation.Validate(v.PropertyID, validation.Required),
   166  	}.Filter()
   167  }
   168  
   169  // Validate validates RemovePropertyRequest
   170  func (v RemovePropertyRequest) Validate() error {
   171  	return validation.Errors{
   172  		"PropertyID": validation.Validate(v.PropertyID, validation.Required),
   173  	}.Filter()
   174  }
   175  
   176  var (
   177  	// ErrGetProperties represents error when fetching properties fails
   178  	ErrGetProperties = errors.New("fetching properties")
   179  	// ErrGetProperty represents error when fetching property fails
   180  	ErrGetProperty = errors.New("fetching property")
   181  	// ErrCreateProperty represents error when creating property fails
   182  	ErrCreateProperty = errors.New("creating property")
   183  	// ErrRemoveProperty represents error when removing property fails
   184  	ErrRemoveProperty = errors.New("removing property")
   185  )
   186  
   187  func (p *papi) GetProperties(ctx context.Context, params GetPropertiesRequest) (*GetPropertiesResponse, error) {
   188  	if err := params.Validate(); err != nil {
   189  		return nil, fmt.Errorf("%s: %w: %s", ErrGetProperties, ErrStructValidation, err)
   190  	}
   191  
   192  	var rval GetPropertiesResponse
   193  
   194  	logger := p.Log(ctx)
   195  	logger.Debug("GetProperties")
   196  
   197  	uri := fmt.Sprintf(
   198  		"/papi/v1/properties?contractId=%s&groupId=%s",
   199  		params.ContractID,
   200  		params.GroupID)
   201  
   202  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   203  	if err != nil {
   204  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetProperties, err)
   205  	}
   206  
   207  	resp, err := p.Exec(req, &rval)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetProperties, err)
   210  	}
   211  
   212  	if resp.StatusCode != http.StatusOK {
   213  		return nil, fmt.Errorf("%s: %w", ErrGetProperties, p.Error(resp))
   214  	}
   215  
   216  	return &rval, nil
   217  }
   218  
   219  func (p *papi) CreateProperty(ctx context.Context, params CreatePropertyRequest) (*CreatePropertyResponse, error) {
   220  	if err := params.Validate(); err != nil {
   221  		return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateProperty, ErrStructValidation, err)
   222  	}
   223  
   224  	logger := p.Log(ctx)
   225  	logger.Debug("CreateProperty")
   226  
   227  	uri := fmt.Sprintf(
   228  		"/papi/v1/properties?contractId=%s&groupId=%s",
   229  		params.ContractID,
   230  		params.GroupID)
   231  
   232  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, nil)
   233  	if err != nil {
   234  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateProperty, err)
   235  	}
   236  
   237  	var rval CreatePropertyResponse
   238  
   239  	resp, err := p.Exec(req, &rval, params.Property)
   240  	if err != nil {
   241  		return nil, fmt.Errorf("%w: request failed: %s", ErrCreateProperty, err)
   242  	}
   243  
   244  	if resp.StatusCode == http.StatusNotFound {
   245  		return nil, fmt.Errorf("%s: %w: %s", ErrCreateProperty, ErrNotFound, err)
   246  	}
   247  
   248  	if resp.StatusCode != http.StatusCreated {
   249  		return nil, fmt.Errorf("%s: %w", ErrCreateProperty, p.Error(resp))
   250  	}
   251  
   252  	id, err := ResponseLinkParse(rval.PropertyLink)
   253  	if err != nil {
   254  		return nil, fmt.Errorf("%s: %w: %s", ErrCreateProperty, ErrInvalidResponseLink, err)
   255  	}
   256  
   257  	rval.PropertyID = id
   258  
   259  	return &rval, nil
   260  }
   261  
   262  func (p *papi) GetProperty(ctx context.Context, params GetPropertyRequest) (*GetPropertyResponse, error) {
   263  	if err := params.Validate(); err != nil {
   264  		return nil, fmt.Errorf("%s: %w: %s", ErrGetProperty, ErrStructValidation, err)
   265  	}
   266  
   267  	var rval GetPropertyResponse
   268  
   269  	logger := p.Log(ctx)
   270  	logger.Debug("GetProperty")
   271  
   272  	uri, err := url.Parse(fmt.Sprintf(
   273  		"/papi/v1/properties/%s",
   274  		params.PropertyID),
   275  	)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetProperty, err)
   278  	}
   279  	q := uri.Query()
   280  	if params.GroupID != "" {
   281  		q.Add("groupId", params.GroupID)
   282  	}
   283  	if params.ContractID != "" {
   284  		q.Add("contractId", params.ContractID)
   285  	}
   286  	uri.RawQuery = q.Encode()
   287  
   288  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   289  	if err != nil {
   290  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetProperty, err)
   291  	}
   292  
   293  	resp, err := p.Exec(req, &rval)
   294  	if err != nil {
   295  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetProperty, err)
   296  	}
   297  
   298  	if resp.StatusCode != http.StatusOK {
   299  		return nil, fmt.Errorf("%s: %w", ErrGetProperty, p.Error(resp))
   300  	}
   301  
   302  	if len(rval.Properties.Items) == 0 {
   303  		return nil, fmt.Errorf("%s: %w: PropertyID: %s", ErrGetProperty, ErrNotFound, params.PropertyID)
   304  	}
   305  	rval.Property = rval.Properties.Items[0]
   306  
   307  	return &rval, nil
   308  }
   309  
   310  func (p *papi) RemoveProperty(ctx context.Context, params RemovePropertyRequest) (*RemovePropertyResponse, error) {
   311  	if err := params.Validate(); err != nil {
   312  		return nil, fmt.Errorf("%s: %w: %s", ErrRemoveProperty, ErrStructValidation, err)
   313  	}
   314  
   315  	var rval RemovePropertyResponse
   316  
   317  	logger := p.Log(ctx)
   318  	logger.Debug("RemoveProperty")
   319  
   320  	uri, err := url.Parse(fmt.Sprintf(
   321  		"/papi/v1/properties/%s",
   322  		params.PropertyID),
   323  	)
   324  	if err != nil {
   325  		return nil, fmt.Errorf("%w: failed parse url: %s", ErrRemoveProperty, err)
   326  	}
   327  	q := uri.Query()
   328  	if params.GroupID != "" {
   329  		q.Add("groupId", params.GroupID)
   330  	}
   331  	if params.ContractID != "" {
   332  		q.Add("contractId", params.ContractID)
   333  	}
   334  	uri.RawQuery = q.Encode()
   335  
   336  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri.String(), nil)
   337  	if err != nil {
   338  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrRemoveProperty, err)
   339  	}
   340  
   341  	resp, err := p.Exec(req, &rval)
   342  	if err != nil {
   343  		return nil, fmt.Errorf("%w: request failed: %s", ErrRemoveProperty, err)
   344  	}
   345  
   346  	if resp.StatusCode != http.StatusOK {
   347  		return nil, fmt.Errorf("%s: %w", ErrRemoveProperty, p.Error(resp))
   348  	}
   349  
   350  	return &rval, nil
   351  }