This is an automated email from the ASF dual-hosted git repository. dangogh pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-trafficcontrol.git
commit a0bdfc89a04295c1a2360a1dfb6f68f58f3de5f8 Author: Dewayne Richardson <dewr...@apache.org> AuthorDate: Wed Feb 14 14:36:49 2018 -0700 refactored the asns to refer to the CRUD interface framework --- traffic_ops/traffic_ops_golang/asn/asns.go | 326 +++++++++++++++++++++ .../traffic_ops_golang/{ => asn}/asns_test.go | 2 +- traffic_ops/traffic_ops_golang/asns.go | 125 -------- traffic_ops/traffic_ops_golang/routes.go | 8 +- 4 files changed, 334 insertions(+), 127 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/asn/asns.go b/traffic_ops/traffic_ops_golang/asn/asns.go new file mode 100644 index 0000000..59ea4cc --- /dev/null +++ b/traffic_ops/traffic_ops_golang/asn/asns.go @@ -0,0 +1,326 @@ +package asn + +/* + * 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/jmoiron/sqlx" + "github.com/lib/pq" +) + +// ASNsPrivLevel ... +const ASNsPrivLevel = 10 + +//we need a type alias to define functions on +type TOASN tc.ASN + +//the refType is passed into the handlers where a copy of its type is used to decode the json. +var refType = TOASN(tc.ASN{}) + +func GetRefType() *TOASN { + return &refType +} + +//Implementation of the Identifier, Validator interface functions +func (asn *TOASN) GetID() int { + return asn.ID +} + +func (asn *TOASN) GetAuditName() string { + return strconv.Itoa(asn.ASN) +} + +func (asn *TOASN) GetType() string { + return "asn" +} + +func (asn *TOASN) SetID(i int) { + asn.ID = i +} + +func (asn *TOASN) Validate(db *sqlx.DB) []error { + errs := []error{} + if asn.ASN < 1 { + errs = append(errs, errors.New(`ASN 'asn' is required.`)) + } + return errs +} + +//The TOASN implementation of the Creator interface +//all implementations of Creator should use transactions and return the proper errorType +//ParsePQUniqueConstraintError is used to determine if a asn 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 asn and have +//to be added to the struct +func (asn *TOASN) 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(), asn) + if err != nil { + if pqErr, ok := err.(*pq.Error); ok { + err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr) + if eType == tc.DataConflictError { + return errors.New("a asn with " + err.Error()), eType + } + return err, eType + } else { + log.Errorf("received non pq error: %++v from create execution", err) + return tc.DBError, tc.SystemError + } + } + defer resultRows.Close() + + var id int + var lastUpdated tc.Time + rowsAffected := 0 + for resultRows.Next() { + rowsAffected++ + if err := resultRows.Scan(&id, &lastUpdated); err != nil { + log.Error.Printf("could not scan id from insert: %s\n", err) + return tc.DBError, tc.SystemError + } + } + if rowsAffected == 0 { + err = errors.New("no asn was inserted, no id was returned") + log.Errorln(err) + return tc.DBError, tc.SystemError + } else if rowsAffected > 1 { + err = errors.New("too many ids returned from asn insert") + log.Errorln(err) + return tc.DBError, tc.SystemError + } + asn.SetID(id) + asn.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 (asn *TOASN) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) { + var rows *sqlx.Rows + + // Query Parameters to Database Query column mappings + // see the fields mapped in the SQL query + queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{ + "domainName": dbhelpers.WhereColumnInfo{"domain_name", nil}, + "dnssecEnabled": dbhelpers.WhereColumnInfo{"dnssec_enabled", nil}, + "id": dbhelpers.WhereColumnInfo{"id", api.IsInt}, + "name": dbhelpers.WhereColumnInfo{"name", 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 ASNs: %v", err) + return nil, []error{tc.DBError}, tc.SystemError + } + defer rows.Close() + + ASNs := []interface{}{} + for rows.Next() { + var s tc.ASN + if err = rows.StructScan(&s); err != nil { + log.Errorf("error parsing ASN rows: %v", err) + return nil, []error{tc.DBError}, tc.SystemError + } + ASNs = append(ASNs, s) + } + + return ASNs, []error{}, tc.NoError +} + +func selectQuery() string { + query := `SELECT +id, +asn, +last_updated, +cachegroup + +FROM asn a` + return query +} + +//The TOASN implementation of the Updater interface +//all implementations of Updater should use transactions and return the proper errorType +//ParsePQUniqueConstraintError is used to determine if a asn 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 (asn *TOASN) 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 asn: %++v", updateQuery(), asn) + resultRows, err := tx.NamedQuery(updateQuery(), asn) + if err != nil { + if pqErr, ok := err.(*pq.Error); ok { + err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr) + if eType == tc.DataConflictError { + return errors.New("a asn with " + err.Error()), eType + } + return err, eType + } else { + log.Errorf("received error: %++v from update execution", err) + return tc.DBError, tc.SystemError + } + } + defer resultRows.Close() + + var lastUpdated tc.Time + 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) + asn.LastUpdated = lastUpdated + if rowsAffected != 1 { + if rowsAffected < 1 { + return errors.New("no asn found with this id"), tc.DataMissingError + } else { + 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 ASN implementation of the Deleter interface +//all implementations of Deleter should use transactions and return the proper errorType +func (asn *TOASN) 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 asn: %++v", deleteQuery(), asn) + result, err := tx.NamedExec(deleteQuery(), asn) + 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 { + if rowsAffected < 1 { + return errors.New("no asn with that id found"), tc.DataMissingError + } else { + 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 insertQuery() string { + query := `INSERT INTO asn ( +asn, +cachegroup) +VALUES ( +:asn, +:cachegroup) RETURNING id,last_updated` + return query +} + +func updateQuery() string { + query := `UPDATE +asn SET +asn=:asn, +cachegroup=:cachegroup, +WHERE id=:id RETURNING last_updated` + return query +} + +func deleteQuery() string { + query := `DELETE FROM asn +WHERE id=:id` + return query +} diff --git a/traffic_ops/traffic_ops_golang/asns_test.go b/traffic_ops/traffic_ops_golang/asn/asns_test.go similarity index 99% rename from traffic_ops/traffic_ops_golang/asns_test.go rename to traffic_ops/traffic_ops_golang/asn/asns_test.go index 64101c0..6501a72 100644 --- a/traffic_ops/traffic_ops_golang/asns_test.go +++ b/traffic_ops/traffic_ops_golang/asn/asns_test.go @@ -1,4 +1,4 @@ -package main +package asn /* * Licensed to the Apache Software Foundation (ASF) under one diff --git a/traffic_ops/traffic_ops_golang/asns.go b/traffic_ops/traffic_ops_golang/asns.go deleted file mode 100644 index 86a2fa7..0000000 --- a/traffic_ops/traffic_ops_golang/asns.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -/* - * 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 ( - "encoding/json" - "fmt" - "net/http" - - "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/dbhelpers" - "github.com/jmoiron/sqlx" -) - -// ASNsPrivLevel ... -const ASNsPrivLevel = 10 - -// ASNsHandler ... -func ASNsHandler(db *sqlx.DB) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - handleErrs := tc.GetHandleErrorsFunc(w, r) - - params, err := api.GetCombinedParams(r) - if err != nil { - log.Errorf("unable to get parameters from request: %s", err) - handleErrs(http.StatusInternalServerError, err) - } - - resp, errs, errType := getASNsResponse(params, db) - if len(errs) > 0 { - tc.HandleErrorsWithType(errs, errType, handleErrs) - return - } - - respBts, err := json.Marshal(resp) - if err != nil { - handleErrs(http.StatusInternalServerError, err) - return - } - - w.Header().Set("Content-Type", "application/json") - fmt.Fprintf(w, "%s", respBts) - } -} - -func getASNsResponse(parameters map[string]string, db *sqlx.DB) (*tc.ASNsResponse, []error, tc.ApiErrorType) { - asns, errs, errType := getASNs(parameters, db) - if len(errs) > 0 { - return nil, errs, errType - } - - resp := tc.ASNsResponse{ - Response: asns, - } - return &resp, nil, tc.NoError -} - -func getASNs(parameters map[string]string, db *sqlx.DB) ([]tc.ASN, []error, tc.ApiErrorType) { - var rows *sqlx.Rows - var err error - - // Query Parameters to Database Query column mappings - // see the fields mapped in the SQL query - queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{ - "asn": dbhelpers.WhereColumnInfo{"a.asn", api.IsInt}, - "id": dbhelpers.WhereColumnInfo{"a.id", api.IsInt}, - "cachegroup": dbhelpers.WhereColumnInfo{"cg.id", api.IsInt}, - } - - where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols) - if len(errs) > 0 { - return nil, errs, tc.DataConflictError - } - query := selectASNsQuery() + where + orderBy - log.Debugln("Query is ", query) - - rows, err = db.NamedQuery(query, queryValues) - if err != nil { - return nil, []error{err}, tc.SystemError - } - defer rows.Close() - - ASNs := []tc.ASN{} - for rows.Next() { - var s tc.ASN - if err = rows.StructScan(&s); err != nil { - return nil, []error{fmt.Errorf("getting ASNs: %v", err)}, tc.SystemError - } - ASNs = append(ASNs, s) - } - return ASNs, nil, tc.NoError -} - -func selectASNsQuery() string { - - query := `SELECT -a.asn, -cg.name as cachegroup, -a.cachegroup as cachegroup_id, -a.id, -a.last_updated - -FROM asn a -JOIN cachegroup cg ON cg.id = a.cachegroup` - return query -} diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go index 8da3ce0..58f2a54 100644 --- a/traffic_ops/traffic_ops_golang/routes.go +++ b/traffic_ops/traffic_ops_golang/routes.go @@ -30,6 +30,7 @@ import ( tclog "github.com/apache/incubator-trafficcontrol/lib/go-log" "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api" + "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/asn" "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth" "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/cdn" dsrequest "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice/request" @@ -55,7 +56,12 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { routes := []Route{ //ASNs - {1.2, http.MethodGet, `asns/?(\.json)?$`, ASNsHandler(d.DB), ASNsPrivLevel, Authenticated, nil}, + {1.3, http.MethodGet, `asns/?(\.json)?$`, api.ReadHandler(asn.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil}, + {1.3, http.MethodGet, `asns/{id}$`, api.ReadHandler(asn.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil}, + {1.3, http.MethodPut, `asns/{id}$`, api.UpdateHandler(asn.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil}, + {1.3, http.MethodPost, `asns/?$`, api.CreateHandler(asn.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil}, + {1.3, http.MethodDelete, `asns/{id}$`, api.DeleteHandler(asn.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil}, + //CDNs // explicitly passed to legacy system until fully implemented. Auth handled by legacy system. {1.2, http.MethodGet, `cdns/capacity$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}}, -- To stop receiving notification emails like this one, please contact dang...@apache.org.