rawlinp commented on a change in pull request #3989: Rewrote 
/deliveryservices/request to Go
URL: https://github.com/apache/trafficcontrol/pull/3989#discussion_r337198277
 
 

 ##########
 File path: lib/go-tc/deliveryservice_requests.go
 ##########
 @@ -20,15 +20,394 @@ import (
        "encoding/json"
        "errors"
        "fmt"
+       "html/template"
        "strconv"
        "strings"
 
-       log "github.com/apache/trafficcontrol/lib/go-log"
+       "github.com/apache/trafficcontrol/lib/go-log"
+       "github.com/apache/trafficcontrol/lib/go-util"
+
+       "github.com/go-ozzo/ozzo-validation"
+       "github.com/go-ozzo/ozzo-validation/is"
 )
 
+// EmailTemplate is an html/template.Template for formatting 
DeliveryServiceRequestRequests into
+// text/html email bodies. Its direct use is discouraged, instead use
+// DeliveryServiceRequestRequest.Format.
+var EmailTemplate = template.Must(template.New("Email 
Template").Parse(`<!DOCTYPE html>
+<html lang="en-US">
+<head>
+<meta charset="utf-8"/>
+<title>Delivery Service Request for {{.Customer}}</title>
+<style>
+aside {
+       padding: 0 1em;
+       color: #6A737D;
+       border-left: .25em solid #DFE2E5;
+}
+body {
+       font-family: sans;
+       background-color: white;
+}
+pre {
+       padding: 5px;
+       background-color: lightgray;
+}
+</style>
+</head>
+<body>
+<h1>Delivery Service Request for {{.Customer}}</h1>
+<p>{{.ServiceDesc}}</p>
+<section>
+       <details>
+               <summary><h2>Service Description</h2></summary>
+               <h3>Content Type</h3>
+               <p>{{.ContentType}}</p>
+               <h3>Delivery Protocol</h3>
+               <p>{{.DeliveryProtocol.String}}</p>
+               <h3>Routing Type</h3>
+               <p>{{.RoutingType.String}}</p>
+       </details>
+</section>
+<section>
+       <details>
+               <summary><h2>Traffic &amp; Library Estimates</h2></summary>
+               <h3>Peak Bandwidth Estimate</h3>
+               <p>{{.PeakBPSEstimate}}Bps</p>
+               <h3>Peak Transactions per Second Estimate</h3>
+               <p>{{.PeakTPSEstimate}}Tps</p>
+               <h3>Max Library Size Estimate</h3>
+               <p>{{.MaxLibrarySizeEstimate}}GB</p>
+       </details>
+</section>
+<section>
+       <details>
+               <summary><h2>Origin Security</h2></summary>
+               <h3>Origin Server URL</h3>
+               <p><a href="{{.OriginURL}}">{{.OriginURL}}</a></p>
+               <h3>Origin Dynamic Remap</h3>
+               <p>{{.HasOriginDynamicRemap}}</p>
+               <h3>Origin Test File</h3>
+               <p>{{.OriginTestFile}}</p>
+               <h3>ACL/Whitelist to Access Origin</h3>
+               <p>{{.HasOriginACLWhitelist}}</p>
+               {{if .OriginHeaders}}<h3>Header(s) to Access Origin</h3>
+               <ul>{{range .OriginHeaders}}
+                       <li>{{.}}</li>{{end}}
+               </ul>{{end}}
+               <h3>Other Origin Security</h3>
+               <p>{{if 
.OtherOriginSecurity}}{{.OtherOriginSecurity}}{{else}}None{{end}}</p>
+       </details>
+</section>
+<section>
+       <details>
+               <summary><h2>Core Features</h2></summary>
+               <h3>Query String Handling</h3>
+               <p>{{.QueryStringHandling}}</p>
+               <h3>Range Request Handling</h3>
+               <p>{{.RangeRequestHandling}}</p>
+               <h3>Signed URLs / URL Tokenization</h3>
+               <p>{{.HasSignedURLs}}</p>
+               <h3>Negative Caching Customization</h3>
+               <p>{{.HasNegativeCachingCustomization}}</p>
+               {{if or .HasNegativeCachingCustomization 
.NegativeCachingCustomizationNote }}<aside>
+                       <p>{{.NegativeCachingCustomizationNote}}</p>
+               </aside>{{else if .HasNegativeCachingCustomization}}<aside>
+                       <p><b>No instructions given!</b></p>
+               </aside>{{end}}
+               {{if .ServiceAliases}}<h3>Service Aliases</h3>
+               <ul>{{range .ServiceAliases}}
+                       <li>{{.}}</li>{{end}}
+               </ul>{{end}}
+       </details>
+</section>
+{{if or .RateLimitingGBPS .RateLimitingTPS .OverflowService}}<section>
+       <details>
+               <summary><h2>Service Limits</h2></summary>
+               {{if .RateLimitingGBPS}}<h3>Bandwidth Limit</h3>
+               <p>{{.RateLimitingGBPS}}GBps</p>{{end}}
+               {{if .RateLimitingTPS}}<h3>Transactions per Second Limit</h3>
+               <p>{{.RateLimitingTPS}}Tps</p>{{end}}
+               {{if .OverflowService}}<h3>Overflow Service</h3>
+               <p>{{.OverflowService}}</p>{{end}}
+       </details>
+</section>{{end}}
+{{if or .HeaderRewriteEdge .HeaderRewriteMid 
.HeaderRewriteRedirectRouter}}<section>
+       <details>
+               <summary><h2>Header Customization</h2></summary>
+               {{if .HeaderRewriteEdge}}<h3>Header Rewrite - Edge Tier</h3>
+               <pre>{{.HeaderRewriteEdge}}</pre>{{end}}
+               {{if .HeaderRewriteMid}}<h3>Header Rewrite - Mid Tier</h3>
+               <pre>{{.HeaderRewriteMid}}</pre>{{end}}
+               {{if .HeaderRewriteRedirectRouter}}<h3>Header Rewrite - 
Router</h3>
+               <pre>{{.HeaderRewriteRedirectRouter}}</pre>{{end}}
+       </details>
+</section>{{end}}
+{{if .Notes}}<section>
+       <details>
+               <summary><h2>Additional Notes</h2></summary>
+               <p>{{.Notes}}</p>
+       </details>
+</section>{{end}}
+</body>
+</html>
+`))
+
 // IDNoMod type is used to suppress JSON unmarshalling
 type IDNoMod int
 
+// DeliveryServiceRequestRequest is a literal request to make a Delivery 
Service.
+type DeliveryServiceRequestRequest struct {
+       // EmailTo is the email address that is ultimately the destination of a 
formatted DeliveryServiceRequestRequest.
+       EmailTo string `json:"emailTo"`
+       // Details holds the actual request in a data structure.
+       Details DeliveryServiceRequestDetails `json:"details"`
+}
+
+// DeliveryServiceRequestDetails
+type DeliveryServiceRequestDetails struct {
+       // ContentType is the type of content to be delivered, e.g. "static", 
"VOD" etc.
+       ContentType string `json:"contentType"`
+       // Customer is the requesting customer - typically this is a Tenant.
+       Customer string `json:"customer"`
+       // DeepCachingType represents whether or not the Delivery Service 
should use Deep Caching.
+       DeepCachingType *DeepCachingType `json:"deepCachingType"`
+       // Delivery Protocol is the protocol clients should use to connect to 
the Delivery Service.
+       DeliveryProtocol *Protocol `json:"deliveryProtocol"`
+       // HasNegativeCachingCustomization indicates whether or not the 
resulting Delivery Service should
+       // customize the use of negative caching. When this is `true`, 
NegativeCachingCustomizationNote
+       // should be consulted for instructions on the customization.
+       HasNegativeCachingCustomization *bool 
`json:"hasNegativeCachingCustomization"`
+       // HasOriginACLWhitelist indicates whether or not the Origin has an ACL 
whitelist. When this is
+       // `true`, Notes should ideally contain the actual whitelist (or 
viewing instructions).
+       HasOriginACLWhitelist *bool `json:"hasOriginACLWhitelist"`
+       // Has OriginDynamicRemap indicates whether or not the OriginURL can 
dynamically map to multiple
+       // different actual origin servers.
+       HasOriginDynamicRemap *bool `json:"hasOriginDynamicRemap"`
+       // HasSignedURLs indicates whether or not the resulting Delivery 
Service should sign its URLs.
+       HasSignedURLs *bool `json:"hasSignedURLs"`
+       // HeaderRewriteEdge is an optional HeaderRewrite rule to apply at the 
Edge tier.
+       HeaderRewriteEdge *string `json:"headerRewriteEdge"`
+       // HeaderRewriteMid is an optional HeaderRewrite rule to apply at the 
Mid tier.
+       HeaderRewriteMid *string `json:"headerRewriteMid"`
+       // HeaderRewriteRedirectRouter is an optional HeaderRewrite rule to 
apply at routing time by
+       // the Traffic Router.
+       HeaderRewriteRedirectRouter *string `json:"headerRewriteRedirectRouter"`
+       // MaxLibrarySizeEstimate is an estimation of the total size of content 
that will be delivered
+       // through the resulting Delivery Service.
+       MaxLibrarySizeEstimate string `json:"maxLibrarySizeEstimate"`
+       // NegativeCachingCustomizationNote is an optional note describing the 
customization to be
+       // applied to Negative Caching. This should never be `nil` (or empty) if
+       // HasNegativeCachingCustomization is `true`, but in that case the 
recipient ought to contact
+       // Customer for instructions.
+       NegativeCachingCustomizationNote *string 
`json:"negativeCachingCustomizationNote"`
+       // Notes is an optional set of extra information supplied to describe 
the requested Delivery
+       // Service.
+       Notes *string `json:"notes"`
+       // OriginHeaders is an optional list of HTTP headers that must be sent 
in requests to the Origin. When
+       // parsing from JSON, this field can be either an actual array of 
headers, or a string containing
+       // a comma-delimited list of said headers.
+       OriginHeaders *OriginHeaders `json:"originHeaders"`
+       // OriginTestFile is the path to a file on the origin that can be 
requested to test the server's
+       // operational readiness, e.g. '/test.xml'.
+       OriginTestFile string `json:"originTestFile"`
+       // OriginURL is the URL of the origin server that has the content to be 
served by the requested
+       // Delivery Service.
+       OriginURL string `json:"originURL"`
+       // OtherOriginSecurity is an optional note about any and all other 
Security employed by the origin
+       // server (beyond an ACL whitelist, which has its own field: 
HasOriginACLWhitelist).
+       OtherOriginSecurity *string `json:"otherOriginSecurity"`
+       // OverflowService is an optional IP Address or URL to which clients 
should be redirected when
+       // the requested Delivery Service exceeds its operational capacity.
+       OverflowService *string `json:"overflowService"`
+       // PeakBPSEstimate is an estimate of the bytes per second expected at 
peak operation.
+       PeakBPSEstimate string `json:"peakBPSEstimate"`
+       // PeakTPSEstimate is an estimate of the transactions per second 
expected at peak operation.
+       PeakTPSEstimate string `json:"peakTPSEstimate"`
+       // QueryStringHandling describes the manner in which the CDN should 
handle query strings in client
+       // requests. Generally one of "use", "drop", or 
"ignore-in-cache-key-and-pass-up".
+       QueryStringHandling string `json:"queryStringHandling"`
+       // RangeRequestHandling describes the manner in which HTTP requests are 
handled.
+       RangeRequestHandling string `json:"rangeRequestHandling"`
+       // RateLimitingGBPS is an optional rate limit for the requested 
Delivery Service in gigabytes per
+       // second.
+       RateLimitingGBPS *uint `json:"rateLimitingGBPS"`
+       // RateLimitingTPS is an optional rate limit for the requested Delivery 
Service in transactions
+       // per second.
+       RateLimitingTPS *uint `json:"rateLimitingTPS"`
+       // RoutingName is the top-level DNS label under which the Delivery 
Service should be requested.
+       RoutingName string `json:"routingName"`
+       // RoutingType is the type of routing Traffic Router should perform for 
the requested Delivery
+       // Service.
+       RoutingType *DSType `json:"routingType"`
+       // ServiceAliases is an optional list of alternative names for the 
requested Delivery Service.
+       ServiceAliases []string `json:"serviceAliases"`
+       // ServiceDesc is a basic description of the requested Delivery Service.
+       ServiceDesc string `json:"serviceDesc"`
+}
+
+// Format formats the DeliveryServiceRequestDetails into the text/html body of 
an email. The template
+// used is EmailTemplate.
+func (d DeliveryServiceRequestDetails) Format() (string, error) {
+       b := &strings.Builder{}
+
+       if err := EmailTemplate.Execute(b, d); err != nil {
+               return "", fmt.Errorf("Failed to apply template: %v", err)
+       }
+       return b.String(), nil
+}
+
+// Validate validates that the delivery service request has all of the 
required fields. In some cases,
+// e.g. the top-level EmailTo field, the format is also checked for 
correctness.
+func (d *DeliveryServiceRequestRequest) Validate() error {
+       errs := make([]error, 0, 2)
+
+       err := validation.ValidateStruct(d,
+               validation.Field(&d.EmailTo, validation.Required, is.Email),
+       )
+       if err != nil {
+               errs = append(errs, err)
+       }
+
+       details := d.Details
+       err = validation.ValidateStruct(&details,
+               validation.Field(&details.ContentType, validation.Required),
+               validation.Field(&details.Customer, validation.Required),
+               validation.Field(&details.DeepCachingType, validation.By(
+                       func(t interface{}) error {
+                               if t == nil {
+                                       return errors.New("deepCachingType: 
required")
+                               }
+                               if *t.(*DeepCachingType) == 
DeepCachingTypeInvalid {
+                                       return errors.New("deepCachingType: 
invalid Deep Caching Type")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.DeliveryProtocol, validation.By(
+                       func(p interface{}) error {
+                               if p == nil {
+                                       return errors.New("deliveryProtocol: 
required")
+                               }
+                               if *p.(*Protocol) == ProtocolInvalid {
+                                       return errors.New("deliveryProtocol: 
invalid Protocol")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.HasNegativeCachingCustomization, 
validation.By(
+                       func (h interface{}) error {
+                               if h == nil {
+                                       return 
errors.New("hasNegativeCachingCustomization: required")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.HasOriginACLWhitelist, validation.By(
+                       func (h interface{}) error {
+                               if h == nil {
+                                       return 
errors.New("hasNegativeCachingCustomization: required")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.HasOriginDynamicRemap, validation.By(
+                       func (h interface{}) error {
+                               if h == nil {
+                                       return 
errors.New("hasNegativeCachingCustomization: required")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.HasSignedURLs, validation.By(
+                       func (h interface{}) error {
+                               if h == nil {
+                                       return 
errors.New("hasNegativeCachingCustomization: required")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.MaxLibrarySizeEstimate, 
validation.Required),
+               validation.Field(&details.OriginHeaders, validation.By(
+                       func (h interface{}) error {
+                               if h == nil {
+                                       return nil
+                               }
+                               if len(*h.(*OriginHeaders)) < 1 {
+                                       return errors.New("originHeaders: 
cannot be an empty list (use 'null' if none)")
+                               }
+                               return nil
+                       })),
+               validation.Field(&details.OriginTestFile, validation.Required),
+               validation.Field(&details.OriginURL, validation.Required, 
is.URL),
+               validation.Field(&details.PeakBPSEstimate, validation.Required),
+               validation.Field(&details.PeakTPSEstimate, validation.Required),
+               validation.Field(&details.QueryStringHandling, 
validation.Required),
+               validation.Field(&details.RangeRequestHandling, 
validation.Required),
+               validation.Field(&details.RoutingName, validation.Required),
+               validation.Field(&details.RoutingType, validation.By(
+                       func (t interface{}) error {
+                               if t == nil || *(t.(*DSType)) == "" {
+                                       return errors.New("routingType: 
required")
+                               }
+                               *t.(*DSType) = 
DSTypeFromString(string(*t.(*DSType)))
+                               switch *(t.(*DSType)) {
 
 Review comment:
   Instead of this switch statement can we simply check if the result from 
`DSTypeFromString` is `DSTypeInvalid` or not? That function basically already 
contains this switch statement.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to