trafficcontrol-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dang...@apache.org
Subject [incubator-trafficcontrol] 08/23: refactored the asns to refer to the CRUD interface framework
Date Thu, 22 Feb 2018 16:57:17 GMT
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 <dewrich@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
dangogh@apache.org.

Mime
View raw message