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

     1  package papi
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  
    10  	validation "github.com/go-ozzo/ozzo-validation/v4"
    11  	"github.com/spf13/cast"
    12  )
    13  
    14  type (
    15  	// Activations contains operations available on Activation resource
    16  	Activations interface {
    17  		// CreateActivation creates a new activation or deactivation request
    18  		//
    19  		// See: https://techdocs.akamai.com/property-mgr/reference/post-property-activations
    20  		CreateActivation(context.Context, CreateActivationRequest) (*CreateActivationResponse, error)
    21  
    22  		// GetActivations returns a list of the property activations
    23  		//
    24  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-activations
    25  		GetActivations(ctx context.Context, params GetActivationsRequest) (*GetActivationsResponse, error)
    26  
    27  		// GetActivation gets details about an activation
    28  		//
    29  		// See: https://techdocs.akamai.com/property-mgr/reference/get-property-activation
    30  		GetActivation(context.Context, GetActivationRequest) (*GetActivationResponse, error)
    31  
    32  		// CancelActivation allows for canceling an activation while it is still PENDING
    33  		//
    34  		// https://techdocs.akamai.com/property-mgr/reference/delete-property-activation
    35  		CancelActivation(context.Context, CancelActivationRequest) (*CancelActivationResponse, error)
    36  	}
    37  
    38  	// ActivationFallbackInfo encapsulates information about fast fallback, which may allow you to fallback to a previous activation when
    39  	// POSTing an activation with useFastFallback enabled.
    40  	ActivationFallbackInfo struct {
    41  		FastFallbackAttempted      bool    `json:"fastFallbackAttempted"`
    42  		FallbackVersion            int     `json:"fallbackVersion"`
    43  		CanFastFallback            bool    `json:"canFastFallback"`
    44  		SteadyStateTime            int     `json:"steadyStateTime"`
    45  		FastFallbackExpirationTime int     `json:"fastFallbackExpirationTime"`
    46  		FastFallbackRecoveryState  *string `json:"fastFallbackRecoveryState,omitempty"`
    47  	}
    48  
    49  	// Activation represents a property activation resource
    50  	Activation struct {
    51  		AccountID              string                  `json:"accountId,omitempty"`
    52  		ActivationID           string                  `json:"activationId,omitempty"`
    53  		ActivationType         ActivationType          `json:"activationType,omitempty"`
    54  		UseFastFallback        bool                    `json:"useFastFallback"`
    55  		FallbackInfo           *ActivationFallbackInfo `json:"fallbackInfo,omitempty"`
    56  		AcknowledgeWarnings    []string                `json:"acknowledgeWarnings,omitempty"`
    57  		AcknowledgeAllWarnings bool                    `json:"acknowledgeAllWarnings"`
    58  		FastPush               bool                    `json:"fastPush,omitempty"`
    59  		FMAActivationState     string                  `json:"fmaActivationState,omitempty"`
    60  		GroupID                string                  `json:"groupId,omitempty"`
    61  		IgnoreHTTPErrors       bool                    `json:"ignoreHttpErrors,omitempty"`
    62  		PropertyName           string                  `json:"propertyName,omitempty"`
    63  		PropertyID             string                  `json:"propertyId,omitempty"`
    64  		PropertyVersion        int                     `json:"propertyVersion"`
    65  		Network                ActivationNetwork       `json:"network"`
    66  		Status                 ActivationStatus        `json:"status,omitempty"`
    67  		SubmitDate             string                  `json:"submitDate,omitempty"`
    68  		UpdateDate             string                  `json:"updateDate,omitempty"`
    69  		Note                   string                  `json:"note,omitempty"`
    70  		NotifyEmails           []string                `json:"notifyEmails"`
    71  		ComplianceRecord       complianceRecord        `json:"complianceRecord,omitempty"`
    72  	}
    73  
    74  	// CreateActivationRequest is the request parameters for a new activation or deactivation request
    75  	CreateActivationRequest struct {
    76  		PropertyID string
    77  		ContractID string
    78  		GroupID    string
    79  		Activation Activation
    80  	}
    81  
    82  	// ActivationsItems are the activation items array from a response
    83  	ActivationsItems struct {
    84  		Items []*Activation `json:"items"`
    85  	}
    86  
    87  	// GetActivationsResponse is the get activation response
    88  	GetActivationsResponse struct {
    89  		Response
    90  
    91  		Activations ActivationsItems `json:"activations"`
    92  
    93  		// RetryAfter is the value of the Retry-After header.
    94  		//  For activations whose status is PENDING, a Retry-After header provides an estimate for when it’s likely to change.
    95  		RetryAfter int `json:"-"`
    96  	}
    97  
    98  	// CreateActivationResponse is the response for a new activation or deactivation
    99  	CreateActivationResponse struct {
   100  		Response
   101  		ActivationID   string
   102  		ActivationLink string `json:"activationLink"`
   103  	}
   104  
   105  	// GetActivationsRequest is the get activation request
   106  	GetActivationsRequest struct {
   107  		PropertyID string
   108  		ContractID string
   109  		GroupID    string
   110  	}
   111  
   112  	// GetActivationRequest is the get activation request
   113  	GetActivationRequest struct {
   114  		PropertyID   string
   115  		ContractID   string
   116  		GroupID      string
   117  		ActivationID string
   118  	}
   119  
   120  	// GetActivationResponse is the get activation response
   121  	GetActivationResponse struct {
   122  		GetActivationsResponse
   123  		Activation *Activation `json:"-"`
   124  	}
   125  
   126  	// CancelActivationRequest is used to delete a PENDING activation
   127  	CancelActivationRequest struct {
   128  		PropertyID   string
   129  		ActivationID string
   130  		ContractID   string
   131  		GroupID      string
   132  	}
   133  
   134  	// CancelActivationResponse is a response from deleting a PENDING activation
   135  	CancelActivationResponse struct {
   136  		Activations ActivationsItems `json:"activations"`
   137  	}
   138  
   139  	// ActivationType is an activation type value
   140  	ActivationType string
   141  
   142  	// ActivationStatus is an activation status value
   143  	ActivationStatus string
   144  
   145  	// ActivationNetwork is the activation network value
   146  	ActivationNetwork string
   147  )
   148  
   149  const (
   150  	// ActivationTypeActivate is used for creating a new activation
   151  	ActivationTypeActivate ActivationType = "ACTIVATE"
   152  
   153  	// ActivationTypeDeactivate is used for creating a new de-activation
   154  	ActivationTypeDeactivate ActivationType = "DEACTIVATE"
   155  
   156  	// ActivationStatusActive is an activation that is currently serving traffic
   157  	ActivationStatusActive ActivationStatus = "ACTIVE"
   158  
   159  	// ActivationStatusInactive is an activation that has been superseded by another
   160  	ActivationStatusInactive ActivationStatus = "INACTIVE"
   161  
   162  	// ActivationStatusNew is a not yet active activation
   163  	ActivationStatusNew ActivationStatus = "NEW"
   164  
   165  	// ActivationStatusPending is the pending status
   166  	ActivationStatusPending ActivationStatus = "PENDING"
   167  
   168  	// ActivationStatusAborted is returned when a PENDING activation is successfully canceled
   169  	ActivationStatusAborted ActivationStatus = "ABORTED"
   170  
   171  	// ActivationStatusFailed is returned when an activation fails downstream from the api
   172  	ActivationStatusFailed ActivationStatus = "FAILED"
   173  
   174  	// ActivationStatusZone1 is not yet active
   175  	ActivationStatusZone1 ActivationStatus = "ZONE_1"
   176  
   177  	// ActivationStatusZone2 is not yet active
   178  	ActivationStatusZone2 ActivationStatus = "ZONE_2"
   179  
   180  	// ActivationStatusZone3 is not yet active
   181  	ActivationStatusZone3 ActivationStatus = "ZONE_3"
   182  
   183  	// ActivationStatusDeactivating is pending deactivation
   184  	ActivationStatusDeactivating ActivationStatus = "PENDING_DEACTIVATION"
   185  
   186  	// ActivationStatusCancelling is returned when cancellation is in progress
   187  	ActivationStatusCancelling ActivationStatus = "PENDING_CANCELLATION"
   188  
   189  	// ActivationStatusDeactivated is deactivated
   190  	ActivationStatusDeactivated ActivationStatus = "DEACTIVATED"
   191  
   192  	// ActivationNetworkStaging is the staging network
   193  	ActivationNetworkStaging ActivationNetwork = "STAGING"
   194  
   195  	// ActivationNetworkProduction is the production network
   196  	ActivationNetworkProduction ActivationNetwork = "PRODUCTION"
   197  )
   198  
   199  // Validate validates CreateActivationRequest
   200  func (v CreateActivationRequest) Validate() error {
   201  	return validation.Errors{
   202  		"PropertyID":                    validation.Validate(v.PropertyID, validation.Required),
   203  		"Activation.AccountID":          validation.Validate(v.Activation.AccountID, validation.Empty),
   204  		"Activation.ActivationID":       validation.Validate(v.Activation.ActivationID, validation.Empty),
   205  		"Activation.FallbackInfo":       validation.Validate(v.Activation.FallbackInfo, validation.Nil),
   206  		"Activation.FMAActivationState": validation.Validate(v.Activation.FMAActivationState, validation.Empty),
   207  		"Activation.GroupID":            validation.Validate(v.Activation.GroupID, validation.Empty),
   208  		"Activation.Network":            validation.Validate(v.Activation.Network, validation.In(ActivationNetworkStaging, ActivationNetworkProduction)),
   209  		"Activation.NotifyEmails":       validation.Validate(v.Activation.NotifyEmails, validation.Length(1, 0)),
   210  		"Activation.PropertyID":         validation.Validate(v.Activation.PropertyID, validation.Empty),
   211  		"Activation.PropertyName":       validation.Validate(v.Activation.PropertyName, validation.Empty),
   212  		"Activation.Status":             validation.Validate(v.Activation.Status, validation.Empty),
   213  		"Activation.SubmitDate":         validation.Validate(v.Activation.SubmitDate, validation.Empty),
   214  		"Activation.UpdateDate":         validation.Validate(v.Activation.UpdateDate, validation.Empty),
   215  		"Activation.Type":               validation.Validate(v.Activation.ActivationType, validation.In(ActivationTypeActivate, ActivationTypeDeactivate)),
   216  		"Activation.ComplianceRecord": validation.Validate(v.Activation.ComplianceRecord,
   217  			validation.When(v.Activation.Network == ActivationNetworkProduction, validation.By(unitTestedFieldValidationRule))),
   218  	}.Filter()
   219  }
   220  
   221  // Validate validates GetActivationsRequest
   222  func (v GetActivationsRequest) Validate() error {
   223  	return validation.Errors{
   224  		"PropertyID": validation.Validate(v.PropertyID, validation.Required),
   225  	}.Filter()
   226  }
   227  
   228  // Validate validates GetActivationRequest
   229  func (v GetActivationRequest) Validate() error {
   230  	return validation.Errors{
   231  		"PropertyID":   validation.Validate(v.PropertyID, validation.Required),
   232  		"ActivationID": validation.Validate(v.ActivationID, validation.Required),
   233  	}.Filter()
   234  }
   235  
   236  // Validate validate CancelActivationRequest
   237  func (v CancelActivationRequest) Validate() error {
   238  	return validation.Errors{
   239  		"PropertyID":   validation.Validate(v.PropertyID, validation.Required),
   240  		"ActivationID": validation.Validate(v.ActivationID, validation.Required),
   241  	}.Filter()
   242  }
   243  
   244  var (
   245  	// ErrCreateActivation represents error when creating activation fails
   246  	ErrCreateActivation = errors.New("creating activation")
   247  	// ErrGetActivations represents error when fetching activations fails
   248  	ErrGetActivations = errors.New("fetching activations")
   249  	// ErrGetActivation represents error when fetching activation fails
   250  	ErrGetActivation = errors.New("fetching activation")
   251  	// ErrCancelActivation represents error when canceling activation fails
   252  	ErrCancelActivation = errors.New("canceling activation")
   253  )
   254  
   255  func (p *papi) CreateActivation(ctx context.Context, params CreateActivationRequest) (*CreateActivationResponse, error) {
   256  	if err := params.Validate(); err != nil {
   257  		return nil, fmt.Errorf("%s: %w: %s", ErrCreateActivation, ErrStructValidation, err)
   258  	}
   259  
   260  	logger := p.Log(ctx)
   261  	logger.Debug("CreateActivation")
   262  
   263  	// explicitly set the activation type
   264  	if params.Activation.ActivationType == "" {
   265  		params.Activation.ActivationType = ActivationTypeActivate
   266  	}
   267  
   268  	uri, err := url.Parse(fmt.Sprintf(
   269  		"/papi/v1/properties/%s/activations",
   270  		params.PropertyID),
   271  	)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrCreateActivation, err)
   274  	}
   275  	q := uri.Query()
   276  	if params.GroupID != "" {
   277  		q.Add("groupId", params.GroupID)
   278  	}
   279  	if params.ContractID != "" {
   280  		q.Add("contractId", params.ContractID)
   281  	}
   282  	uri.RawQuery = q.Encode()
   283  
   284  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil)
   285  	if err != nil {
   286  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateActivation, err)
   287  	}
   288  
   289  	var rval CreateActivationResponse
   290  
   291  	resp, err := p.Exec(req, &rval, params.Activation)
   292  	if err != nil {
   293  		return nil, fmt.Errorf("%w: request failed: %s", ErrCreateActivation, err)
   294  	}
   295  
   296  	if resp.StatusCode != http.StatusCreated {
   297  		return nil, fmt.Errorf("%s: %w", ErrCreateActivation, p.Error(resp))
   298  	}
   299  
   300  	id, err := ResponseLinkParse(rval.ActivationLink)
   301  	if err != nil {
   302  		return nil, fmt.Errorf("%s: %w: %s", ErrCreateActivation, ErrInvalidResponseLink, err)
   303  	}
   304  	rval.ActivationID = id
   305  
   306  	return &rval, nil
   307  }
   308  
   309  func (p *papi) GetActivations(ctx context.Context, params GetActivationsRequest) (*GetActivationsResponse, error) {
   310  	if err := params.Validate(); err != nil {
   311  		return nil, fmt.Errorf("%s: %w: %s", ErrGetActivations, ErrStructValidation, err)
   312  	}
   313  
   314  	logger := p.Log(ctx)
   315  	logger.Debug("GetActivations")
   316  
   317  	uri, err := url.Parse(fmt.Sprintf(
   318  		"/papi/v1/properties/%s/activations",
   319  		params.PropertyID),
   320  	)
   321  	if err != nil {
   322  		return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetActivations, err)
   323  	}
   324  	q := uri.Query()
   325  	if params.GroupID != "" {
   326  		q.Add("groupId", params.GroupID)
   327  	}
   328  	if params.ContractID != "" {
   329  		q.Add("contractId", params.ContractID)
   330  	}
   331  	uri.RawQuery = q.Encode()
   332  
   333  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
   334  	if err != nil {
   335  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetActivations, err)
   336  	}
   337  
   338  	var rval GetActivationsResponse
   339  
   340  	resp, err := p.Exec(req, &rval)
   341  	if err != nil {
   342  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetActivations, err)
   343  	}
   344  
   345  	if resp.StatusCode != http.StatusOK {
   346  		return nil, fmt.Errorf("%s: %w", ErrGetActivations, p.Error(resp))
   347  	}
   348  
   349  	return &rval, nil
   350  }
   351  
   352  func (p *papi) GetActivation(ctx context.Context, params GetActivationRequest) (*GetActivationResponse, error) {
   353  	if err := params.Validate(); err != nil {
   354  		return nil, fmt.Errorf("%s: %w: %s", ErrGetActivation, ErrStructValidation, err)
   355  	}
   356  
   357  	logger := p.Log(ctx)
   358  	logger.Debug("GetActivation")
   359  
   360  	uri := fmt.Sprintf(
   361  		"/papi/v1/properties/%s/activations/%s?contractId=%s&groupId=%s",
   362  		params.PropertyID,
   363  		params.ActivationID,
   364  		params.ContractID,
   365  		params.GroupID)
   366  
   367  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
   368  	if err != nil {
   369  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetActivation, err)
   370  	}
   371  
   372  	var rval GetActivationResponse
   373  
   374  	resp, err := p.Exec(req, &rval)
   375  	if err != nil {
   376  		return nil, fmt.Errorf("%w: request failed: %s", ErrGetActivation, err)
   377  	}
   378  
   379  	if resp.StatusCode != http.StatusOK {
   380  		return nil, fmt.Errorf("%s: %w", ErrGetActivation, p.Error(resp))
   381  	}
   382  
   383  	// Get the Retry-After header to return the caller
   384  	if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
   385  		rval.RetryAfter = cast.ToInt(retryAfter)
   386  	}
   387  
   388  	if len(rval.Activations.Items) == 0 {
   389  		return nil, fmt.Errorf("%s: %w: ActivationID: %s", ErrGetActivation, ErrNotFound, params.ActivationID)
   390  	}
   391  	rval.Activation = rval.Activations.Items[0]
   392  
   393  	return &rval, nil
   394  }
   395  
   396  func (p *papi) CancelActivation(ctx context.Context, params CancelActivationRequest) (*CancelActivationResponse, error) {
   397  	if err := params.Validate(); err != nil {
   398  		return nil, fmt.Errorf("%s: %w: %s", ErrCancelActivation, ErrStructValidation, err)
   399  	}
   400  
   401  	logger := p.Log(ctx)
   402  	logger.Debug("CancelActivation")
   403  
   404  	uri := fmt.Sprintf(
   405  		"/papi/v1/properties/%s/activations/%s?contractId=%s&groupId=%s",
   406  		params.PropertyID,
   407  		params.ActivationID,
   408  		params.ContractID,
   409  		params.GroupID)
   410  
   411  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil)
   412  	if err != nil {
   413  		return nil, fmt.Errorf("%w: failed to create request: %s", ErrCancelActivation, err)
   414  	}
   415  
   416  	var rval CancelActivationResponse
   417  
   418  	resp, err := p.Exec(req, &rval)
   419  	if err != nil {
   420  		return nil, fmt.Errorf("%w: request failed: %s", ErrCancelActivation, err)
   421  	}
   422  
   423  	if resp.StatusCode != http.StatusOK {
   424  		return nil, fmt.Errorf("%s: %w", ErrCancelActivation, p.Error(resp))
   425  	}
   426  
   427  	return &rval, nil
   428  }