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  }