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