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 }