github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/cps/changes.go (about) 1 package cps 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 ) 12 13 type ( 14 // ChangeOperations is a CPS change API interface 15 ChangeOperations interface { 16 // GetChangeStatus fetches change status for given enrollment and change ID 17 // 18 // See: https://techdocs.akamai.com/cps/reference/get-enrollment-change 19 GetChangeStatus(context.Context, GetChangeStatusRequest) (*Change, error) 20 21 // CancelChange cancels a pending change 22 // 23 // See: https://techdocs.akamai.com/cps/reference/delete-enrollment-change 24 CancelChange(context.Context, CancelChangeRequest) (*CancelChangeResponse, error) 25 26 // UpdateChange updates a pending change 27 // Deprecated: this function will be removed in a future release. Use one of: 28 // AcknowledgeChangeManagement(), AcknowledgePostVerificationWarnings(), 29 // AcknowledgePreVerificationWarnings(), UploadThirdPartyCertAndTrustChain() 30 // or AcknowledgeDVChallenges() 31 // 32 // See: https://techdocs.akamai.com/cps/reference/post-change-allowed-input-param 33 UpdateChange(context.Context, UpdateChangeRequest) (*UpdateChangeResponse, error) 34 } 35 36 // Change contains change status information 37 Change struct { 38 AllowedInput []AllowedInput `json:"allowedInput"` 39 StatusInfo *StatusInfo `json:"statusInfo"` 40 } 41 42 // AllowedInput contains the resource locations (path) of data inputs allowed by this Change 43 AllowedInput struct { 44 Info string `json:"info"` 45 RequiredToProceed bool `json:"requiredToProceed"` 46 Type string `json:"type"` 47 Update string `json:"update"` 48 } 49 50 // StatusInfo contains the status for this Change at this time 51 StatusInfo struct { 52 DeploymentSchedule *DeploymentSchedule `json:"deploymentSchedule"` 53 Description string `json:"description"` 54 Error *StatusInfoError `json:"error,omitempty"` 55 State string `json:"state"` 56 Status string `json:"status"` 57 } 58 59 // StatusInfoError contains error information for this Change 60 StatusInfoError struct { 61 Code string `json:"code"` 62 Description string `json:"description"` 63 Timestamp string `json:"timestamp"` 64 } 65 66 // Certificate is a digital certificate object 67 Certificate struct { 68 Certificate string `json:"certificate"` 69 TrustChain string `json:"trustChain,omitempty"` 70 } 71 72 // GetChangeStatusRequest contains params required to perform GetChangeStatus 73 GetChangeStatusRequest struct { 74 EnrollmentID int 75 ChangeID int 76 } 77 78 // GetChangeRequest contains params required to fetch a specific change (e.g. DV challenges) 79 // It is the same for all GET change requests 80 GetChangeRequest struct { 81 EnrollmentID int 82 ChangeID int 83 } 84 85 // CancelChangeRequest contains params required to send CancelChange request 86 CancelChangeRequest struct { 87 EnrollmentID int 88 ChangeID int 89 } 90 91 // CancelChangeResponse is a response object returned from CancelChange request 92 CancelChangeResponse struct { 93 Change string `json:"change"` 94 } 95 96 // UpdateChangeRequest contains params and body required to send UpdateChange request 97 UpdateChangeRequest struct { 98 Certificate 99 EnrollmentID int 100 ChangeID int 101 AllowedInputTypeParam AllowedInputType 102 } 103 104 // UpdateChangeResponse is a response object returned from UpdateChange request 105 UpdateChangeResponse struct { 106 Change string `json:"change"` 107 } 108 109 // AcknowledgementRequest contains params and body required to send acknowledgement. It is the same for all acknowledgement types (dv, pre-verification-warnings etc.) 110 AcknowledgementRequest struct { 111 Acknowledgement 112 EnrollmentID int 113 ChangeID int 114 } 115 116 // Acknowledgement is a request body of acknowledgement request 117 Acknowledgement struct { 118 Acknowledgement string `json:"acknowledgement"` 119 } 120 121 // AllowedInputType represents allowedInputTypeParam used for fetching and updating changes 122 AllowedInputType string 123 ) 124 125 const ( 126 // AllowedInputTypeChangeManagementACK parameter value 127 AllowedInputTypeChangeManagementACK AllowedInputType = "change-management-ack" 128 // AllowedInputTypeLetsEncryptChallengesCompleted parameter value 129 AllowedInputTypeLetsEncryptChallengesCompleted AllowedInputType = "lets-encrypt-challenges-completed" 130 // AllowedInputTypePostVerificationWarningsACK parameter value 131 AllowedInputTypePostVerificationWarningsACK AllowedInputType = "post-verification-warnings-ack" 132 // AllowedInputTypePreVerificationWarningsACK parameter value 133 AllowedInputTypePreVerificationWarningsACK AllowedInputType = "pre-verification-warnings-ack" 134 // AllowedInputTypeThirdPartyCertAndTrustChain parameter value 135 AllowedInputTypeThirdPartyCertAndTrustChain AllowedInputType = "third-party-cert-and-trust-chain" 136 ) 137 138 const ( 139 // AcknowledgementAcknowledge parameter value 140 AcknowledgementAcknowledge = "acknowledge" 141 // AcknowledgementDeny parameter value 142 AcknowledgementDeny = "deny" 143 ) 144 145 // AllowedInputContentTypeHeader maps content type headers to specific allowed input type params 146 var AllowedInputContentTypeHeader = map[AllowedInputType]string{ 147 AllowedInputTypeChangeManagementACK: "application/vnd.akamai.cps.acknowledgement-with-hash.v1+json", 148 AllowedInputTypeLetsEncryptChallengesCompleted: "application/vnd.akamai.cps.acknowledgement.v1+json", 149 AllowedInputTypePostVerificationWarningsACK: "application/vnd.akamai.cps.acknowledgement.v1+json", 150 AllowedInputTypePreVerificationWarningsACK: "application/vnd.akamai.cps.acknowledgement.v1+json", 151 AllowedInputTypeThirdPartyCertAndTrustChain: "application/vnd.akamai.cps.certificate-and-trust-chain.v1+json", 152 } 153 154 // Validate validates GetChangeRequest 155 func (c GetChangeRequest) Validate() error { 156 return validation.Errors{ 157 "EnrollmentID": validation.Validate(c.EnrollmentID, validation.Required), 158 "ChangeID": validation.Validate(c.ChangeID, validation.Required), 159 }.Filter() 160 } 161 162 // Validate validates GetChangeStatusRequest 163 func (c GetChangeStatusRequest) Validate() error { 164 return validation.Errors{ 165 "EnrollmentID": validation.Validate(c.EnrollmentID, validation.Required), 166 "ChangeID": validation.Validate(c.ChangeID, validation.Required), 167 }.Filter() 168 } 169 170 // Validate validates CancelChangeRequest 171 func (c CancelChangeRequest) Validate() error { 172 return validation.Errors{ 173 "EnrollmentID": validation.Validate(c.EnrollmentID, validation.Required), 174 "ChangeID": validation.Validate(c.ChangeID, validation.Required), 175 }.Filter() 176 } 177 178 // Validate validates UpdateChangeRequest 179 func (c UpdateChangeRequest) Validate() error { 180 return validation.Errors{ 181 "EnrollmentID": validation.Validate(c.EnrollmentID, validation.Required), 182 "ChangeID": validation.Validate(c.ChangeID, validation.Required), 183 "AllowedInputTypeParam": validation.Validate(c.AllowedInputTypeParam, validation.In( 184 AllowedInputTypeChangeManagementACK, 185 AllowedInputTypeLetsEncryptChallengesCompleted, 186 AllowedInputTypePostVerificationWarningsACK, 187 AllowedInputTypePreVerificationWarningsACK, 188 AllowedInputTypeThirdPartyCertAndTrustChain, 189 )), 190 "Certificate": validation.Validate(c.Certificate, validation.Required), 191 }.Filter() 192 } 193 194 // Validate validates AcknowledgementRequest 195 func (a AcknowledgementRequest) Validate() error { 196 return validation.Errors{ 197 "EnrollmentID": validation.Validate(a.EnrollmentID, validation.Required), 198 "ChangeID": validation.Validate(a.ChangeID, validation.Required), 199 "Acknowledgement": validation.Validate(a.Acknowledgement), 200 }.Filter() 201 } 202 203 // Validate validates Acknowledgement 204 func (a Acknowledgement) Validate() error { 205 return validation.Errors{ 206 "Acknowledgement": validation.Validate(a.Acknowledgement, validation.Required, validation.In(AcknowledgementAcknowledge, AcknowledgementDeny)), 207 }.Filter() 208 } 209 210 // Validate validates Certificate 211 func (c Certificate) Validate() error { 212 return validation.Errors{ 213 "Certificate": validation.Validate(c.Certificate, validation.Required), 214 }.Filter() 215 } 216 217 var ( 218 // ErrGetChangeStatus is returned when GetChangeStatus fails 219 ErrGetChangeStatus = errors.New("fetching change") 220 // ErrCancelChange is returned when CancelChange fails 221 ErrCancelChange = errors.New("canceling change") 222 // ErrUpdateChange is returned when UpdateChange fails 223 ErrUpdateChange = errors.New("updating change") 224 ) 225 226 func (c *cps) GetChangeStatus(ctx context.Context, params GetChangeStatusRequest) (*Change, error) { 227 if err := params.Validate(); err != nil { 228 return nil, fmt.Errorf("%s: %w: %s", ErrGetChangeStatus, ErrStructValidation, err) 229 } 230 231 var rval Change 232 233 logger := c.Log(ctx) 234 logger.Debug("GetChangeStatus") 235 236 uri, err := url.Parse(fmt.Sprintf( 237 "/cps/v2/enrollments/%d/changes/%d", 238 params.EnrollmentID, 239 params.ChangeID), 240 ) 241 if err != nil { 242 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetChangeStatus, err) 243 } 244 245 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil) 246 if err != nil { 247 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetChangeStatus, err) 248 } 249 req.Header.Set("Accept", "application/vnd.akamai.cps.change.v2+json") 250 251 resp, err := c.Exec(req, &rval) 252 if err != nil { 253 return nil, fmt.Errorf("%w: request failed: %s", ErrGetChangeStatus, err) 254 } 255 256 if resp.StatusCode != http.StatusOK { 257 return nil, fmt.Errorf("%s: %w", ErrGetChangeStatus, c.Error(resp)) 258 } 259 260 return &rval, nil 261 } 262 263 func (c *cps) CancelChange(ctx context.Context, params CancelChangeRequest) (*CancelChangeResponse, error) { 264 if err := params.Validate(); err != nil { 265 return nil, fmt.Errorf("%s: %w: %s", ErrCancelChange, ErrStructValidation, err) 266 } 267 268 var rval CancelChangeResponse 269 270 logger := c.Log(ctx) 271 logger.Debug("CancelChange") 272 273 uri, err := url.Parse(fmt.Sprintf( 274 "/cps/v2/enrollments/%d/changes/%d", 275 params.EnrollmentID, 276 params.ChangeID), 277 ) 278 if err != nil { 279 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrCancelChange, err) 280 } 281 282 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri.String(), nil) 283 if err != nil { 284 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCancelChange, err) 285 } 286 req.Header.Set("Accept", "application/vnd.akamai.cps.change-id.v1+json") 287 288 resp, err := c.Exec(req, &rval) 289 if err != nil { 290 return nil, fmt.Errorf("%w: request failed: %s", ErrCancelChange, err) 291 } 292 293 if resp.StatusCode != http.StatusOK { 294 return nil, fmt.Errorf("%s: %w", ErrCancelChange, c.Error(resp)) 295 } 296 297 return &rval, nil 298 } 299 300 func (c *cps) UpdateChange(ctx context.Context, params UpdateChangeRequest) (*UpdateChangeResponse, error) { 301 if err := params.Validate(); err != nil { 302 return nil, fmt.Errorf("%s: %w: %s", ErrUpdateChange, ErrStructValidation, err) 303 } 304 305 var rval UpdateChangeResponse 306 307 logger := c.Log(ctx) 308 logger.Debug("UpdateChangeLetsEncryptChallenges") 309 310 uri, err := url.Parse(fmt.Sprintf( 311 "/cps/v2/enrollments/%d/changes/%d/input/update/%s", 312 params.EnrollmentID, 313 params.ChangeID, 314 params.AllowedInputTypeParam), 315 ) 316 if err != nil { 317 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrUpdateChange, err) 318 } 319 320 req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil) 321 if err != nil { 322 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateChange, err) 323 } 324 req.Header.Set("Accept", "application/vnd.akamai.cps.change-id.v1+json") 325 req.Header.Set("Content-Type", AllowedInputContentTypeHeader[params.AllowedInputTypeParam]) 326 327 resp, err := c.Exec(req, &rval) 328 if err != nil { 329 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateChange, err) 330 } 331 332 if resp.StatusCode != http.StatusOK { 333 return nil, fmt.Errorf("%s: %w", ErrUpdateChange, c.Error(resp)) 334 } 335 336 return &rval, nil 337 }