rob05c commented on a change in pull request #2124: Add TO Go deliveryservices 
routes
URL: 
https://github.com/apache/incubator-trafficcontrol/pull/2124#discussion_r190379804
 
 

 ##########
 File path: 
traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
 ##########
 @@ -0,0 +1,1109 @@
+package deliveryservice
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+       "database/sql"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strings"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/config"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/riaksvc"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+
+       "github.com/go-ozzo/ozzo-validation"
+       "github.com/jmoiron/sqlx"
+       "github.com/lib/pq"
+)
+
+//we need a type alias to define functions on
+type TODeliveryServiceV13 struct {
+       DS  *tc.DeliveryServiceNullableV13
+       Cfg config.Config
+       DB  *sqlx.DB
+}
+
+func (tods *TODeliveryServiceV13) V12() *TODeliveryServiceV12 {
+       return &TODeliveryServiceV12{DS: &tods.DS.DeliveryServiceNullableV12, 
DB: tods.DB, Cfg: tods.Cfg}
+}
+
+func (tods TODeliveryServiceV13) MarshalJSON() ([]byte, error) { return 
json.Marshal(tods.DS) }
+func (tods *TODeliveryServiceV13) UnmarshalJSON(data []byte) error {
+       return json.Unmarshal(data, tods.DS)
+}
+
+func GetRefTypeV13(cfg config.Config, db *sqlx.DB) *TODeliveryServiceV13 {
+       return &TODeliveryServiceV13{Cfg: cfg, DB: db, DS: 
&tc.DeliveryServiceNullableV13{}}
+}
+
+func (ds TODeliveryServiceV13) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return ds.V12().GetKeyFieldsInfo()
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (ds TODeliveryServiceV13) GetKeys() (map[string]interface{}, bool) {
+       return ds.V12().GetKeys()
+}
+
+func (tods *TODeliveryServiceV13) SetKeys(keys map[string]interface{}) {
+       tods.V12().SetKeys(keys)
+}
+
+func (ds *TODeliveryServiceV13) GetAuditName() string {
+       return ds.V12().GetAuditName()
+}
+
+func (ds *TODeliveryServiceV13) GetType() string {
+       return ds.V12().GetType()
+}
+
+func ValidateV13(db *sqlx.DB, ds *tc.DeliveryServiceNullableV13) []error {
+       if ds == nil {
+               return []error{}
+       }
+       tods := TODeliveryServiceV13{DS: ds, DB: db} // TODO set Cfg?
+       return tods.Validate(db)
+}
+
+func (tods *TODeliveryServiceV13) Sanitize(db *sqlx.DB) {
+       v12 := tods.V12()
+       v12.Sanitize(db)
+       tods.DS.DeliveryServiceNullableV12 = 
tc.DeliveryServiceNullableV12(*v12.DS) // TODO avoid copy
+       signedAlgorithm := "url_sig"
+       if tods.DS.Signed && (tods.DS.SigningAlgorithm == nil || 
*tods.DS.SigningAlgorithm == "") {
+               tods.DS.SigningAlgorithm = &signedAlgorithm
+       }
+       if !tods.DS.Signed && tods.DS.SigningAlgorithm != nil && 
*tods.DS.SigningAlgorithm == signedAlgorithm {
+               tods.DS.Signed = true
+       }
+       if tods.DS.DeepCachingType == nil {
+               s := tc.DeepCachingType("")
+               tods.DS.DeepCachingType = &s
+       }
+       *tods.DS.DeepCachingType = 
tc.DeepCachingTypeFromString(string(*tods.DS.DeepCachingType))
+}
+
+func (tods *TODeliveryServiceV13) Validate(db *sqlx.DB) []error {
+       tods.Sanitize(db)
+       ds := tods.DS
+       neverOrAlways := 
validation.NewStringRule(tovalidate.IsOneOfStringICase("NEVER", "ALWAYS"),
+               "must be one of 'NEVER' or 'ALWAYS'")
+       errs := tovalidate.ToErrors(validation.Errors{
+               "deepCachingType": validation.Validate(ds.DeepCachingType, 
neverOrAlways),
+       })
+
+       oldErrs := tods.V12().Validate(db)
+       return append(errs, oldErrs...)
+}
+
+// Create implements the Creator interface.
+//all implementations of Creator should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a ds with conflicting 
values exists
+//if so, it will return an errorType of DataConflict and the type should be 
appended to the
+//generic error message returned
+//The insert sql returns the id and lastUpdated values of the newly inserted 
ds and have
+//to be added to the struct
+func (tods *TODeliveryServiceV13) Create(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       ds := tods.DS
+
+       // TODO allow users to post names (type, cdn, etc) and get the IDs from 
the names. This isn't trivial to do in a single query, without dynamically 
building the entire insert query, and ideally inserting would be one query. But 
it'd be much more convenient for users. Alternatively, remove IDs from the 
database entirely and use real candidate keys.
+
+       // TODO change DeepCachingType to implement sql.Valuer and sql.Scanner, 
so sqlx struct scan can be used.
+       deepCachingType := tc.DeepCachingType("").String()
+       if ds.DeepCachingType != nil {
+               deepCachingType = ds.DeepCachingType.String() // necessary, 
because DeepCachingType's default needs to insert the string, not "", and Query 
doesn't call .String().
+       }
+
+       tx, err := db.Beginx()
+       if err != nil {
+               log.Errorln("could not begin transaction: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+       commitTx := false
+       defer FinishTx(tx, &commitTx)
+
+       resultRows, err := tx.Query(insertQuery(), &ds.Active, &ds.CacheURL, 
&ds.CCRDNSTTL, &ds.CDNID, &ds.CheckPath, &deepCachingType, &ds.DisplayName, 
&ds.DNSBypassCNAME, &ds.DNSBypassIP, &ds.DNSBypassIP6, &ds.DNSBypassTTL, 
&ds.DSCP, &ds.EdgeHeaderRewrite, &ds.GeoLimitRedirectURL, &ds.GeoLimit, 
&ds.GeoLimitCountries, &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS, 
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.InfoURL, &ds.InitialDispersion, 
&ds.IPV6RoutingEnabled, &ds.LogsEnabled, &ds.LongDesc, &ds.LongDesc1, 
&ds.LongDesc2, &ds.MaxDNSAnswers, &ds.MidHeaderRewrite, &ds.MissLat, 
&ds.MissLong, &ds.MultiSiteOrigin, &ds.OrgServerFQDN, &ds.OriginShield, 
&ds.ProfileID, &ds.Protocol, &ds.QStringIgnore, &ds.RangeRequestHandling, 
&ds.RegexRemap, &ds.RegionalGeoBlocking, &ds.RemapText, &ds.RoutingName, 
&ds.SigningAlgorithm, &ds.SSLKeyVersion, &ds.TenantID, &ds.TRRequestHeaders, 
&ds.TRResponseHeaders, &ds.TypeID, &ds.XMLID)
+
+       if err != nil {
+               if pqerr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqerr)
+                       return errors.New("a delivery service with " + 
err.Error()), eType
+               }
+               log.Errorln("received non pq error from create execution: " + 
err.Error())
+               return tc.DBError, tc.SystemError
+       }
+
+       id := 0
+       lastUpdated := tc.TimeNoMod{}
+       if !resultRows.Next() {
+               log.Errorln("no deliveryservice request inserted, no id was 
returned")
+               return tc.DBError, tc.SystemError
+       }
+       if err := resultRows.Scan(&id, &lastUpdated); err != nil {
+               log.Errorln("could not scan id from insert: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+       if resultRows.Next() {
+               log.Errorln("too many ids returned from deliveryservice request 
insert")
+               return tc.DBError, tc.SystemError
+       }
+       tods.SetKeys(map[string]interface{}{"id": id})
+
+       if ds.ID == nil {
+               return errors.New("missing id after insert"), tc.SystemError
+       }
+       if ds.XMLID == nil {
+               return errors.New("missing xml_id after insert"), tc.SystemError
+       }
+       if ds.TypeID == nil {
+               return errors.New("missing type after insert"), tc.SystemError
+       }
+       dsType, err := getTypeNameFromID(*ds.TypeID, tx)
+       if err != nil {
+               return errors.New("getting delivery service type: " + 
err.Error()), tc.DataMissingError
+       }
+       ds.Type = &dsType
+       if ds.Protocol == nil {
+               return errors.New("missing protocol after insert"), 
tc.SystemError
+       }
+
+       if err := createDefaultRegex(tx, *ds.ID, *ds.XMLID); err != nil {
+               log.Errorln("creating default regex: " + err.Error())
+               return errors.New("failed to create default regex"), 
tc.SystemError
+       }
+
+       matchlists, err := 
readGetDeliveryServicesMatchLists([]string{*ds.XMLID}, tx)
+       if err != nil {
+               log.Errorln("creating DS: reading matchlists: " + err.Error())
+               return errors.New("failed to get matchlists"), tc.SystemError
+       }
+       if matchlist, ok := matchlists[*ds.XMLID]; !ok {
+               log.Errorln("creating DS: reading matchlists: not found")
+               return errors.New("failed to get matchlist"), tc.SystemError
+       } else {
+               ds.MatchList = &matchlist
+       }
+
+       cdnName, cdnDomain, dnssecEnabled, err := 
getCDNNameDomainDNSSecEnabled(*ds.ID, tx)
+       if err != nil {
+               log.Errorln("creating DS: getting CDN info: " + err.Error())
+               return errors.New("failed to get CDN info"), tc.SystemError
+       }
+
+       ds.ExampleURLs = makeExampleURLs(*ds.Protocol, *ds.Type, 
*ds.RoutingName, *ds.MatchList, cdnDomain)
+
+       if err := ensureHeaderRewriteParams(tx, *ds.ID, *ds.XMLID, 
ds.EdgeHeaderRewrite, edgeTier, *ds.Type); err != nil {
+               log.Errorln("creating edge header rewrite parameters: " + 
err.Error())
+               return errors.New("failed to create header rewrite 
parameters"), tc.SystemError
+       }
+       if err := ensureHeaderRewriteParams(tx, *ds.ID, *ds.XMLID, 
ds.MidHeaderRewrite, midTier, *ds.Type); err != nil {
+               log.Errorln("creating mid header rewrite parameters: " + 
err.Error())
+               return errors.New("failed to create header rewrite 
parameters"), tc.SystemError
+       }
+       if err := ensureRegexRemapParams(tx, *ds.ID, *ds.XMLID, ds.RegexRemap); 
err != nil {
+               log.Errorln("creating regex remap parameters: " + err.Error())
+               return errors.New("failed to create regex remap parameters"), 
tc.SystemError
+       }
+       if err := ensureCacheURLParams(tx, *ds.ID, *ds.XMLID, ds.CacheURL); err 
!= nil {
+               log.Errorln("creating cache url parameters: " + err.Error())
+               return errors.New("failed to create cache url parameters"), 
tc.SystemError
+       }
+
+       if err := createDNSSecKeys(tx, tods.Cfg, *ds.ID, *ds.XMLID, 
*ds.Protocol, cdnName, cdnDomain, dnssecEnabled, ds.ExampleURLs); err != nil {
+               log.Errorln("creating DNSSEC keys: " + err.Error())
+               return errors.New("failed to create DNSSEC keys"), 
tc.SystemError
+       }
+       ds.LastUpdated = &lastUpdated
+       commitTx = true
+       return nil, tc.NoError
+}
+
+func (tods *TODeliveryServiceV13) Read(db *sqlx.DB, params map[string]string, 
user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+       returnable := []interface{}{}
+       dses, errs, errType := readGetDeliveryServices(params, db)
+       if len(errs) > 0 {
+               for _, err := range errs {
+                       if err.Error() == `id cannot parse to integer` { // 
TODO create const for string
+                               return nil, []error{errors.New("Resource not 
found.")}, tc.DataMissingError //matches perl response
+                       }
+               }
+               return nil, errs, errType
+       }
+
+       dses, err := filterAuthorized(dses, user, db)
+       if err != nil {
+               log.Errorln("Checking tenancy: " + err.Error())
+               return nil, []error{errors.New("Error checking tenancy.")}, 
tc.SystemError
+       }
+
+       for _, ds := range dses {
+               returnable = append(returnable, ds)
+       }
+       return returnable, nil, tc.NoError
+}
+
+// FinishTx commits the transaction if commit is true when it's called, 
otherwise it rolls back the transaction. This is designed to be called in a 
defer.
+func FinishTx(tx *sqlx.Tx, commit *bool) {
+       if tx == nil {
+               return
+       }
+       if !*commit {
+               tx.Rollback()
+               return
+       }
+       tx.Commit()
+}
+
+func createDefaultRegex(tx *sqlx.Tx, dsID int, xmlID string) error {
+       regexStr := `.*\.` + xmlID + `\..*`
+       regexID := 0
+       if err := tx.QueryRow(`INSERT INTO regex (type, pattern) VALUES 
((select id from type where name = 'HOST_REGEXP'), $1::text) RETURNING id`, 
regexStr).Scan(&regexID); err != nil {
+               return errors.New("insert regex: " + err.Error())
+       }
+       if _, err := tx.Exec(`INSERT INTO deliveryservice_regex 
(deliveryservice, regex, set_number) VALUES ($1::bigint, $2::bigint, 0)`, dsID, 
regexID); err != nil {
+               return errors.New("executing parameter query to insert 
location: " + err.Error())
+       }
+       return nil
+}
+
+func getOldHostName(id int, tx *sqlx.Tx) (string, error) {
+       q := `
+SELECT ds.xml_id, ds.protocol, type.name, ds.routing_name, cdn.domain_name
+FROM  deliveryservice as ds
+JOIN type ON ds.type = type.id
+JOIN cdn ON ds.cdn_id = cdn.id
+WHERE ds.id=$1
+`
+       xmlID := ""
+       protocol := sql.NullInt64{}
+       dsType := ""
+       routingName := ""
+       cdnDomain := ""
+       if err := tx.QueryRow(q, id).Scan(&xmlID, &protocol, &dsType, 
&routingName, &cdnDomain); err != nil {
+               return "", fmt.Errorf("querying delivery service %v host name: 
"+err.Error()+"\n", id)
+       }
+       matchLists, err := readGetDeliveryServicesMatchLists([]string{xmlID}, 
tx)
+       if err != nil {
+               return "", errors.New("getting delivery services matchlist: " + 
err.Error())
+       }
+       matchList, ok := matchLists[xmlID]
+       if !ok {
+               return "", errors.New("delivery service has no match lists (is 
your delivery service missing regexes?)")
+       }
+       host, err := getHostName(int(protocol.Int64), dsType, routingName, 
matchList, cdnDomain) // protocol defaults to 0: doesn't need to check Valid()
+       if err != nil {
+               return "", errors.New("getting hostname: " + err.Error())
+       }
+       return host, nil
+}
+
+func getTypeNameFromID(id int, tx *sqlx.Tx) (string, error) {
+       // TODO combine with getOldHostName, to only make one query?
+       name := ""
+       if err := tx.QueryRow(`SELECT name FROM type WHERE id = $1`, 
id).Scan(&name); err != nil {
+               return "", fmt.Errorf("querying type ID %v: "+err.Error()+"\n", 
id)
+       }
+       return name, nil
+}
+
+//The TODeliveryService implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a delivery service with 
conflicting values exists
+//if so, it will return an errorType of DataConflict and the type should be 
appended to the
+//generic error message returned
+func (tods *TODeliveryServiceV13) Update(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       ds := tods.DS
+       tx, err := db.Beginx()
+       if err != nil {
+               log.Errorln("could not begin transaction: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+       commitTx := false
+       defer FinishTx(tx, &commitTx)
+
+       if ds.XMLID == nil {
+               return errors.New("missing xml_id"), tc.DataMissingError
+       }
+       if ds.ID == nil {
+               return errors.New("missing id"), tc.DataMissingError
+       }
+
+       oldHostName, err := getOldHostName(*ds.ID, tx)
+       if err != nil {
+               return errors.New("error getting existing delivery service 
hostname: " + err.Error()), tc.SystemError
+       }
+
+       // TODO change DeepCachingType to implement sql.Valuer and sql.Scanner, 
so sqlx struct scan can be used.
+       deepCachingType := tc.DeepCachingType("").String()
+       if ds.DeepCachingType != nil {
+               deepCachingType = ds.DeepCachingType.String() // necessary, 
because DeepCachingType's default needs to insert the string, not "", and Query 
doesn't call .String().
+       }
+
+       resultRows, err := tx.Query(updateDSQuery(), &ds.Active, &ds.CacheURL, 
&ds.CCRDNSTTL, &ds.CDNID, &ds.CheckPath, &deepCachingType, &ds.DisplayName, 
&ds.DNSBypassCNAME, &ds.DNSBypassIP, &ds.DNSBypassIP6, &ds.DNSBypassTTL, 
&ds.DSCP, &ds.EdgeHeaderRewrite, &ds.GeoLimitRedirectURL, &ds.GeoLimit, 
&ds.GeoLimitCountries, &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS, 
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.InfoURL, &ds.InitialDispersion, 
&ds.IPV6RoutingEnabled, &ds.LogsEnabled, &ds.LongDesc, &ds.LongDesc1, 
&ds.LongDesc2, &ds.MaxDNSAnswers, &ds.MidHeaderRewrite, &ds.MissLat, 
&ds.MissLong, &ds.MultiSiteOrigin, &ds.OrgServerFQDN, &ds.OriginShield, 
&ds.ProfileID, &ds.Protocol, &ds.QStringIgnore, &ds.RangeRequestHandling, 
&ds.RegexRemap, &ds.RegionalGeoBlocking, &ds.RemapText, &ds.RoutingName, 
&ds.SigningAlgorithm, &ds.SSLKeyVersion, &ds.TenantID, &ds.TRRequestHeaders, 
&ds.TRResponseHeaders, &ds.TypeID, &ds.XMLID, &ds.ID)
+
+       if err != nil {
+               if err, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(err)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a delivery service with " + 
err.Error()), eType
+                       }
+                       return err, eType
+               }
+               log.Errorf("received error: %++v from update execution", err)
+               return tc.DBError, tc.SystemError
+       }
+       if !resultRows.Next() {
+               // return errors.New("no delivery service found with this id"), 
tc.DataMissingError, tc.SystemError
+               return errors.New("no delivery service found with this id"), 
tc.DataMissingError
+       }
+       lastUpdated := tc.TimeNoMod{}
+       if err := resultRows.Scan(&lastUpdated); err != nil {
+               log.Errorf("could not scan lastUpdated from insert: %s\n", err)
+               return tc.DBError, tc.SystemError
+       }
+       if resultRows.Next() {
+               xmlID := ""
+               if ds.XMLID != nil {
+                       xmlID = *ds.XMLID
+               }
+               err := errors.New("this update affected too many rows: > 1")
+               log.Errorln("updating delivery service " + xmlID + ": " + 
err.Error())
+               return err, tc.SystemError
+       }
+
+       if ds.ID == nil {
+               return errors.New("missing id after update"), tc.SystemError
+       }
+       if ds.XMLID == nil {
+               return errors.New("missing xml_id after update"), tc.SystemError
+       }
+       if ds.TypeID == nil {
+               return errors.New("missing type after update"), tc.SystemError
+       }
+       if ds.Protocol == nil {
+               return errors.New("missing protocol after update"), 
tc.SystemError
+       }
+       if ds.RoutingName == nil {
+               return errors.New("missing routing name after update"), 
tc.SystemError
+       }
+       dsType, err := getTypeNameFromID(*ds.TypeID, tx)
+       if err != nil {
+               return errors.New("getting delivery service type: " + 
err.Error()), tc.DataMissingError
+       }
+       ds.Type = &dsType
+
+       cdnDomain, err := getCDNDomain(*ds.ID, db) // need to get the domain 
again, in case it changed.
+       if err != nil {
+               return errors.New("getting new CDN domain: " + err.Error()), 
tc.SystemError
+       }
+
+       newHostName, err := getHostName(*ds.Protocol, *ds.Type, 
*ds.RoutingName, *ds.MatchList, cdnDomain)
+       if err != nil {
+               return errors.New("getting new hostname: " + err.Error()), 
tc.SystemError
+       }
+
+       matchLists, err := 
readGetDeliveryServicesMatchLists([]string{*ds.XMLID}, tx)
+       if err != nil {
+               return errors.New("getting matchlists after update: " + 
err.Error()), tc.SystemError
+       }
+       if ml, ok := matchLists[*ds.XMLID]; !ok {
+               return errors.New("no matchlists after update"), tc.SystemError
+       } else {
+               ds.MatchList = &ml
+       }
+
+       if oldHostName != newHostName {
+               if err := updateSSLKeys((*tc.DeliveryServiceNullableV13)(ds), 
newHostName, db, tods.Cfg); err != nil {
+                       err := errors.New("updating SSL keys: " + err.Error())
+                       log.Errorln("updating delivery service " + *ds.XMLID + 
": updating SSL keys: " + err.Error())
+                       return err, tc.SystemError
+               }
+       }
+
+       if err := ensureHeaderRewriteParams(tx, *ds.ID, *ds.XMLID, 
ds.EdgeHeaderRewrite, edgeTier, *ds.Type); err != nil {
+               log.Errorln("creating header rewrite parameters: " + 
err.Error())
+               return errors.New("failed to create header rewrite 
parameters"), tc.SystemError
+       }
+       if err := ensureHeaderRewriteParams(tx, *ds.ID, *ds.XMLID, 
ds.MidHeaderRewrite, midTier, *ds.Type); err != nil {
+               log.Errorln("creating header rewrite parameters: " + 
err.Error())
+               return errors.New("failed to create header rewrite 
parameters"), tc.SystemError
+       }
+       if err := ensureRegexRemapParams(tx, *ds.ID, *ds.XMLID, ds.RegexRemap); 
err != nil {
+               log.Errorln("creating regex remap parameters: " + err.Error())
+               return errors.New("failed to create regex remap parameters"), 
tc.SystemError
+       }
+       if err := ensureCacheURLParams(tx, *ds.ID, *ds.XMLID, ds.CacheURL); err 
!= nil {
+               log.Errorln("creating cache url parameters: " + err.Error())
+               return errors.New("failed to create cache url parameters"), 
tc.SystemError
+       }
+
+       ds.LastUpdated = &lastUpdated
+       commitTx = true
+       return nil, tc.NoError
+}
+
+//The DeliveryService implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper 
errorType
+func (tods *TODeliveryServiceV13) Delete(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       ds := tods.DS
+
+       log.Debugln("TODeliveryServiceV13.Delete calling id '%v' xmlid '%v'\n", 
ds.ID, ds.XMLID)
+       // return nil, tc.NoError // debug
+
+       tx, err := db.Beginx()
+       if err != nil {
+               log.Errorln("could not begin transaction: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+       commitTx := false
+       defer FinishTx(tx, &commitTx)
+
+       if err != nil {
+               log.Errorln("could not begin transaction: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+
+       if tods.DS.ID == nil {
+               log.Errorln("TODeliveryServiceV13.Delete called with nil ID")
+               return tc.DBError, tc.DataMissingError
+       } else if ok, err := tods.V12().LoadXMLID(db); err != nil {
+               log.Errorln("TODeliveryServiceV13.Delete ID '" + 
string(*tods.DS.ID) + "' loading XML ID: " + err.Error())
+               return tc.DBError, tc.SystemError
+       } else if !ok {
+               log.Errorln("TODeliveryServiceV13.Delete ID '" + 
string(*tods.DS.ID) + "' had no delivery service!")
+               return tc.DBError, tc.DataMissingError
+       } else if ds.XMLID == nil {
+               // should never happen
+               log.Errorf("TODeliveryServiceV13.Delete ID '%v' loaded nil XML 
ID!", *tods.DS.ID)
+               return tc.DBError, tc.SystemError
+       }
+
+       result, err := tx.Exec(`DELETE FROM deliveryservice WHERE id=$1`, ds.ID)
+       if err != nil {
+               log.Errorln("TODeliveryServiceV13.Delete deleting delivery 
service: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+       rowsAffected, err := result.RowsAffected()
+       if err != nil {
+               return tc.DBError, tc.SystemError
+       }
+       if rowsAffected != 1 {
+               if rowsAffected < 1 {
+                       return errors.New("no delivery service with that id 
found"), tc.DataMissingError
+               }
+               return fmt.Errorf("this create affected too many rows: %d", 
rowsAffected), tc.SystemError
+       }
+
+       if _, err := tx.Exec(`DELETE FROM deliveryservice_regex WHERE 
deliveryservice=$1`, ds.ID); err != nil {
+               log.Errorln("TODeliveryServiceV13.Delete deleting delivery 
service regexes: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+
+       paramConfigFilePrefixes := []string{"hdr_rw_", "hdr_rw_mid_", 
"regex_remap_", "cacheurl_"}
+       configFiles := []string{}
+       for _, prefix := range paramConfigFilePrefixes {
+               configFiles = append(configFiles, prefix+*ds.XMLID+".config")
+       }
+
+       if _, err := tx.Exec(`DELETE FROM parameter WHERE name = 'location' AND 
config_file = ANY($1)`, pq.Array(configFiles)); err != nil {
+               log.Errorln("TODeliveryServiceV13.Delete deleting delivery 
service parameters: " + err.Error())
+               return tc.DBError, tc.SystemError
+       }
+
+       commitTx = true
+       return nil, tc.NoError
+}
+
+// IsTenantAuthorized implements the Tenantable interface to ensure the user 
is authorized on the deliveryservice tenant
+func (tods *TODeliveryServiceV13) IsTenantAuthorized(user auth.CurrentUser, db 
*sqlx.DB) (bool, error) {
+       tods.V12().LoadTenantID(db) // ignore errors and keep user-set tenant 
ID (which happens on Create)
+
+       // TODO make Tenant framework able to handle loading vs accepting user 
input, for Create vs Update vs Delete
+
+       // if ok, err := tods.V12().LoadTenantID(db); err != nil {
+       //      return false, errors.New("loading tenant ID from database: " + 
err.Error())
+       // } else if !ok {
+       //      return false, errors.New("delivery service not found in 
database")
+       // }
+       if tods.DS.TenantID == nil {
+               log.Debugf("tenantID is nil")
+               return false, errors.New("tenantID is nil")
+       }
+       return tenant.IsResourceAuthorizedToUser(*tods.DS.TenantID, user, db)
+}
+
+func filterAuthorized(dses []tc.DeliveryServiceNullableV13, user 
auth.CurrentUser, db *sqlx.DB) ([]tc.DeliveryServiceNullableV13, error) {
 
 Review comment:
   Fixed

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on 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