This is an automated email from the ASF dual-hosted git repository.

mitchell852 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-trafficcontrol.git

commit 413e109742abab2af43e9bac6b9ca4df5a7d9da1
Author: Dewayne Richardson <dewr...@apache.org>
AuthorDate: Fri Mar 9 08:40:10 2018 -0700

    work in progress until CRUD Interfaces support compound keys
---
 lib/go-tc/profile_parameters.go                    |  39 +++
 traffic_ops/client/v13/profile_parameter.go        | 133 ++++++++
 .../testing/api/v13/profile_parameters_test.go     | 132 ++++++++
 traffic_ops/testing/api/v13/traffic_control.go     |   1 +
 .../parameter/parameters_test.go                   |   8 +-
 .../profileparameter/profile_parameters.go         | 346 +++++++++++++++++++++
 .../profile_parameters_test.go}                    |  69 ++--
 .../traffic_ops_golang/region/regions_test.go      |   2 +-
 8 files changed, 692 insertions(+), 38 deletions(-)

diff --git a/lib/go-tc/profile_parameters.go b/lib/go-tc/profile_parameters.go
new file mode 100644
index 0000000..4902faf
--- /dev/null
+++ b/lib/go-tc/profile_parameters.go
@@ -0,0 +1,39 @@
+package tc
+
+/*
+ * 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.
+ */
+
+// ProfileParametersResponse ...
+type ProfileParametersResponse struct {
+       Response []ProfileParameter `json:"response"`
+}
+
+// ProfileParameter ...
+type ProfileParameter struct {
+       LastUpdated TimeNoMod `json:"lastUpdated"`
+       Profile     int       `json:"profile"`
+       Parameter   int       `json:"parameter"`
+}
+
+// ProfileParameterNullable ...
+type ProfileParameterNullable struct {
+       LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
+       Profile     *int       `json:"profile" db:"profile"`
+       Parameter   *int       `json:"parameter "db:"parameter"`
+}
diff --git a/traffic_ops/client/v13/profile_parameter.go 
b/traffic_ops/client/v13/profile_parameter.go
new file mode 100644
index 0000000..aaa3fac
--- /dev/null
+++ b/traffic_ops/client/v13/profile_parameter.go
@@ -0,0 +1,133 @@
+/*
+
+   Licensed 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.
+*/
+
+package v13
+
+import (
+       "encoding/json"
+       "fmt"
+       "net"
+       "net/http"
+       "strconv"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+const (
+       API_v13_Profile_Parameters = "/api/1.3/profile_parameters"
+)
+
+// Create a ProfileParameter
+func (to *Session) CreateProfileParameter(pp tc.ProfileParameter) (tc.Alerts, 
ReqInf, error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(pp)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       resp, remoteAddr, err := to.request(http.MethodPost, 
API_v13_Profile_Parameters, reqBody)
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
+
+// Update a Profile Parameter by Profile
+func (to *Session) UpdateParameterByProfile(id int, pp tc.ProfileParameter) 
(tc.Alerts, ReqInf, error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(pp)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       URI := fmt.Sprintf("%s/%d", API_v13_Profile_Parameters, id)
+       resp, remoteAddr, err := to.request(http.MethodPut, URI, reqBody)
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
+
+// Returns a list of Profile Parameters
+func (to *Session) GetProfileParameters() ([]tc.ProfileParameter, ReqInf, 
error) {
+       resp, remoteAddr, err := to.request(http.MethodGet, 
API_v13_Profile_Parameters, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.ProfileParametersResponse
+       err = json.NewDecoder(resp.Body).Decode(&data)
+       return data.Response, reqInf, nil
+}
+
+// GET a Profile Parameter by the Profile
+func (to *Session) GetProfileParameterByIDs(profile int, parameter int) 
([]tc.ProfileParameter, ReqInf, error) {
+       URI := fmt.Sprintf("%s?profile=%d&parameter=%d", 
API_v13_Profile_Parameters, profile)
+       resp, remoteAddr, err := to.request(http.MethodGet, URI, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.ProfileParametersResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// GET a Profile Parameter by the Parameter
+func (to *Session) GetProfileParameterByParameter(parameter int) 
([]tc.ProfileParameter, ReqInf, error) {
+       URI := API_v13_Profile_Parameters + "?parameter=" + 
strconv.Itoa(parameter)
+       resp, remoteAddr, err := to.request(http.MethodGet, URI, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data tc.ProfileParametersResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// DELETE a Parameter by Parameter
+func (to *Session) DeleteParameterByParameter(parameter int) (tc.Alerts, 
ReqInf, error) {
+       URI := fmt.Sprintf("%s/%d", API_v13_Profile_Parameters, parameter)
+       resp, remoteAddr, err := to.request(http.MethodDelete, URI, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       defer resp.Body.Close()
+       var alerts tc.Alerts
+       err = json.NewDecoder(resp.Body).Decode(&alerts)
+       return alerts, reqInf, nil
+}
diff --git a/traffic_ops/testing/api/v13/profile_parameters_test.go 
b/traffic_ops/testing/api/v13/profile_parameters_test.go
new file mode 100644
index 0000000..e4b5f4f
--- /dev/null
+++ b/traffic_ops/testing/api/v13/profile_parameters_test.go
@@ -0,0 +1,132 @@
+/*
+
+   Licensed 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.
+*/
+
+package v13
+
+import (
+       "sync"
+       "testing"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       tc "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+func TestProfileParameters(t *testing.T) {
+
+       CreateTestProfileParameters(t)
+       UpdateTestProfileParameters(t)
+       GetTestProfileParameters(t)
+       DeleteTestProfileParameters(t)
+
+}
+
+func CreateTestProfileParameters(t *testing.T) {
+
+       for _, pp := range testData.ProfileParameters {
+               resp, _, err := TOSession.CreateProfileParameter(pp)
+               log.Debugln("Response: ", resp)
+               if err != nil {
+                       t.Errorf("could not CREATE profile parameters: %v\n", 
err)
+               }
+       }
+
+}
+
+func UpdateTestProfileParameters(t *testing.T) {
+
+       firstPP := testData.ProfileParameters[0]
+       // Retrieve the Parameter by profile so we can get the id for the Update
+       resp, _, err := 
TOSession.GetProfileParameterByParameter(firstPP.Profile)
+       if err != nil {
+               t.Errorf("cannot GET Parameter by name: %v - %v\n", 
firstPP.Profile, err)
+       }
+       remotePP := resp[0]
+       expectedPP := 1
+       remotePP.Profile = expectedPP
+       var alert tc.Alerts
+       alert, _, err = TOSession.UpdateParameterByProfile(remotePP.Profile, 
remotePP)
+       if err != nil {
+               t.Errorf("cannot UPDATE Parameter by profile: %v - %v\n", err, 
alert)
+       }
+
+       // Retrieve the Parameter to check Parameter name got updated
+       resp, _, err = TOSession.GetProfileParameterByProfile(remotePP.Profile)
+       if err != nil {
+               t.Errorf("cannot GET Parameter by profile: %v - %v\n", 
firstPP.Profile, err)
+       }
+       respParameter := resp[0]
+       if respParameter.Profile != expectedPP {
+               t.Errorf("results do not match actual: %s, expected: %s\n", 
respParameter.Profile, expectedPP)
+       }
+
+}
+
+func GetTestProfileParameters(t *testing.T) {
+
+       for _, pp := range testData.ProfileParameters {
+               resp, _, err := 
TOSession.GetProfileParameterByProfile(pp.Profile)
+               if err != nil {
+                       t.Errorf("cannot GET Parameter by name: %v - %v\n", 
err, resp)
+               }
+       }
+}
+
+func DeleteTestProfileParametersParallel(t *testing.T) {
+
+       var wg sync.WaitGroup
+       for _, pp := range testData.ProfileParameters {
+
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+                       DeleteTestProfileParameter(t, pp)
+               }()
+
+       }
+       wg.Wait()
+}
+
+func DeleteTestProfileParameters(t *testing.T) {
+
+       for _, pp := range testData.ProfileParameters {
+               DeleteTestProfileParameter(t, pp)
+       }
+}
+
+func DeleteTestProfileParameter(t *testing.T, pp tc.ProfileParameter) {
+
+       // Retrieve the PtofileParameter by profile so we can get the id for 
the Update
+       resp, _, err := TOSession.GetProfileParameterByIDs(pp.Profile, 
pp.Parameter)
+       if err != nil {
+               t.Errorf("cannot GET Parameter by profile: %v - %v\n", 
pp.Profile, err)
+       }
+       if len(resp) > 0 {
+               respPP := resp[0]
+
+               delResp, _, err := 
TOSession.DeleteProfileParameterByProfile(respPP.Profile)
+               if err != nil {
+                       t.Errorf("cannot DELETE Parameter by profile: %v - 
%v\n", err, delResp)
+               }
+
+               // Retrieve the Parameter to see if it got deleted
+               pls, _, err := TOSession.GetProfileParameterByIDs(pp.Profile)
+               if err != nil {
+                       t.Errorf("error deleting Parameter name: %s\n", 
err.Error())
+               }
+               if len(pls) > 0 {
+                       t.Errorf("expected Parameter Name: %s and ConfigFile: 
%s to be deleted\n", pp.Profile, pp.Parameter)
+               }
+       }
+}
diff --git a/traffic_ops/testing/api/v13/traffic_control.go 
b/traffic_ops/testing/api/v13/traffic_control.go
index b804ca0..0036803 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -31,6 +31,7 @@ type TrafficControl struct {
        Divisions                      []tcapi.Division                      
`json:"divisions"`
        Profiles                       []tcapi.Profile                       
`json:"profiles"`
        Parameters                     []tcapi.Parameter                     
`json:"parameters"`
+       ProfileParameters              []tcapi.ProfileParameter              
`json:"profileParameters"`
        PhysLocations                  []tcapi.PhysLocation                  
`json:"physLocations"`
        Regions                        []tcapi.Region                        
`json:"regions"`
        Servers                        []v13.Server                          
`json:"servers"`
diff --git a/traffic_ops/traffic_ops_golang/parameter/parameters_test.go 
b/traffic_ops/traffic_ops_golang/parameter/parameters_test.go
index 4cbfc89..a16df6e 100644
--- a/traffic_ops/traffic_ops_golang/parameter/parameters_test.go
+++ b/traffic_ops/traffic_ops_golang/parameter/parameters_test.go
@@ -91,15 +91,15 @@ func TestGetParameters(t *testing.T) {
                )
        }
        mock.ExpectQuery("SELECT").WillReturnRows(rows)
-       v := map[string]string{"dsId": "1"}
+       v := map[string]string{"name": "1"}
 
-       parameters, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+       pps, errs, _ := refType.Read(db, v, auth.CurrentUser{})
        if len(errs) > 0 {
                t.Errorf("parameter.Read expected: no errors, actual: %v", errs)
        }
 
-       if len(parameters) != 2 {
-               t.Errorf("parameter.Read expected: len(parameters) == 2, 
actual: %v", len(parameters))
+       if len(pps) != 2 {
+               t.Errorf("parameter.Read expected: len(pps) == 2, actual: %v", 
len(pps))
        }
 
 }
diff --git 
a/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go 
b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
new file mode 100644
index 0000000..3576ca0
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters.go
@@ -0,0 +1,346 @@
+package profileparameter
+
+/*
+ * 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 (
+       "errors"
+       "fmt"
+       "strconv"
+
+       "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/dbhelpers"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+       validation "github.com/go-ozzo/ozzo-validation"
+
+       "github.com/jmoiron/sqlx"
+       "github.com/lib/pq"
+)
+
+//we need a type alias to define functions on
+type TOProfileParameter tc.ProfileParameterNullable
+
+//the refType is passed into the handlers where a copy of its type is used to 
decode the json.
+var refType = TOProfileParameter(tc.ProfileParameterNullable{})
+
+func GetRefType() *TOProfileParameter {
+       return &refType
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (pp *TOProfileParameter) GetID() (int, bool) {
+       if pp.Profile == nil {
+               return 0, false
+       }
+       return *pp.Profile, true
+}
+
+func (pp *TOProfileParameter) GetAuditName() string {
+       if pp.Profile != nil {
+               return strconv.Itoa(*pp.Profile)
+       }
+       return "unknown"
+}
+
+func (pp *TOProfileParameter) GetType() string {
+       return "profileParameter"
+}
+
+func (pp *TOProfileParameter) SetID(i int) {
+       pp.Profile = &i
+}
+
+// Validate fulfills the api.Validator interface
+func (pp TOProfileParameter) Validate(db *sqlx.DB) []error {
+
+       errs := validation.Errors{
+               "profile":   validation.Validate(pp.Profile, 
validation.Required),
+               "parameter": validation.Validate(pp.Parameter, 
validation.Required),
+       }
+
+       return tovalidate.ToErrors(errs)
+}
+
+//The TOProfileParameter implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a profileparameter 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 profile and lastUpdated values of the newly 
inserted profileparameter and have
+//to be added to the struct
+func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       resultRows, err := tx.NamedQuery(insertQuery(), pp)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a parameter with " + 
err.Error()), eType
+                       }
+                       return err, eType
+               }
+               log.Errorf("received non pq error: %++v from create execution", 
err)
+               return tc.DBError, tc.SystemError
+       }
+       defer resultRows.Close()
+
+       var profile int
+       var lastUpdated tc.TimeNoMod
+       rowsAffected := 0
+       for resultRows.Next() {
+               rowsAffected++
+               if err := resultRows.Scan(&profile, &lastUpdated); err != nil {
+                       log.Error.Printf("could not scan profile from insert: 
%s\n", err)
+                       return tc.DBError, tc.SystemError
+               }
+       }
+       if rowsAffected == 0 {
+               err = errors.New("no parameter was inserted, no profile was 
returned")
+               log.Errorln(err)
+               return tc.DBError, tc.SystemError
+       }
+       if rowsAffected > 1 {
+               err = errors.New("too many ids returned from parameter insert")
+               log.Errorln(err)
+               return tc.DBError, tc.SystemError
+       }
+
+       pp.SetID(profile)
+       pp.LastUpdated = &lastUpdated
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+func insertQuery() string {
+       query := `INSERT INTO profile_parameter (
+profile,
+parameter) VALUES (
+:profile,
+:parameter) RETURNING profile_profile,last_updated`
+       return query
+}
+
+func (pp *TOProfileParameter) Read(db *sqlx.DB, parameters map[string]string, 
user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+       var rows *sqlx.Rows
+
+       privLevel := user.PrivLevel
+
+       // Query Parameters to Database Query column mappings
+       // see the fields mapped in the SQL query
+       queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+               "profile":      dbhelpers.WhereColumnInfo{"pp.profile", 
api.IsInt},
+               "last_updated": dbhelpers.WhereColumnInfo{"pp.last_updated", 
nil},
+               "name":         dbhelpers.WhereColumnInfo{"p.parameter", nil},
+       }
+
+       where, orderBy, queryValues, errs := 
dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols)
+       if len(errs) > 0 {
+               return nil, errs, tc.DataConflictError
+       }
+
+       query := selectQuery() + where + orderBy
+       log.Debugln("Query is ", query)
+
+       rows, err := db.NamedQuery(query, queryValues)
+       if err != nil {
+               log.Errorf("Error querying Parameters: %v", err)
+               return nil, []error{tc.DBError}, tc.SystemError
+       }
+       defer rows.Close()
+
+       params := []interface{}{}
+       hiddenField := "********"
+       for rows.Next() {
+               var p tc.ParameterNullable
+               if err = rows.StructScan(&p); err != nil {
+                       log.Errorf("error parsing pp rows: %v", err)
+                       return nil, []error{tc.DBError}, tc.SystemError
+               }
+               var isSecure bool
+               if p.Secure != nil {
+                       isSecure = *p.Secure
+               }
+
+               if isSecure && (privLevel < auth.PrivLevelAdmin) {
+                       p.Value = &hiddenField
+               }
+               params = append(params, p)
+       }
+
+       return params, []error{}, tc.NoError
+
+}
+
+//The TOProfileParameter implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a parameter 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 (pp *TOProfileParameter) Update(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       log.Debugf("about to run exec query: %s with parameter: %++v", 
updateQuery(), pp)
+       resultRows, err := tx.NamedQuery(updateQuery(), pp)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a parameter with " + 
err.Error()), eType
+                       }
+                       return err, eType
+               }
+               log.Errorf("received error: %++v from update execution", err)
+               return tc.DBError, tc.SystemError
+       }
+       defer resultRows.Close()
+
+       // get LastUpdated field -- updated by trigger in the db
+       var lastUpdated tc.TimeNoMod
+       rowsAffected := 0
+       for resultRows.Next() {
+               rowsAffected++
+               if err := resultRows.Scan(&lastUpdated); err != nil {
+                       log.Error.Printf("could not scan lastUpdated from 
insert: %s\n", err)
+                       return tc.DBError, tc.SystemError
+               }
+       }
+       log.Debugf("lastUpdated: %++v", lastUpdated)
+       pp.LastUpdated = &lastUpdated
+       if rowsAffected != 1 {
+               if rowsAffected < 1 {
+                       return errors.New("no parameter found with this id"), 
tc.DataMissingError
+               }
+               return fmt.Errorf("this update affected too many rows: %d", 
rowsAffected), tc.SystemError
+       }
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+//The Parameter implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper 
errorType
+func (pp *TOProfileParameter) Delete(db *sqlx.DB, user auth.CurrentUser) 
(error, tc.ApiErrorType) {
+       rollbackTransaction := true
+       tx, err := db.Beginx()
+       defer func() {
+               if tx == nil || !rollbackTransaction {
+                       return
+               }
+               err := tx.Rollback()
+               if err != nil {
+                       log.Errorln(errors.New("rolling back transaction: " + 
err.Error()))
+               }
+       }()
+
+       if err != nil {
+               log.Error.Printf("could not begin transaction: %v", err)
+               return tc.DBError, tc.SystemError
+       }
+       log.Debugf("about to run exec query: %s with parameter: %++v", 
deleteQuery(), pp)
+       result, err := tx.NamedExec(deleteQuery(), pp)
+       if err != nil {
+               log.Errorf("received error: %++v from delete execution", err)
+               return tc.DBError, tc.SystemError
+       }
+       rowsAffected, err := result.RowsAffected()
+       if err != nil {
+               return tc.DBError, tc.SystemError
+       }
+       if rowsAffected < 1 {
+               return errors.New("no parameter with that id found"), 
tc.DataMissingError
+       }
+       if rowsAffected > 1 {
+               return fmt.Errorf("this create affected too many rows: %d", 
rowsAffected), tc.SystemError
+       }
+
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+func selectQuery() string {
+
+       query := `SELECT
+p.last_updated,
+p.profile,
+p.id,
+p.parameter
+FROM profile_parameter p`
+       return query
+}
+
+func updateQuery() string {
+       query := `UPDATE
+profile_parameter SET
+profile=:profile,
+parameter=:parameter
+WHERE id=:id RETURNING last_updated`
+       return query
+}
+
+func deleteQuery() string {
+       query := `DELETE FROM profile_parameter
+WHERE id=:id`
+       return query
+}
diff --git a/traffic_ops/traffic_ops_golang/region/regions_test.go 
b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters_test.go
similarity index 60%
copy from traffic_ops/traffic_ops_golang/region/regions_test.go
copy to 
traffic_ops/traffic_ops_golang/profileparameter/profile_parameters_test.go
index 3f08cc1..ced1a11 100644
--- a/traffic_ops/traffic_ops_golang/region/regions_test.go
+++ b/traffic_ops/traffic_ops_golang/profileparameter/profile_parameters_test.go
@@ -1,4 +1,4 @@
-package region
+package profileparameter
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -32,24 +32,29 @@ import (
        sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
 )
 
-func getTestRegions() []tc.Region {
-       regions := []tc.Region{}
-       testCase := tc.Region{
-               DivisionName: "west",
-               ID:           1,
-               Name:         "region1",
-               LastUpdated:  tc.TimeNoMod{Time: time.Now()},
+func getTestProfileParameters() []tc.ProfileParameterNullable {
+       pps := []tc.ProfileParameterNullable{}
+       lastUpdated := tc.TimeNoMod{}
+       lastUpdated.Scan(time.Now())
+       profile := 1
+       parameter := 1
+
+       pp := tc.ProfileParameterNullable{
+               LastUpdated: &lastUpdated,
+               Profile:     &profile,
+               Parameter:   &parameter,
        }
-       regions = append(regions, testCase)
+       pps = append(pps, pp)
 
-       testCase2 := testCase
-       testCase2.Name = "region2"
-       regions = append(regions, testCase2)
+       pp2 := pp
+       pp2.Profile = &profile
+       pp2.Parameter = &parameter
+       pps = append(pps, pp2)
 
-       return regions
+       return pps
 }
 
-func TestReadRegions(t *testing.T) {
+func TestGetProfileParameters(t *testing.T) {
        mockDB, mock, err := sqlmock.New()
        if err != nil {
                t.Fatalf("an error '%s' was not expected when opening a stub 
database connection", err)
@@ -59,50 +64,48 @@ func TestReadRegions(t *testing.T) {
        db := sqlx.NewDb(mockDB, "sqlmock")
        defer db.Close()
 
-       refType := GetRefType()
-
-       testRegions := getTestRegions()
-       cols := test.ColsFromStructByTag("db", tc.Region{})
+       testPPs := getTestProfileParameters()
+       cols := test.ColsFromStructByTag("db", tc.ProfileParameterNullable{})
        rows := sqlmock.NewRows(cols)
 
-       for _, ts := range testRegions {
+       for _, ts := range testPPs {
                rows = rows.AddRow(
-                       ts.Division,
-                       ts.ID,
                        ts.LastUpdated,
-                       ts.Name,
+                       ts.Profile,
+                       ts.Parameter,
                )
        }
        mock.ExpectQuery("SELECT").WillReturnRows(rows)
-       v := map[string]string{"dsId": "1"}
+       v := map[string]string{"profile": "1"}
 
-       regions, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+       pps, errs, _ := refType.Read(db, v, auth.CurrentUser{})
        if len(errs) > 0 {
-               t.Errorf("region.Read expected: no errors, actual: %v", errs)
+               t.Errorf("profileparameter.Read expected: no errors, actual: 
%v", errs)
        }
 
-       if len(regions) != 2 {
-               t.Errorf("region.Read expected: len(regions) == 2, actual: %v", 
len(regions))
+       if len(pps) != 2 {
+               t.Errorf("profileparameter.Read expected: len(pps) == 2, 
actual: %v", len(pps))
        }
+
 }
 
 func TestInterfaces(t *testing.T) {
        var i interface{}
-       i = &TORegion{}
+       i = &TOProfileParameter{}
 
        if _, ok := i.(api.Creator); !ok {
-               t.Errorf("Region must be Creator")
+               t.Errorf("ProfileParameter must be Creator")
        }
        if _, ok := i.(api.Reader); !ok {
-               t.Errorf("Region must be Reader")
+               t.Errorf("ProfileParameter must be Reader")
        }
        if _, ok := i.(api.Updater); !ok {
-               t.Errorf("Region must be Updater")
+               t.Errorf("ProfileParameter must be Updater")
        }
        if _, ok := i.(api.Deleter); !ok {
-               t.Errorf("Region must be Deleter")
+               t.Errorf("ProfileParameter must be Deleter")
        }
        if _, ok := i.(api.Identifier); !ok {
-               t.Errorf("Region must be Identifier")
+               t.Errorf("ProfileParameter must be Identifier")
        }
 }
diff --git a/traffic_ops/traffic_ops_golang/region/regions_test.go 
b/traffic_ops/traffic_ops_golang/region/regions_test.go
index 3f08cc1..0720840 100644
--- a/traffic_ops/traffic_ops_golang/region/regions_test.go
+++ b/traffic_ops/traffic_ops_golang/region/regions_test.go
@@ -74,7 +74,7 @@ func TestReadRegions(t *testing.T) {
                )
        }
        mock.ExpectQuery("SELECT").WillReturnRows(rows)
-       v := map[string]string{"dsId": "1"}
+       v := map[string]string{"id": "1"}
 
        regions, errs, _ := refType.Read(db, v, auth.CurrentUser{})
        if len(errs) > 0 {

-- 
To stop receiving notification emails like this one, please contact
mitchell...@apache.org.

Reply via email to