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  }