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