github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/gtm/domain.go (about)

     1  package gtm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"reflect"
     9  	"strings"
    10  	"unicode"
    11  )
    12  
    13  // Domains contains operations available on a Domain resource.
    14  type Domains interface {
    15  	// NullFieldMap retrieves map of null fields.
    16  	NullFieldMap(context.Context, *Domain) (*NullFieldMapStruct, error)
    17  	// GetDomainStatus retrieves current status for the given domain name.
    18  	//
    19  	// See: https://techdocs.akamai.com/gtm/reference/get-status-current
    20  	GetDomainStatus(context.Context, string) (*ResponseStatus, error)
    21  	// ListDomains retrieves all Domains.
    22  	//
    23  	// See: https://techdocs.akamai.com/gtm/reference/get-domains
    24  	ListDomains(context.Context) ([]*DomainItem, error)
    25  	// GetDomain retrieves a Domain with the given domain name.
    26  	//
    27  	// See: https://techdocs.akamai.com/gtm/reference/get-domain
    28  	GetDomain(context.Context, string) (*Domain, error)
    29  	// CreateDomain creates domain.
    30  	//
    31  	// See: https://techdocs.akamai.com/gtm/reference/post-domain
    32  	CreateDomain(context.Context, *Domain, map[string]string) (*DomainResponse, error)
    33  	// DeleteDomain is a method applied to a domain object resulting in removal.
    34  	//
    35  	// See: ** Not Supported by API **
    36  	DeleteDomain(context.Context, *Domain) (*ResponseStatus, error)
    37  	// UpdateDomain is a method applied to a domain object resulting in an update.
    38  	//
    39  	// See: https://techdocs.akamai.com/gtm/reference/put-domain
    40  	UpdateDomain(context.Context, *Domain, map[string]string) (*ResponseStatus, error)
    41  }
    42  
    43  // The Domain data structure represents a GTM domain
    44  type Domain struct {
    45  	Name                         string          `json:"name"`
    46  	Type                         string          `json:"type"`
    47  	ASMaps                       []*ASMap        `json:"asMaps,omitempty"`
    48  	Resources                    []*Resource     `json:"resources,omitempty"`
    49  	DefaultUnreachableThreshold  float32         `json:"defaultUnreachableThreshold,omitempty"`
    50  	EmailNotificationList        []string        `json:"emailNotificationList,omitempty"`
    51  	MinPingableRegionFraction    float32         `json:"minPingableRegionFraction,omitempty"`
    52  	DefaultTimeoutPenalty        int             `json:"defaultTimeoutPenalty,omitempty"`
    53  	Datacenters                  []*Datacenter   `json:"datacenters,omitempty"`
    54  	ServermonitorLivenessCount   int             `json:"servermonitorLivenessCount,omitempty"`
    55  	RoundRobinPrefix             string          `json:"roundRobinPrefix,omitempty"`
    56  	ServermonitorLoadCount       int             `json:"servermonitorLoadCount,omitempty"`
    57  	PingInterval                 int             `json:"pingInterval,omitempty"`
    58  	MaxTTL                       int64           `json:"maxTTL,omitempty"`
    59  	LoadImbalancePercentage      float64         `json:"loadImbalancePercentage,omitempty"`
    60  	DefaultHealthMax             float64         `json:"defaultHealthMax,omitempty"`
    61  	LastModified                 string          `json:"lastModified,omitempty"`
    62  	Status                       *ResponseStatus `json:"status,omitempty"`
    63  	MapUpdateInterval            int             `json:"mapUpdateInterval,omitempty"`
    64  	MaxProperties                int             `json:"maxProperties,omitempty"`
    65  	MaxResources                 int             `json:"maxResources,omitempty"`
    66  	DefaultSSLClientPrivateKey   string          `json:"defaultSslClientPrivateKey,omitempty"`
    67  	DefaultErrorPenalty          int             `json:"defaultErrorPenalty,omitempty"`
    68  	Links                        []*Link         `json:"links,omitempty"`
    69  	Properties                   []*Property     `json:"properties,omitempty"`
    70  	MaxTestTimeout               float64         `json:"maxTestTimeout,omitempty"`
    71  	CNameCoalescingEnabled       bool            `json:"cnameCoalescingEnabled"`
    72  	DefaultHealthMultiplier      float64         `json:"defaultHealthMultiplier,omitempty"`
    73  	ServermonitorPool            string          `json:"servermonitorPool,omitempty"`
    74  	LoadFeedback                 bool            `json:"loadFeedback"`
    75  	MinTTL                       int64           `json:"minTTL,omitempty"`
    76  	GeographicMaps               []*GeoMap       `json:"geographicMaps,omitempty"`
    77  	CIDRMaps                     []*CIDRMap      `json:"cidrMaps,omitempty"`
    78  	DefaultMaxUnreachablePenalty int             `json:"defaultMaxUnreachablePenalty"`
    79  	DefaultHealthThreshold       float64         `json:"defaultHealthThreshold,omitempty"`
    80  	LastModifiedBy               string          `json:"lastModifiedBy,omitempty"`
    81  	ModificationComments         string          `json:"modificationComments,omitempty"`
    82  	MinTestInterval              int             `json:"minTestInterval,omitempty"`
    83  	PingPacketSize               int             `json:"pingPacketSize,omitempty"`
    84  	DefaultSSLClientCertificate  string          `json:"defaultSslClientCertificate,omitempty"`
    85  	EndUserMappingEnabled        bool            `json:"endUserMappingEnabled"`
    86  	SignAndServe                 bool            `json:"signAndServe"`
    87  	SignAndServeAlgorithm        *string         `json:"signAndServeAlgorithm"`
    88  }
    89  
    90  // DomainsList contains a list of domain items
    91  type DomainsList struct {
    92  	DomainItems []*DomainItem `json:"items"`
    93  }
    94  
    95  // DomainItem is a DomainsList item
    96  type DomainItem struct {
    97  	AcgID                 string  `json:"acgId"`
    98  	LastModified          string  `json:"lastModified"`
    99  	Links                 []*Link `json:"links"`
   100  	Name                  string  `json:"name"`
   101  	Status                string  `json:"status"`
   102  	LastModifiedBy        string  `json:"lastModifiedBy"`
   103  	ChangeID              string  `json:"changeId"`
   104  	ActivationState       string  `json:"activationState"`
   105  	ModificationComments  string  `json:"modificationComments"`
   106  	SignAndServe          bool    `json:"signAndServe"`
   107  	SignAndServeAlgorithm string  `json:"signAndServeAlgorithm"`
   108  	DeleteRequestID       string  `json:"deleteRequestId"`
   109  }
   110  
   111  // Validate validates Domain
   112  func (d *Domain) Validate() error {
   113  	if len(d.Name) < 1 {
   114  		return fmt.Errorf("Domain is missing Name")
   115  	}
   116  	if len(d.Type) < 1 {
   117  		return fmt.Errorf("Domain is missing Type")
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func (g *gtm) GetDomainStatus(ctx context.Context, domainName string) (*ResponseStatus, error) {
   124  	logger := g.Log(ctx)
   125  	logger.Debug("GetDomainStatus")
   126  
   127  	getURL := fmt.Sprintf("/config-gtm/v1/domains/%s/status/current", domainName)
   128  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   129  	if err != nil {
   130  		return nil, fmt.Errorf("failed to create GetDomain request: %w", err)
   131  	}
   132  	setVersionHeader(req, schemaVersion)
   133  
   134  	var result ResponseStatus
   135  	resp, err := g.Exec(req, &result)
   136  	if err != nil {
   137  		return nil, fmt.Errorf("GetDomain request failed: %w", err)
   138  	}
   139  
   140  	if resp.StatusCode != http.StatusOK {
   141  		return nil, g.Error(resp)
   142  	}
   143  
   144  	return &result, nil
   145  }
   146  
   147  func (g *gtm) ListDomains(ctx context.Context) ([]*DomainItem, error) {
   148  	logger := g.Log(ctx)
   149  	logger.Debug("ListDomains")
   150  
   151  	getURL := fmt.Sprintf("/config-gtm/v1/domains")
   152  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   153  	if err != nil {
   154  		return nil, fmt.Errorf("failed to create ListDomains request: %w", err)
   155  	}
   156  	setVersionHeader(req, schemaVersion)
   157  
   158  	var result DomainsList
   159  	resp, err := g.Exec(req, &result)
   160  	if err != nil {
   161  		return nil, fmt.Errorf("ListDomains request failed: %w", err)
   162  	}
   163  
   164  	if resp.StatusCode != http.StatusOK {
   165  		return nil, g.Error(resp)
   166  	}
   167  
   168  	return result.DomainItems, nil
   169  }
   170  
   171  func (g *gtm) GetDomain(ctx context.Context, domainName string) (*Domain, error) {
   172  	logger := g.Log(ctx)
   173  	logger.Debug("GetDomain")
   174  
   175  	getURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domainName)
   176  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   177  	if err != nil {
   178  		return nil, fmt.Errorf("failed to create GetDomain request: %w", err)
   179  	}
   180  	setVersionHeader(req, schemaVersion)
   181  
   182  	var result Domain
   183  	resp, err := g.Exec(req, &result)
   184  	if err != nil {
   185  		return nil, fmt.Errorf("GetDomain request failed: %w", err)
   186  	}
   187  
   188  	if resp.StatusCode != http.StatusOK {
   189  		return nil, g.Error(resp)
   190  	}
   191  
   192  	return &result, nil
   193  }
   194  
   195  // save method; Create or Update
   196  func (d *Domain) save(_ context.Context, g *gtm, queryArgs map[string]string, req *http.Request) (*DomainResponse, error) {
   197  	// set schema version
   198  	setVersionHeader(req, schemaVersion)
   199  
   200  	// Look for optional args
   201  	if len(queryArgs) > 0 {
   202  		q := req.URL.Query()
   203  		if val, ok := queryArgs["contractId"]; ok {
   204  			q.Add("contractId", strings.TrimPrefix(val, "ctr_"))
   205  		}
   206  		if val, ok := queryArgs["gid"]; ok {
   207  			q.Add("gid", strings.TrimPrefix(val, "grp_"))
   208  		}
   209  		req.URL.RawQuery = q.Encode()
   210  	}
   211  
   212  	var result DomainResponse
   213  	resp, err := g.Exec(req, &result, d)
   214  	if err != nil {
   215  		return nil, fmt.Errorf("domain request failed: %w", err)
   216  	}
   217  
   218  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   219  		return nil, g.Error(resp)
   220  	}
   221  
   222  	return &result, nil
   223  }
   224  
   225  func (g *gtm) CreateDomain(ctx context.Context, domain *Domain, queryArgs map[string]string) (*DomainResponse, error) {
   226  	logger := g.Log(ctx)
   227  	logger.Debug("CreateDomain")
   228  
   229  	if err := domain.Validate(); err != nil {
   230  		return nil, fmt.Errorf("CreateDomain validation failed. %w", err)
   231  	}
   232  
   233  	postURL := fmt.Sprintf("/config-gtm/v1/domains/")
   234  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, nil)
   235  	if err != nil {
   236  		return nil, fmt.Errorf("failed to create CreateDomain request: %w", err)
   237  	}
   238  
   239  	return domain.save(ctx, g, queryArgs, req)
   240  }
   241  
   242  func (g *gtm) UpdateDomain(ctx context.Context, domain *Domain, queryArgs map[string]string) (*ResponseStatus, error) {
   243  	logger := g.Log(ctx)
   244  	logger.Debug("UpdateDomain")
   245  
   246  	if err := domain.Validate(); err != nil {
   247  		return nil, fmt.Errorf("UpdateDomain validation failed. %w", err)
   248  	}
   249  
   250  	putURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name)
   251  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
   252  	if err != nil {
   253  		return nil, fmt.Errorf("failed to create UpdateDomain request: %w", err)
   254  	}
   255  
   256  	stat, err := domain.save(ctx, g, queryArgs, req)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  	return stat.Status, err
   261  }
   262  
   263  func (g *gtm) DeleteDomain(ctx context.Context, domain *Domain) (*ResponseStatus, error) {
   264  	logger := g.Log(ctx)
   265  	logger.Debug("DeleteDomain")
   266  
   267  	delURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name)
   268  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, delURL, nil)
   269  	if err != nil {
   270  		return nil, fmt.Errorf("failed to create DeleteDomain request: %w", err)
   271  	}
   272  	setVersionHeader(req, schemaVersion)
   273  
   274  	var result ResponseBody
   275  	resp, err := g.Exec(req, &result)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("DeleteDomain request failed: %w", err)
   278  	}
   279  
   280  	if resp.StatusCode != http.StatusOK {
   281  		return nil, g.Error(resp)
   282  	}
   283  
   284  	return result.Status, nil
   285  }
   286  
   287  // NullPerObjectAttributeStruct represents core and child null object attributes
   288  type NullPerObjectAttributeStruct struct {
   289  	CoreObjectFields  map[string]string
   290  	ChildObjectFields map[string]interface{} // NullObjectAttributeStruct
   291  }
   292  
   293  // NullFieldMapStruct returned null Objects structure
   294  type NullFieldMapStruct struct {
   295  	Domain      NullPerObjectAttributeStruct            // entry is domain
   296  	Properties  map[string]NullPerObjectAttributeStruct // entries are properties
   297  	Datacenters map[string]NullPerObjectAttributeStruct // entries are datacenters
   298  	Resources   map[string]NullPerObjectAttributeStruct // entries are resources
   299  	CidrMaps    map[string]NullPerObjectAttributeStruct // entries are cidrmaps
   300  	GeoMaps     map[string]NullPerObjectAttributeStruct // entries are geomaps
   301  	AsMaps      map[string]NullPerObjectAttributeStruct // entries are asmaps
   302  }
   303  
   304  // ObjectMap represents ObjectMap datatype
   305  type ObjectMap map[string]interface{}
   306  
   307  func (g *gtm) NullFieldMap(ctx context.Context, domain *Domain) (*NullFieldMapStruct, error) {
   308  	logger := g.Log(ctx)
   309  	logger.Debug("NullFieldMap")
   310  
   311  	if err := domain.Validate(); err != nil {
   312  		return nil, fmt.Errorf("domain validation failed. %w", err)
   313  	}
   314  
   315  	var nullFieldMap = &NullFieldMapStruct{}
   316  	var domFields = NullPerObjectAttributeStruct{}
   317  	domainMap := make(map[string]string)
   318  	var objMap ObjectMap
   319  
   320  	getURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name)
   321  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   322  	if err != nil {
   323  		return nil, fmt.Errorf("failed to create GetDomain request: %w", err)
   324  	}
   325  	setVersionHeader(req, schemaVersion)
   326  	resp, err := g.Exec(req, &objMap)
   327  	if err != nil {
   328  		return nil, fmt.Errorf("GetDomain request failed: %w", err)
   329  	}
   330  
   331  	if resp.StatusCode != http.StatusOK {
   332  		return nil, g.Error(resp)
   333  	}
   334  
   335  	for i, d := range objMap {
   336  		objVal := fmt.Sprint(d)
   337  		if fmt.Sprintf("%T", d) == "<nil>" {
   338  			if objVal == "<nil>" {
   339  				domainMap[makeFirstCharUpperCase(i)] = ""
   340  			}
   341  			continue
   342  		}
   343  		list, ok := d.([]interface{})
   344  		if !ok {
   345  			continue
   346  		}
   347  
   348  		switch i {
   349  		case "properties":
   350  			nullFieldMap.Properties = processObjectList(list)
   351  		case "datacenters":
   352  			nullFieldMap.Datacenters = processObjectList(list)
   353  		case "resources":
   354  			nullFieldMap.Resources = processObjectList(list)
   355  		case "cidrMaps":
   356  			nullFieldMap.CidrMaps = processObjectList(list)
   357  		case "geographicMaps":
   358  			nullFieldMap.GeoMaps = processObjectList(list)
   359  		case "asMaps":
   360  			nullFieldMap.AsMaps = processObjectList(list)
   361  		}
   362  	}
   363  
   364  	domFields.CoreObjectFields = domainMap
   365  	nullFieldMap.Domain = domFields
   366  
   367  	return nullFieldMap, nil
   368  
   369  }
   370  
   371  func makeFirstCharUpperCase(origString string) string {
   372  	a := []rune(origString)
   373  	a[0] = unicode.ToUpper(a[0])
   374  	// hack
   375  	if origString == "cname" {
   376  		a[1] = unicode.ToUpper(a[1])
   377  	}
   378  	return string(a)
   379  }
   380  
   381  func processObjectList(objectList []interface{}) map[string]NullPerObjectAttributeStruct {
   382  	nullObjectsList := make(map[string]NullPerObjectAttributeStruct)
   383  	for _, obj := range objectList {
   384  		nullObjectFields := NullPerObjectAttributeStruct{}
   385  		objectName := ""
   386  		objectDCID := ""
   387  		objectMap := make(map[string]string)
   388  		objectChildList := make(map[string]interface{})
   389  		for objF, objD := range obj.(map[string]interface{}) {
   390  			objVal := fmt.Sprint(objD)
   391  			switch fmt.Sprintf("%T", objD) {
   392  			case "<nil>":
   393  				if objVal == "<nil>" {
   394  					objectMap[makeFirstCharUpperCase(objF)] = ""
   395  				}
   396  			case "map[string]interface {}":
   397  				// include null stand alone struct elements in core
   398  				for moName, moValue := range objD.(map[string]interface{}) {
   399  					if fmt.Sprintf("%T", moValue) == "<nil>" {
   400  						objectMap[makeFirstCharUpperCase(moName)] = ""
   401  					}
   402  				}
   403  			case "[]interface {}":
   404  				iSlice := objD.([]interface{})
   405  				if len(iSlice) > 0 && reflect.TypeOf(iSlice[0]).Kind() != reflect.String && reflect.TypeOf(iSlice[0]).Kind() != reflect.Int64 && reflect.TypeOf(iSlice[0]).Kind() != reflect.Float64 && reflect.TypeOf(iSlice[0]).Kind() != reflect.Int32 {
   406  					objectChildList[makeFirstCharUpperCase(objF)] = processObjectList(objD.([]interface{}))
   407  				}
   408  			default:
   409  				if objF == "name" {
   410  					objectName = objVal
   411  				}
   412  				if objF == "datacenterId" {
   413  					objectDCID = objVal
   414  				}
   415  			}
   416  		}
   417  		nullObjectFields.CoreObjectFields = objectMap
   418  		nullObjectFields.ChildObjectFields = objectChildList
   419  
   420  		if objectDCID == "" {
   421  			if objectName != "" {
   422  				nullObjectsList[objectName] = nullObjectFields
   423  			} else {
   424  				nullObjectsList["unknown"] = nullObjectFields // TODO: What if mnore than one?
   425  			}
   426  		} else {
   427  			nullObjectsList[objectDCID] = nullObjectFields
   428  		}
   429  	}
   430  
   431  	return nullObjectsList
   432  }