github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.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/v2/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 // See: https://developer.akamai.com/api/core_features/edge_hostnames/v1.html#edgehostname 21 EdgeHostnames interface { 22 // DeleteEdgeHostname allows deleting a specific edge hostname. 23 // You must have an Admin or Technical role in order to delete an edge hostname. 24 // You can delete any hostname that’s not currently part of an active Property Manager configuration. 25 // 26 // See: https://developer.akamai.com/api/core_features/edge_hostnames/v1.html#deleteedgehostnamebyname 27 DeleteEdgeHostname(context.Context, DeleteEdgeHostnameRequest) (*DeleteEdgeHostnameResponse, error) 28 29 // GetEdgeHostname gets a specific edge hostname's details including its product ID, IP version behavior, 30 // and China CDN or Edge IP Binding status. 31 // 32 // See: https://techdocs.akamai.com/edge-hostnames/reference/get-edgehostnameid 33 GetEdgeHostname(context.Context, int) (*GetEdgeHostnameResponse, error) 34 35 // UpdateEdgeHostname allows update ttl (path = "/ttl") or IpVersionBehaviour (path = "/ipVersionBehavior") 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 ProductID string `json:"productId"` 139 TTL int `json:"ttl"` 140 Map string `json:"map,omitempty"` 141 SlotNumber int `json:"slotNumber,omitempty"` 142 Comments string `json:"comments"` 143 SerialNumber int `json:"serialNumber,omitempty"` 144 CustomTarget string `json:"customTarget,omitempty"` 145 ChinaCdn ChinaCDN `json:"chinaCdn,omitempty"` 146 IsEdgeIPBindingEnabled bool `json:"isEdgeIPBindingEnabled,omitempty"` 147 } 148 ) 149 150 // Validate validates DeleteEdgeHostnameRequest 151 func (r DeleteEdgeHostnameRequest) Validate() error { 152 return validation.Errors{ 153 "DNSZone": validation.Validate(r.DNSZone, validation.Required), 154 "RecordName": validation.Validate(r.RecordName, validation.Required), 155 }.Filter() 156 } 157 158 // Validate validates DeleteEdgeHostnameRequest 159 func (r UpdateEdgeHostnameRequest) Validate() error { 160 errs := validation.Errors{ 161 "DNSZone": validation.Validate(r.DNSZone, validation.Required), 162 "RecordName": validation.Validate(r.RecordName, validation.Required), 163 "Body": validation.Validate(r.Body), 164 } 165 return edgegriderr.ParseValidationErrors(errs) 166 } 167 168 // Validate validates UpdateEdgeHostnameRequestBody 169 func (b UpdateEdgeHostnameRequestBody) Validate() error { 170 return validation.Errors{ 171 "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))), 172 "Op": validation.Validate(b.Op, validation.Required, validation.In("replace").Error(fmt.Sprintf("value '%s' is invalid. Must use 'replace'", b.Op))), 173 "Value": validation.Validate(b.Value, validation.Required), 174 }.Filter() 175 } 176 177 var ( 178 // ErrDeleteEdgeHostname represents error when deleting edge hostname fails 179 ErrDeleteEdgeHostname = errors.New("delete edge hostname") 180 // ErrGetEdgeHostname represents error when getting edge hostname fails 181 ErrGetEdgeHostname = errors.New("get edge hostname") 182 // ErrUpdateEdgeHostname represents error when updating edge hostname fails 183 ErrUpdateEdgeHostname = errors.New("update edge hostname") 184 ) 185 186 func (h *hapi) DeleteEdgeHostname(ctx context.Context, params DeleteEdgeHostnameRequest) (*DeleteEdgeHostnameResponse, error) { 187 if err := params.Validate(); err != nil { 188 return nil, fmt.Errorf("%s: %w: %s", ErrDeleteEdgeHostname, ErrStructValidation, err) 189 } 190 191 logger := h.Log(ctx) 192 logger.Debug("DeleteEdgeHostname") 193 194 uri := fmt.Sprintf( 195 "/hapi/v1/dns-zones/%s/edge-hostnames/%s", 196 params.DNSZone, 197 params.RecordName, 198 ) 199 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil) 200 if err != nil { 201 return nil, fmt.Errorf("%w: failed to create request: %s", ErrDeleteEdgeHostname, err) 202 } 203 204 q := req.URL.Query() 205 if len(params.StatusUpdateEmail) > 0 { 206 emails := strings.Join(params.StatusUpdateEmail, ",") 207 q.Add("statusUpdateEmail", emails) 208 } 209 if params.Comments != "" { 210 q.Add("comments", params.Comments) 211 } 212 req.URL.RawQuery = q.Encode() 213 214 var rval DeleteEdgeHostnameResponse 215 216 resp, err := h.Exec(req, &rval) 217 if err != nil { 218 return nil, fmt.Errorf("%w: request failed: %s", ErrDeleteEdgeHostname, err) 219 } 220 221 if resp.StatusCode != http.StatusAccepted { 222 return nil, fmt.Errorf("%s: %w", ErrDeleteEdgeHostname, h.Error(resp)) 223 } 224 225 return &rval, nil 226 } 227 228 func (h *hapi) GetEdgeHostname(ctx context.Context, edgeHostnameID int) (*GetEdgeHostnameResponse, error) { 229 logger := h.Log(ctx) 230 logger.Debug("GetEdgeHostname") 231 232 uri := fmt.Sprintf("/hapi/v1/edge-hostnames/%d", edgeHostnameID) 233 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) 234 if err != nil { 235 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetEdgeHostname, err) 236 } 237 238 var rval GetEdgeHostnameResponse 239 240 resp, err := h.Exec(req, &rval) 241 if err != nil { 242 return nil, fmt.Errorf("%w: request failed: %s", ErrGetEdgeHostname, err) 243 } 244 245 if resp.StatusCode != http.StatusOK { 246 return nil, fmt.Errorf("%s: %w", ErrGetEdgeHostname, h.Error(resp)) 247 } 248 249 return &rval, nil 250 } 251 252 func (h *hapi) UpdateEdgeHostname(ctx context.Context, request UpdateEdgeHostnameRequest) (*UpdateEdgeHostnameResponse, error) { 253 if err := request.Validate(); err != nil { 254 return nil, fmt.Errorf("%w: %s: %s", ErrUpdateEdgeHostname, ErrStructValidation, err) 255 } 256 257 logger := h.Log(ctx) 258 logger.Debug("UpdateEdgeHostname") 259 260 uri := fmt.Sprintf("/hapi/v1/dns-zones/%s/edge-hostnames/%s", request.DNSZone, request.RecordName) 261 262 body, err := buildBody(request.Body) 263 if err != nil { 264 return nil, fmt.Errorf("%w: failed to create request body", err) 265 } 266 267 req, err := http.NewRequestWithContext(ctx, http.MethodPatch, uri, body) 268 if err != nil { 269 return nil, err 270 } 271 272 q := req.URL.Query() 273 if len(request.StatusUpdateEmail) > 0 { 274 emails := strings.Join(request.StatusUpdateEmail, ",") 275 q.Add("statusUpdateEmail", emails) 276 } 277 if request.Comments != "" { 278 q.Add("comments", request.Comments) 279 } 280 req.URL.RawQuery = q.Encode() 281 282 req.Header.Set("Content-Type", "application/json-patch+json") 283 284 var result UpdateEdgeHostnameResponse 285 286 resp, err := h.Exec(req, &result) 287 if err != nil { 288 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateEdgeHostname, err) 289 } 290 291 if resp.StatusCode != http.StatusAccepted { 292 return nil, fmt.Errorf("%w: %s", ErrUpdateEdgeHostname, h.Error(resp)) 293 } 294 295 return &result, nil 296 } 297 298 func buildBody(body []UpdateEdgeHostnameRequestBody) (io.Reader, error) { 299 reqBody, err := json.Marshal(body) 300 if err != nil { 301 return nil, err 302 } 303 return bytes.NewBuffer(reqBody), nil 304 }