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 }