github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/hapi/edgehostname.go (about) 1 package hapi 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "net/http" 11 "strings" 12 13 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegriderr" 14 15 validation "github.com/go-ozzo/ozzo-validation/v4" 16 ) 17 18 type ( 19 // EdgeHostnames contains operations available on Edge Hostname resource. 20 EdgeHostnames interface { 21 // DeleteEdgeHostname allows deleting a specific edge hostname. 22 // You must have an Admin or Technical role in order to delete an edge hostname. 23 // You can delete any hostname that’s not currently part of an active Property Manager configuration. 24 // 25 // See: https://techdocs.akamai.com/edge-hostnames/reference/delete-edgehostname 26 DeleteEdgeHostname(context.Context, DeleteEdgeHostnameRequest) (*DeleteEdgeHostnameResponse, error) 27 28 // GetEdgeHostname gets a specific edge hostname's details including its product ID, IP version behavior, 29 // and China CDN or Edge IP Binding status. 30 // 31 // See: https://techdocs.akamai.com/edge-hostnames/reference/get-edgehostnameid 32 GetEdgeHostname(context.Context, int) (*GetEdgeHostnameResponse, error) 33 34 // UpdateEdgeHostname allows update ttl (path = "/ttl") or IpVersionBehaviour (path = "/ipVersionBehavior") 35 // 36 // See: https://techdocs.akamai.com/edge-hostnames/reference/patch-edgehostnames 37 UpdateEdgeHostname(context.Context, UpdateEdgeHostnameRequest) (*UpdateEdgeHostnameResponse, error) 38 } 39 40 // DeleteEdgeHostnameRequest is used to delete edge hostname 41 DeleteEdgeHostnameRequest struct { 42 DNSZone string 43 RecordName string 44 StatusUpdateEmail []string 45 Comments string 46 } 47 48 // DeleteEdgeHostnameResponse is a response from deleting edge hostname 49 DeleteEdgeHostnameResponse struct { 50 Action string `json:"action"` 51 ChangeID int `json:"changeId"` 52 Comments string `json:"comments"` 53 Status string `json:"status"` 54 StatusMessage string `json:"statusMessage"` 55 StatusUpdateDate string `json:"statusUpdateDate"` 56 StatusUpdateEmail string `json:"statusUpdateEmail"` 57 SubmitDate string `json:"submitDate"` 58 Submitter string `json:"submitter"` 59 SubmitterEmail string `json:"submitterEmail"` 60 EdgeHostnames []EdgeHostname `json:"edgeHostnames"` 61 } 62 63 // UpdateEdgeHostnameRequest is a request used to update edge hostname 64 UpdateEdgeHostnameRequest struct { 65 DNSZone string 66 RecordName string 67 StatusUpdateEmail []string 68 Comments string 69 Body []UpdateEdgeHostnameRequestBody 70 } 71 72 // UpdateEdgeHostnameRequestBody is a request's body used to update edge hostname 73 UpdateEdgeHostnameRequestBody struct { 74 Op string `json:"op"` 75 Path string `json:"path"` 76 Value string `json:"value"` 77 } 78 79 // UpdateEdgeHostnameResponse is a response from deleting edge hostname 80 UpdateEdgeHostnameResponse struct { 81 Action string `json:"action,omitempty"` 82 ChangeID int `json:"changeId,omitempty"` 83 Comments string `json:"comments,omitempty"` 84 Status string `json:"status,omitempty"` 85 StatusMessage string `json:"statusMessage,omitempty"` 86 StatusUpdateDate string `json:"statusUpdateDate,omitempty"` 87 StatusUpdateEmail string `json:"statusUpdateEmail,omitempty"` 88 SubmitDate string `json:"submitDate,omitempty"` 89 Submitter string `json:"submitter,omitempty"` 90 SubmitterEmail string `json:"submitterEmail,omitempty"` 91 EdgeHostnames []EdgeHostname `json:"edgeHostnames,omitempty"` 92 } 93 94 // EdgeHostname represents edge hostname part of DeleteEdgeHostnameResponse and UpdateEdgeHostnameResponse 95 EdgeHostname struct { 96 EdgeHostnameID int `json:"edgeHostnameId,omitempty"` 97 RecordName string `json:"recordName"` 98 DNSZone string `json:"dnsZone"` 99 SecurityType string `json:"securityType"` 100 UseDefaultTTL bool `json:"useDefaultTtl"` 101 UseDefaultMap bool `json:"useDefaultMap"` 102 TTL int `json:"ttl"` 103 Map string `json:"map,omitempty"` 104 SlotNumber int `json:"slotNumber,omitempty"` 105 IPVersionBehavior string `json:"ipVersionBehavior,omitempty"` 106 Comments string `json:"comments,omitempty"` 107 ChinaCDN ChinaCDN `json:"chinaCdn,omitempty"` 108 CustomTarget string `json:"customTarget,omitempty"` 109 IsEdgeIPBindingEnabled bool `json:"isEdgeIPBindingEnabled,omitempty"` 110 MapAlias string `json:"mapAlias,omitempty"` 111 ProductId string `json:"productId,omitempty"` 112 SerialNumber int `json:"serialNumber,omitempty"` 113 UseCases []UseCase `json:"useCases,omitempty"` 114 } 115 116 // ChinaCDN represents China CDN settings of EdgeHostname 117 ChinaCDN struct { 118 IsChinaCDN bool `json:"isChinaCdn,omitempty"` 119 CustomChinaCDNMap string `json:"customChinaCdnMap,omitempty"` 120 } 121 122 // UseCase represents useCase attribute in EdgeHostname 123 UseCase struct { 124 Type string `json:"type,omitempty"` 125 Option string `json:"option"` 126 UseCase string `json:"useCase"` 127 } 128 129 // GetEdgeHostnameResponse represents edge hostname 130 GetEdgeHostnameResponse struct { 131 EdgeHostnameID int `json:"edgeHostnameId"` 132 RecordName string `json:"recordName"` 133 DNSZone string `json:"dnsZone"` 134 SecurityType string `json:"securityType"` 135 UseDefaultTTL bool `json:"useDefaultTtl"` 136 UseDefaultMap bool `json:"useDefaultMap"` 137 IPVersionBehavior string `json:"ipVersionBehavior"` 138 TTL int `json:"ttl"` 139 Map string `json:"map,omitempty"` 140 SlotNumber int `json:"slotNumber,omitempty"` 141 Comments string `json:"comments"` 142 SerialNumber int `json:"serialNumber,omitempty"` 143 CustomTarget string `json:"customTarget,omitempty"` 144 ChinaCdn ChinaCDN `json:"chinaCdn,omitempty"` 145 IsEdgeIPBindingEnabled bool `json:"isEdgeIPBindingEnabled,omitempty"` 146 } 147 ) 148 149 // Validate validates DeleteEdgeHostnameRequest 150 func (r DeleteEdgeHostnameRequest) Validate() error { 151 return validation.Errors{ 152 "DNSZone": validation.Validate(r.DNSZone, validation.Required), 153 "RecordName": validation.Validate(r.RecordName, validation.Required), 154 }.Filter() 155 } 156 157 // Validate validates DeleteEdgeHostnameRequest 158 func (r UpdateEdgeHostnameRequest) Validate() error { 159 errs := validation.Errors{ 160 "DNSZone": validation.Validate(r.DNSZone, validation.Required), 161 "RecordName": validation.Validate(r.RecordName, validation.Required), 162 "Body": validation.Validate(r.Body), 163 } 164 return edgegriderr.ParseValidationErrors(errs) 165 } 166 167 // Validate validates UpdateEdgeHostnameRequestBody 168 func (b UpdateEdgeHostnameRequestBody) Validate() error { 169 return validation.Errors{ 170 "Path": validation.Validate(b.Path, validation.Required, validation.In("/ttl", "/ipVersionBehavior").Error(fmt.Sprintf("value '%s' is invalid. Must be one of: '/ttl' or '/ipVersionBehavior'", b.Path))), 171 "Op": validation.Validate(b.Op, validation.Required, validation.In("replace").Error(fmt.Sprintf("value '%s' is invalid. Must use 'replace'", b.Op))), 172 "Value": validation.Validate(b.Value, validation.Required), 173 }.Filter() 174 } 175 176 var ( 177 // ErrDeleteEdgeHostname represents error when deleting edge hostname fails 178 ErrDeleteEdgeHostname = errors.New("delete edge hostname") 179 // ErrGetEdgeHostname represents error when getting edge hostname fails 180 ErrGetEdgeHostname = errors.New("get edge hostname") 181 // ErrUpdateEdgeHostname represents error when updating edge hostname fails 182 ErrUpdateEdgeHostname = errors.New("update edge hostname") 183 ) 184 185 func (h *hapi) DeleteEdgeHostname(ctx context.Context, params DeleteEdgeHostnameRequest) (*DeleteEdgeHostnameResponse, error) { 186 if err := params.Validate(); err != nil { 187 return nil, fmt.Errorf("%s: %w: %s", ErrDeleteEdgeHostname, ErrStructValidation, err) 188 } 189 190 logger := h.Log(ctx) 191 logger.Debug("DeleteEdgeHostname") 192 193 uri := fmt.Sprintf( 194 "/hapi/v1/dns-zones/%s/edge-hostnames/%s", 195 params.DNSZone, 196 params.RecordName, 197 ) 198 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil) 199 if err != nil { 200 return nil, fmt.Errorf("%w: failed to create request: %s", ErrDeleteEdgeHostname, err) 201 } 202 203 q := req.URL.Query() 204 if len(params.StatusUpdateEmail) > 0 { 205 emails := strings.Join(params.StatusUpdateEmail, ",") 206 q.Add("statusUpdateEmail", emails) 207 } 208 if params.Comments != "" { 209 q.Add("comments", params.Comments) 210 } 211 req.URL.RawQuery = q.Encode() 212 213 var rval DeleteEdgeHostnameResponse 214 215 resp, err := h.Exec(req, &rval) 216 if err != nil { 217 return nil, fmt.Errorf("%w: request failed: %s", ErrDeleteEdgeHostname, err) 218 } 219 220 if resp.StatusCode != http.StatusAccepted { 221 return nil, fmt.Errorf("%s: %w", ErrDeleteEdgeHostname, h.Error(resp)) 222 } 223 224 return &rval, nil 225 } 226 227 func (h *hapi) GetEdgeHostname(ctx context.Context, edgeHostnameID int) (*GetEdgeHostnameResponse, error) { 228 logger := h.Log(ctx) 229 logger.Debug("GetEdgeHostname") 230 231 uri := fmt.Sprintf("/hapi/v1/edge-hostnames/%d", edgeHostnameID) 232 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) 233 if err != nil { 234 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetEdgeHostname, err) 235 } 236 237 var rval GetEdgeHostnameResponse 238 239 resp, err := h.Exec(req, &rval) 240 if err != nil { 241 return nil, fmt.Errorf("%w: request failed: %s", ErrGetEdgeHostname, err) 242 } 243 244 if resp.StatusCode != http.StatusOK { 245 return nil, fmt.Errorf("%s: %w", ErrGetEdgeHostname, h.Error(resp)) 246 } 247 248 return &rval, nil 249 } 250 251 func (h *hapi) UpdateEdgeHostname(ctx context.Context, request UpdateEdgeHostnameRequest) (*UpdateEdgeHostnameResponse, error) { 252 if err := request.Validate(); err != nil { 253 return nil, fmt.Errorf("%w: %s: %s", ErrUpdateEdgeHostname, ErrStructValidation, err) 254 } 255 256 logger := h.Log(ctx) 257 logger.Debug("UpdateEdgeHostname") 258 259 uri := fmt.Sprintf("/hapi/v1/dns-zones/%s/edge-hostnames/%s", request.DNSZone, request.RecordName) 260 261 body, err := buildBody(request.Body) 262 if err != nil { 263 return nil, fmt.Errorf("%w: failed to create request body", err) 264 } 265 266 req, err := http.NewRequestWithContext(ctx, http.MethodPatch, uri, body) 267 if err != nil { 268 return nil, err 269 } 270 271 q := req.URL.Query() 272 if len(request.StatusUpdateEmail) > 0 { 273 emails := strings.Join(request.StatusUpdateEmail, ",") 274 q.Add("statusUpdateEmail", emails) 275 } 276 if request.Comments != "" { 277 q.Add("comments", request.Comments) 278 } 279 req.URL.RawQuery = q.Encode() 280 281 req.Header.Set("Content-Type", "application/json-patch+json") 282 283 var result UpdateEdgeHostnameResponse 284 285 resp, err := h.Exec(req, &result) 286 if err != nil { 287 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateEdgeHostname, err) 288 } 289 290 if resp.StatusCode != http.StatusAccepted { 291 return nil, fmt.Errorf("%w: %s", ErrUpdateEdgeHostname, h.Error(resp)) 292 } 293 294 return &result, nil 295 } 296 297 func buildBody(body []UpdateEdgeHostnameRequestBody) (io.Reader, error) { 298 reqBody, err := json.Marshal(body) 299 if err != nil { 300 return nil, err 301 } 302 return bytes.NewBuffer(reqBody), nil 303 }