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 }