You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@trafficcontrol.apache.org by GitBox <gi...@apache.org> on 2018/02/22 16:57:12 UTC

[GitHub] dangogh closed pull request #1890: Updated /asns, /cdns and /divisions to use the CRUD interface

dangogh closed pull request #1890: Updated /asns, /cdns and /divisions to use the CRUD interface
URL: https://github.com/apache/incubator-trafficcontrol/pull/1890
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/traffic_ops/client/asn.go b/traffic_ops/client/asn.go
new file mode 100644
index 0000000000..fefeeae32a
--- /dev/null
+++ b/traffic_ops/client/asn.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 client
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"net/http"
+
+	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+const (
+	API_v2_ASNs = "/api/1.3/asns"
+)
+
+// Create a ASN
+func (to *Session) CreateASN(entity tc.ASN) (tc.Alerts, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(entity)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return tc.Alerts{}, reqInf, err
+	}
+	resp, remoteAddr, err := to.request(http.MethodPost, API_v2_ASNs, 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 ASN by ID
+func (to *Session) UpdateASNByID(id int, entity tc.ASN) (tc.Alerts, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(entity)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return tc.Alerts{}, reqInf, err
+	}
+	route := fmt.Sprintf("%s/%d", API_v2_ASNs, id)
+	resp, remoteAddr, err := to.request(http.MethodPut, route, 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 ASNs
+func (to *Session) GetASNs() ([]tc.ASN, ReqInf, error) {
+	resp, remoteAddr, err := to.request(http.MethodGet, API_v2_ASNs, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.ASNsResponse
+	err = json.NewDecoder(resp.Body).Decode(&data)
+	return data.Response, reqInf, nil
+}
+
+// GET a ASN by the id
+func (to *Session) GetASNByID(id int) ([]tc.ASN, ReqInf, error) {
+	route := fmt.Sprintf("%s/%d", API_v2_ASNs, id)
+	resp, remoteAddr, err := to.request(http.MethodGet, route, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.ASNsResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// GET an ASN by the asn number
+func (to *Session) GetASNByASN(asn int) ([]tc.ASN, ReqInf, error) {
+	url := fmt.Sprintf("%s?asn=%d", API_v2_ASNs, asn)
+	resp, remoteAddr, err := to.request(http.MethodGet, url, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.ASNsResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// DELETE an ASN by asn number
+func (to *Session) DeleteASNByASN(asn int) (tc.Alerts, ReqInf, error) {
+	route := fmt.Sprintf("%s/asn/%d", API_v2_ASNs, asn)
+	resp, remoteAddr, err := to.request(http.MethodDelete, route, 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/client/cdn.go b/traffic_ops/client/cdn.go
index 7ab3c909ba..6bf2d93de8 100644
--- a/traffic_ops/client/cdn.go
+++ b/traffic_ops/client/cdn.go
@@ -117,9 +117,9 @@ func (to *Session) GetCDNByName(name string) ([]tc.CDN, ReqInf, error) {
 	return data.Response, reqInf, nil
 }
 
-// DELETE a CDN by CDN name
-func (to *Session) DeleteCDNByName(name string) (tc.Alerts, ReqInf, error) {
-	route := fmt.Sprintf("%s/name/%s", API_v2_CDNs, name)
+// DELETE a CDN by ID
+func (to *Session) DeleteCDNByID(id int) (tc.Alerts, ReqInf, error) {
+	route := fmt.Sprintf("%s/%d", API_v2_CDNs, id)
 	resp, remoteAddr, err := to.request(http.MethodDelete, route, nil)
 	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
 	if err != nil {
diff --git a/traffic_ops/client/division.go b/traffic_ops/client/division.go
new file mode 100644
index 0000000000..28832f9c3a
--- /dev/null
+++ b/traffic_ops/client/division.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 client
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+	"net/http"
+
+	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+const (
+	API_v2_Divisions = "/api/1.3/divisions"
+)
+
+// Create a Division
+func (to *Session) CreateDivision(division tc.Division) (tc.Alerts, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(division)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return tc.Alerts{}, reqInf, err
+	}
+	resp, remoteAddr, err := to.request(http.MethodPost, API_v2_Divisions, 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 Division by ID
+func (to *Session) UpdateDivisionByID(id int, division tc.Division) (tc.Alerts, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(division)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return tc.Alerts{}, reqInf, err
+	}
+	route := fmt.Sprintf("%s/%d", API_v2_Divisions, id)
+	resp, remoteAddr, err := to.request(http.MethodPut, route, 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 Divisions
+func (to *Session) GetDivisions() ([]tc.Division, ReqInf, error) {
+	resp, remoteAddr, err := to.request(http.MethodGet, API_v2_Divisions, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.DivisionsResponse
+	err = json.NewDecoder(resp.Body).Decode(&data)
+	return data.Response, reqInf, nil
+}
+
+// GET a Division by the Division id
+func (to *Session) GetDivisionByID(id int) ([]tc.Division, ReqInf, error) {
+	route := fmt.Sprintf("%s/%d", API_v2_Divisions, id)
+	resp, remoteAddr, err := to.request(http.MethodGet, route, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.DivisionsResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// GET a Division by the Division name
+func (to *Session) GetDivisionByName(name string) ([]tc.Division, ReqInf, error) {
+	url := fmt.Sprintf("%s?name=%s", API_v2_Divisions, name)
+	resp, remoteAddr, err := to.request(http.MethodGet, url, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data tc.DivisionsResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// DELETE a Division by Division name
+func (to *Session) DeleteDivisionByName(name string) (tc.Alerts, ReqInf, error) {
+	route := fmt.Sprintf("%s/name/%s", API_v2_Divisions, name)
+	resp, remoteAddr, err := to.request(http.MethodDelete, route, 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/tc-fixtures.json b/traffic_ops/testing/api/tc-fixtures.json
new file mode 100644
index 0000000000..4154fa075d
--- /dev/null
+++ b/traffic_ops/testing/api/tc-fixtures.json
@@ -0,0 +1,110 @@
+{
+    "asns": [
+        {
+            "asn": 1111,
+            "cachegroup": "cachegroup1",
+            "cachegroupId": 1
+        },
+        {
+            "asn": 2222,
+            "cachegroup": "cachegroup2",
+            "cachegroupId": 2
+        }
+    ],
+    "cachegroups": [
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "parentCacheGroup",
+            "parentCacheGroupName": null
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "secondaryCacheGroup",
+            "parentCacheGroupName": null
+        },
+        {
+            "latitude": 0,
+            "longitude": 0,
+            "name": "cachegroup1",
+            "parentCacheGroupName": "parentCacheGroup",
+            "secondaryCacheGroupName": "parentCacheGroup",
+            "shortName": "cg1"
+        },
+        {
+            "latitude": 24.1234,
+            "longitude": -121.1234,
+            "name": "cachegroup2",
+            "parentCacheGroupName": "secondaryCacheGroup",
+            "secondaryCacheGroupName": "secondaryCacheGroup",
+            "shortName": "cg2"
+        }
+    ],
+    "cdns": [
+        {
+            "dnssecEnabled": true,
+            "domainName": "test.cdn1.net",
+            "name": "cdn1"
+        },
+        {
+            "dnssecEnabled": false,
+            "domainName": "test.cdn2.net",
+            "name": "cdn2"
+        },
+        {
+            "dnssecEnabled": true,
+            "domainName": "test.cdn3.net",
+            "name": "cdn3"
+        }
+    ],
+    "deliveryServices": [
+        {
+            "active": false,
+            "dscp": 40,
+            "tenantName": "tenant1",
+            "xmlId": "ds1"
+        },
+        {
+            "active": false,
+            "dscp": 40,
+            "tenantName": "tenant2",
+            "xmlId": "ds2"
+        }
+    ],
+    "divisions": [
+        {
+            "name": "division1"
+        },
+        {
+            "name": "division2"
+        }
+    ],
+    "regions": [
+        {
+            "divisionName": "division1",
+            "name": "region1"
+        },
+        {
+            "divisionName": "division2",
+            "name": "region2"
+        }
+    ],
+    "tenants": [
+        {
+            "active": true,
+            "name": "root",
+            "parentTenantName": null
+        },
+        {
+            "active": true,
+            "name": "tenant1",
+            "parentTenantName": "root"
+        },
+        {
+            "active": false,
+            "name": "tenant2",
+            "parentTenantName": "root"
+        }
+    ]
+}
diff --git a/traffic_ops/testing/api/v13/cdns_test.go b/traffic_ops/testing/api/v13/cdns_test.go
index 2e425b1c81..1a7d9a870d 100644
--- a/traffic_ops/testing/api/v13/cdns_test.go
+++ b/traffic_ops/testing/api/v13/cdns_test.go
@@ -84,18 +84,25 @@ func GetTestCDNs(t *testing.T) {
 
 func DeleteTestCDNs(t *testing.T) {
 
-	secondCDN := testData.CDNs[1]
-	resp, _, err := TOSession.DeleteCDNByName(secondCDN.Name)
+	cdn := testData.CDNs[1]
+	// Retrieve the CDN by name so we can get the id for the Update
+	resp, _, err := TOSession.GetCDNByName(cdn.Name)
+	if err != nil {
+		t.Errorf("cannot GET CDN by name: %v - %v\n", cdn.Name, err)
+	}
+	respCDN := resp[0]
+
+	delResp, _, err := TOSession.DeleteCDNByID(respCDN.ID)
 	if err != nil {
-		t.Errorf("cannot DELETE CDN by name: %v - %v\n", err, resp)
+		t.Errorf("cannot DELETE CDN by name: %v - %v\n", err, delResp)
 	}
 
 	// Retrieve the CDN to see if it got deleted
-	cdns, _, err := TOSession.GetCDNByName(secondCDN.Name)
+	cdns, _, err := TOSession.GetCDNByName(cdn.Name)
 	if err != nil {
 		t.Errorf("error deleting CDN name: %s\n", err.Error())
 	}
 	if len(cdns) > 0 {
-		t.Errorf("expected CDN name: %s to be deleted\n", secondCDN.Name)
+		t.Errorf("expected CDN name: %s to be deleted\n", cdn.Name)
 	}
 }
diff --git a/traffic_ops/traffic_ops_golang/api/change_log.go b/traffic_ops/traffic_ops_golang/api/change_log.go
index 53d41285a1..73c06fceba 100644
--- a/traffic_ops/traffic_ops_golang/api/change_log.go
+++ b/traffic_ops/traffic_ops_golang/api/change_log.go
@@ -44,7 +44,7 @@ const (
 	Deleted   = "Deleted"
 )
 
-func InsertChangeLog(level string, action string, i Identifier, user auth.CurrentUser, db *sqlx.DB) error {
+func CreateChangeLog(level string, action string, i Identifier, user auth.CurrentUser, db *sqlx.DB) error {
 	query := `INSERT INTO log (level, message, tm_user) VALUES ($1, $2, $3)`
 	id, _ := i.GetID()
 	message := action + " " + i.GetType() + ": " + i.GetAuditName() + " id: " + strconv.Itoa(id)
diff --git a/traffic_ops/traffic_ops_golang/api/change_log_test.go b/traffic_ops/traffic_ops_golang/api/change_log_test.go
index 62e69c7454..bfbcab2ba9 100644
--- a/traffic_ops/traffic_ops_golang/api/change_log_test.go
+++ b/traffic_ops/traffic_ops_golang/api/change_log_test.go
@@ -44,7 +44,7 @@ func (i *testIdentifier) GetAuditName() string {
 	return "testerInstance"
 }
 
-func TestInsertChangeLog(t *testing.T) {
+func TestCreateChangeLog(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)
@@ -60,7 +60,7 @@ func TestInsertChangeLog(t *testing.T) {
 
 	mock.ExpectExec("INSERT").WithArgs(ApiChange, expectedMessage, 1).WillReturnResult(sqlmock.NewResult(1, 1))
 	user := auth.CurrentUser{ID: 1}
-	err = InsertChangeLog(ApiChange, Created, &i, user, db)
+	err = CreateChangeLog(ApiChange, Created, &i, user, db)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
index 89208fad82..903fa9f3d7 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go
@@ -220,7 +220,7 @@ func UpdateHandler(typeRef Updater, db *sqlx.DB) http.HandlerFunc {
 			return
 		}
 		//auditing here
-		InsertChangeLog(ApiChange, Updated, u, *user, db)
+		CreateChangeLog(ApiChange, Updated, u, *user, db)
 		//form response to send across the wire
 		resp := struct {
 			Response interface{} `json:"response"`
@@ -291,7 +291,7 @@ func DeleteHandler(typeRef Deleter, db *sqlx.DB) http.HandlerFunc {
 			return
 		}
 		//audit here
-		InsertChangeLog(ApiChange, Deleted, d, *user, db)
+		CreateChangeLog(ApiChange, Deleted, d, *user, db)
 		//
 		resp := struct {
 			tc.Alerts
@@ -308,7 +308,7 @@ func DeleteHandler(typeRef Deleter, db *sqlx.DB) http.HandlerFunc {
 	}
 }
 
-//this creates a handler function from the pointer to a struct implementing the Inserter interface
+//this creates a handler function from the pointer to a struct implementing the Creator interface
 //it must be immediately assigned to a local variable
 //   this generic handler encapsulates the logic for handling:
 //   *fetching the id from the path parameter
@@ -316,7 +316,7 @@ func DeleteHandler(typeRef Deleter, db *sqlx.DB) http.HandlerFunc {
 //   *decoding and validating the struct
 //   *change log entry
 //   *forming and writing the body over the wire
-func CreateHandler(typeRef Inserter, db *sqlx.DB) http.HandlerFunc {
+func CreateHandler(typeRef Creator, db *sqlx.DB) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		handleErrs := tc.GetHandleErrorsFunc(w, r)
 
@@ -326,7 +326,7 @@ func CreateHandler(typeRef Inserter, db *sqlx.DB) http.HandlerFunc {
 			handleErrs(http.StatusBadRequest, errs...)
 			return
 		}
-		i := decoded.(Inserter)
+		i := decoded.(Creator)
 		log.Debugf("%++v", i)
 		//now we have a validated local object to insert
 
@@ -351,13 +351,13 @@ func CreateHandler(typeRef Inserter, db *sqlx.DB) http.HandlerFunc {
 			}
 		}
 
-		err, errType := i.Insert(db, *user)
+		err, errType := i.Create(db, *user)
 		if err != nil {
 			tc.HandleErrorsWithType([]error{err}, errType, handleErrs)
 			return
 		}
 
-		InsertChangeLog(ApiChange, Created, i, *user, db)
+		CreateChangeLog(ApiChange, Created, i, *user, db)
 
 		resp := struct {
 			Response interface{} `json:"response"`
diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
index 7f6873c532..51b3aa641e 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go
@@ -62,8 +62,8 @@ func (v *tester) Validate(db *sqlx.DB) []error {
 	return []error{}
 }
 
-//Inserter interface functions
-func (i *tester) Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+//Creator interface functions
+func (i *tester) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
 	return i.error, i.errorType
 }
 
diff --git a/traffic_ops/traffic_ops_golang/api/shared_interfaces.go b/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
index 0cd04baa0c..da23de554c 100644
--- a/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
+++ b/traffic_ops/traffic_ops_golang/api/shared_interfaces.go
@@ -37,8 +37,8 @@ type Identifier interface {
 	GetAuditName() string
 }
 
-type Inserter interface {
-	Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType)
+type Creator interface {
+	Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType)
 	SetID(int)
 	Identifier
 	Validator
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 0000000000..5037e2eeed
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/asn/asns.go
@@ -0,0 +1,331 @@
+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, bool) {
+	return asn.ID, true
+}
+
+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{
+		"asn":        dbhelpers.WhereColumnInfo{"a.asn", nil},
+		"cachegroup": dbhelpers.WhereColumnInfo{"c.name", nil},
+		"id":         dbhelpers.WhereColumnInfo{"a.id", api.IsInt},
+	}
+	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
+a.id,
+a.asn,
+a.last_updated,
+a.cachegroup AS cachegroup_id,
+c.name AS cachegroup
+
+FROM asn a JOIN cachegroup c ON a.cachegroup = c.id`
+	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,
+(SELECT id from cachegroup
+WHERE name=:cachegroup
+))
+RETURNING id,last_updated`
+	return query
+}
+
+func updateQuery() string {
+	query := `UPDATE
+asn SET
+asn=:asn,
+cachegroup=(SELECT id from cachegroup
+WHERE name=: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 70%
rename from traffic_ops/traffic_ops_golang/asns_test.go
rename to traffic_ops/traffic_ops_golang/asn/asns_test.go
index 64101c0dd2..9d967df3cd 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
@@ -24,6 +24,8 @@ import (
 	"time"
 
 	"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/test"
 	"github.com/jmoiron/sqlx"
 
@@ -73,25 +75,35 @@ func TestGetASNs(t *testing.T) {
 	mock.ExpectQuery("SELECT").WillReturnRows(rows)
 	v := map[string]string{"dsId": "1"}
 
-	servers, errs, errType := getASNs(v, db)
+	asns, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+
 	if len(errs) > 0 {
-		t.Errorf("getASNs expected: no errors, actual: %v with type %s", errs, errType.String())
+		t.Errorf("asn.Read expected: no errors, actual: %v", errs)
 	}
 
-	if len(servers) != 2 {
-		t.Errorf("getASNs expected: len(servers) == 1, actual: %v", len(servers))
+	if len(asns) != 2 {
+		t.Errorf("asn.Read expected: len(asns) == 2, actual: %v", len(asns))
 	}
 
 }
 
-type SortableASNs []tc.ASN
+func TestInterfaces(t *testing.T) {
+	var i interface{}
+	i = &TOASN{}
 
-func (s SortableASNs) Len() int {
-	return len(s)
-}
-func (s SortableASNs) Swap(i, j int) {
-	s[i], s[j] = s[j], s[i]
-}
-func (s SortableASNs) Less(i, j int) bool {
-	return s[i].ASN < s[j].ASN
+	if _, ok := i.(api.Creator); !ok {
+		t.Errorf("asn must be creator")
+	}
+	if _, ok := i.(api.Reader); !ok {
+		t.Errorf("asn must be reader")
+	}
+	if _, ok := i.(api.Updater); !ok {
+		t.Errorf("asn must be updater")
+	}
+	if _, ok := i.(api.Deleter); !ok {
+		t.Errorf("asn must be deleter")
+	}
+	if _, ok := i.(api.Identifier); !ok {
+		t.Errorf("asn must be Identifier")
+	}
 }
diff --git a/traffic_ops/traffic_ops_golang/asns.go b/traffic_ops/traffic_ops_golang/asns.go
deleted file mode 100644
index 86a2fa77ba..0000000000
--- 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/cdn/cdns.go b/traffic_ops/traffic_ops_golang/cdn/cdns.go
index 9766c28bce..c1b7510965 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns.go
@@ -71,6 +71,75 @@ func (cdn *TOCDN) Validate(db *sqlx.DB) []error {
 	return errs
 }
 
+//The TOCDN implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a cdn 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 cdn and have
+//to be added to the struct
+func (cdn *TOCDN) 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(), cdn)
+	if err != nil {
+		if pqErr, ok := err.(*pq.Error); ok {
+			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+			if eType == tc.DataConflictError {
+				return errors.New("a cdn 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 cdn 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 cdn insert")
+		log.Errorln(err)
+		return tc.DBError, tc.SystemError
+	}
+	cdn.SetID(id)
+	cdn.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 (cdn *TOCDN) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
 	var rows *sqlx.Rows
 
@@ -87,7 +156,7 @@ func (cdn *TOCDN) Read(db *sqlx.DB, parameters map[string]string, user auth.Curr
 		return nil, errs, tc.DataConflictError
 	}
 
-	query := selectCDNsQuery() + where + orderBy
+	query := selectQuery() + where + orderBy
 	log.Debugln("Query is ", query)
 
 	rows, err := db.NamedQuery(query, queryValues)
@@ -110,18 +179,6 @@ func (cdn *TOCDN) Read(db *sqlx.DB, parameters map[string]string, user auth.Curr
 	return CDNs, []error{}, tc.NoError
 }
 
-func selectCDNsQuery() string {
-	query := `SELECT
-dnssec_enabled,
-domain_name,
-id,
-last_updated,
-name
-
-FROM cdn c`
-	return query
-}
-
 //The TOCDN implementation of the Updater interface
 //all implementations of Updater should use transactions and return the proper errorType
 //ParsePQUniqueConstraintError is used to determine if a cdn with conflicting values exists
@@ -144,8 +201,8 @@ func (cdn *TOCDN) Update(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiError
 		log.Error.Printf("could not begin transaction: %v", err)
 		return tc.DBError, tc.SystemError
 	}
-	log.Debugf("about to run exec query: %s with cdn: %++v", updateCDNQuery(), cdn)
-	resultRows, err := tx.NamedQuery(updateCDNQuery(), cdn)
+	log.Debugf("about to run exec query: %s with cdn: %++v", updateQuery(), cdn)
+	resultRows, err := tx.NamedQuery(updateQuery(), cdn)
 	if err != nil {
 		if pqErr, ok := err.(*pq.Error); ok {
 			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
@@ -187,75 +244,6 @@ func (cdn *TOCDN) Update(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiError
 	return nil, tc.NoError
 }
 
-//The TOCDN implementation of the Inserter interface
-//all implementations of Inserter should use transactions and return the proper errorType
-//ParsePQUniqueConstraintError is used to determine if a cdn 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 cdn and have
-//to be added to the struct
-func (cdn *TOCDN) Insert(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(insertCDNQuery(), cdn)
-	if err != nil {
-		if pqErr, ok := err.(*pq.Error); ok {
-			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
-			if eType == tc.DataConflictError {
-				return errors.New("a cdn 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 cdn 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 cdn insert")
-		log.Errorln(err)
-		return tc.DBError, tc.SystemError
-	}
-	cdn.SetID(id)
-	cdn.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
-}
-
 //The CDN implementation of the Deleter interface
 //all implementations of Deleter should use transactions and return the proper errorType
 func (cdn *TOCDN) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
@@ -275,8 +263,8 @@ func (cdn *TOCDN) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiError
 		log.Error.Printf("could not begin transaction: %v", err)
 		return tc.DBError, tc.SystemError
 	}
-	log.Debugf("about to run exec query: %s with cdn: %++v", deleteCDNQuery(), cdn)
-	result, err := tx.NamedExec(deleteCDNQuery(), cdn)
+	log.Debugf("about to run exec query: %s with cdn: %++v", deleteQuery(), cdn)
+	result, err := tx.NamedExec(deleteQuery(), cdn)
 	if err != nil {
 		log.Errorf("received error: %++v from delete execution", err)
 		return tc.DBError, tc.SystemError
@@ -301,7 +289,19 @@ func (cdn *TOCDN) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiError
 	return nil, tc.NoError
 }
 
-func updateCDNQuery() string {
+func selectQuery() string {
+	query := `SELECT
+dnssec_enabled,
+domain_name,
+id,
+last_updated,
+name
+
+FROM cdn c`
+	return query
+}
+
+func updateQuery() string {
 	query := `UPDATE
 cdn SET
 dnssec_enabled=:dnssec_enabled,
@@ -311,7 +311,7 @@ WHERE id=:id RETURNING last_updated`
 	return query
 }
 
-func insertCDNQuery() string {
+func insertQuery() string {
 	query := `INSERT INTO cdn (
 dnssec_enabled,
 domain_name,
@@ -322,7 +322,7 @@ name) VALUES (
 	return query
 }
 
-func deleteCDNQuery() string {
+func deleteQuery() string {
 	query := `DELETE FROM cdn
 WHERE id=:id`
 	return query
diff --git a/traffic_ops/traffic_ops_golang/cdn/cdns_test.go b/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
index ff60aae1b1..38c9abde95 100644
--- a/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
+++ b/traffic_ops/traffic_ops_golang/cdn/cdns_test.go
@@ -24,6 +24,7 @@ import (
 	"time"
 
 	"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/test"
 	"github.com/jmoiron/sqlx"
@@ -66,8 +67,6 @@ func TestReadCDNs(t *testing.T) {
 	cols := test.ColsFromStructByTag("db", tc.CDN{})
 	rows := sqlmock.NewRows(cols)
 
-	//TODO: drichardson - build helper to add these Rows from the struct values
-	//                    or by CSV if types get in the way
 	for _, ts := range testCDNs {
 		rows = rows.AddRow(
 			ts.DNSSECEnabled,
@@ -89,3 +88,24 @@ func TestReadCDNs(t *testing.T) {
 		t.Errorf("cdn.Read expected: len(servers) == 2, actual: %v", len(servers))
 	}
 }
+
+func TestInterfaces(t *testing.T) {
+	var i interface{}
+	i = &TOCDN{}
+
+	if _, ok := i.(api.Creator); !ok {
+		t.Errorf("cdn must be creator")
+	}
+	if _, ok := i.(api.Reader); !ok {
+		t.Errorf("cdn must be reader")
+	}
+	if _, ok := i.(api.Updater); !ok {
+		t.Errorf("cdn must be updater")
+	}
+	if _, ok := i.(api.Deleter); !ok {
+		t.Errorf("cdn must be deleter")
+	}
+	if _, ok := i.(api.Identifier); !ok {
+		t.Errorf("cdn must be Identifier")
+	}
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index bc26ace1bc..a969b21aec 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -264,14 +264,14 @@ func (ds *TODeliveryService) Update(db *sqlx.DB, user auth.CurrentUser) (error,
 	return nil, tc.NoError
 }
 
-// Insert implements the Inserter interface.
-//all implementations of Inserter should use transactions and return the proper errorType
+// 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 (ds *TODeliveryService) Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+func (ds *TODeliveryService) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
 	tx, err := db.Beginx()
 	defer func() {
 		if tx == nil {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_test.go
index 55dd62e4de..28c50cebfe 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_test.go
@@ -32,8 +32,8 @@ func getInterface(req *TODeliveryService) interface{} {
 func TestInterfaces(t *testing.T) {
 	r := getInterface(&TODeliveryService{})
 
-	if _, ok := r.(api.Inserter); !ok {
-		t.Errorf("DeliveryService must be Inserter")
+	if _, ok := r.(api.Creator); !ok {
+		t.Errorf("DeliveryService must be Creator")
 	}
 	// TODO: uncomment when ds.Reader interface is implemented
 	/*if _, ok := r.(api.Reader); !ok {
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
index 6770be9274..aff46781bd 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests.go
@@ -267,14 +267,14 @@ func (req *TODeliveryServiceRequest) Update(db *sqlx.DB, user auth.CurrentUser)
 	return nil, tc.NoError
 }
 
-// Insert implements the tc.Inserter interface
-//all implementations of Inserter should use transactions and return the proper errorType
+// Creator implements the tc.Creator interface
+//all implementations of Creator should use transactions and return the proper errorType
 //ParsePQUniqueConstraintError is used to determine if a request 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 request and have
 //to be added to the struct
-func (req *TODeliveryServiceRequest) Insert(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+func (req *TODeliveryServiceRequest) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
 	if req == nil {
 		return errors.New("nil deliveryservice_request"), tc.SystemError
 	}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
index 103a9c6813..e038f69c77 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/request/requests_test.go
@@ -32,8 +32,8 @@ func TestInterfaces(t *testing.T) {
 	var i interface{}
 	i = &TODeliveryServiceRequest{}
 
-	if _, ok := i.(api.Inserter); !ok {
-		t.Errorf("DeliveryServiceRequest must be Inserter")
+	if _, ok := i.(api.Creator); !ok {
+		t.Errorf("DeliveryServiceRequest must be Creator")
 	}
 	if _, ok := i.(api.Reader); !ok {
 		t.Errorf("DeliveryServiceRequest must be Reader")
diff --git a/traffic_ops/traffic_ops_golang/division/divisions.go b/traffic_ops/traffic_ops_golang/division/divisions.go
new file mode 100644
index 0000000000..a461a3c4a2
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/division/divisions.go
@@ -0,0 +1,360 @@
+package division
+
+/*
+ * 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"
+
+	"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"
+)
+
+//we need a type alias to define functions on
+type TODivision tc.Division
+
+//the refType is passed into the handlers where a copy of its type is used to decode the json.
+var refType = TODivision(tc.Division{})
+
+func GetRefType() *TODivision {
+	return &refType
+}
+
+func (division *TODivision) GetAuditName() string {
+	return division.Name
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (division *TODivision) GetID() (int, bool) {
+	return division.ID, true
+}
+
+func (division *TODivision) SetID(i int) {
+	division.ID = i
+}
+func (division *TODivision) GetType() string {
+	return "division"
+}
+
+func (division *TODivision) Validate(db *sqlx.DB) []error {
+	errs := []error{}
+	if len(division.Name) < 1 {
+		errs = append(errs, errors.New(`Division 'name' is required.`))
+	}
+	return errs
+}
+
+//The TODivision implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a division 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 division and have
+//to be added to the struct
+func (division *TODivision) 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(), division)
+	if err != nil {
+		if pqErr, ok := err.(*pq.Error); ok {
+			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+			if eType == tc.DataConflictError {
+				return errors.New("a division 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 division 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 division insert")
+		log.Errorln(err)
+		return tc.DBError, tc.SystemError
+	}
+	division.SetID(id)
+	division.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 (division *TODivision) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+	// Query Parameters to Database Query column mappings
+	// see the fields mapped in the SQL query
+	queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+		"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 Divisions: %v", err)
+		return nil, []error{tc.DBError}, tc.SystemError
+	}
+	defer rows.Close()
+
+	divisions := []interface{}{}
+	for rows.Next() {
+		var s tc.Division
+		if err = rows.StructScan(&s); err != nil {
+			log.Errorf("error parsing Division rows: %v", err)
+			return nil, []error{tc.DBError}, tc.SystemError
+		}
+		divisions = append(divisions, s)
+	}
+
+	return divisions, []error{}, tc.NoError
+}
+
+//The TODivision implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a division 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 (division *TODivision) 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 division: %++v", updateQuery(), division)
+	resultRows, err := tx.NamedQuery(updateQuery(), division)
+	if err != nil {
+		if pqErr, ok := err.(*pq.Error); ok {
+			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+			if eType == tc.DataConflictError {
+				return errors.New("a division 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)
+	division.LastUpdated = lastUpdated
+	if rowsAffected != 1 {
+		if rowsAffected < 1 {
+			return errors.New("no division 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
+}
+
+func getDivisionsResponse(params map[string]string, db *sqlx.DB) (*tc.DivisionsResponse, []error, tc.ApiErrorType) {
+	divisions, errs, errType := getDivisions(params, db)
+	if len(errs) > 0 {
+		return nil, errs, errType
+	}
+
+	resp := tc.DivisionsResponse{
+		Response: divisions,
+	}
+	return &resp, nil, tc.NoError
+}
+
+func getDivisions(params map[string]string, db *sqlx.DB) ([]tc.Division, []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
+	queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{
+		"id":   dbhelpers.WhereColumnInfo{"id", api.IsInt},
+		"name": dbhelpers.WhereColumnInfo{"name", nil},
+	}
+
+	where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(params, queryParamsToSQLCols)
+	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 {
+		return nil, []error{err}, tc.SystemError
+	}
+	defer rows.Close()
+
+	o := []tc.Division{}
+	for rows.Next() {
+		var d tc.Division
+		if err = rows.StructScan(&d); err != nil {
+			return nil, []error{fmt.Errorf("getting divisions: %v", err)}, tc.SystemError
+		}
+		o = append(o, d)
+	}
+	return o, nil, tc.NoError
+}
+
+//The Division implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper errorType
+func (division *TODivision) 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 division: %++v", deleteQuery(), division)
+	result, err := tx.NamedExec(deleteQuery(), division)
+	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 division 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 division (
+name) VALUES (:name) RETURNING id,last_updated`
+	return query
+}
+
+func selectQuery() string {
+
+	query := `SELECT
+id,
+last_updated,
+name 
+
+FROM division d`
+	return query
+}
+
+func updateQuery() string {
+	query := `UPDATE
+division SET
+name=:name
+WHERE id=:id RETURNING last_updated`
+	return query
+}
+
+func deleteQuery() string {
+	query := `DELETE FROM division
+WHERE id=:id`
+	return query
+}
diff --git a/traffic_ops/traffic_ops_golang/divisions_test.go b/traffic_ops/traffic_ops_golang/division/divisions_test.go
similarity index 79%
rename from traffic_ops/traffic_ops_golang/divisions_test.go
rename to traffic_ops/traffic_ops_golang/division/divisions_test.go
index 636b3fb514..10ab0078ab 100644
--- a/traffic_ops/traffic_ops_golang/divisions_test.go
+++ b/traffic_ops/traffic_ops_golang/division/divisions_test.go
@@ -1,4 +1,4 @@
-package main
+package division
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -24,6 +24,7 @@ import (
 	"time"
 
 	"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/test"
 	"github.com/jmoiron/sqlx"
 
@@ -81,14 +82,23 @@ func TestGetDivisions(t *testing.T) {
 
 }
 
-type SortableDivisions []tc.Division
+func TestInterfaces(t *testing.T) {
+	var i interface{}
+	i = &TODivision{}
 
-func (s SortableDivisions) Len() int {
-	return len(s)
-}
-func (s SortableDivisions) Swap(i, j int) {
-	s[i], s[j] = s[j], s[i]
-}
-func (s SortableDivisions) Less(i, j int) bool {
-	return s[i].Name < s[j].Name
+	if _, ok := i.(api.Creator); !ok {
+		t.Errorf("division must be creator")
+	}
+	if _, ok := i.(api.Reader); !ok {
+		t.Errorf("division must be reader")
+	}
+	if _, ok := i.(api.Updater); !ok {
+		t.Errorf("division must be updater")
+	}
+	if _, ok := i.(api.Deleter); !ok {
+		t.Errorf("division must be deleter")
+	}
+	if _, ok := i.(api.Identifier); !ok {
+		t.Errorf("division must be Identifier")
+	}
 }
diff --git a/traffic_ops/traffic_ops_golang/divisions.go b/traffic_ops/traffic_ops_golang/divisions.go
deleted file mode 100644
index 10eac0bc99..0000000000
--- a/traffic_ops/traffic_ops_golang/divisions.go
+++ /dev/null
@@ -1,118 +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"
-)
-
-func divisionsHandler(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 := getDivisionsResponse(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 getDivisionsResponse(params map[string]string, db *sqlx.DB) (*tc.DivisionsResponse, []error, tc.ApiErrorType) {
-	divisions, errs, errType := getDivisions(params, db)
-	if len(errs) > 0 {
-		return nil, errs, errType
-	}
-
-	resp := tc.DivisionsResponse{
-		Response: divisions,
-	}
-	return &resp, nil, tc.NoError
-}
-
-func getDivisions(params map[string]string, db *sqlx.DB) ([]tc.Division, []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
-	queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{
-		"id":   dbhelpers.WhereColumnInfo{"id", api.IsInt},
-		"name": dbhelpers.WhereColumnInfo{"name", nil},
-	}
-
-	where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(params, queryParamsToSQLCols)
-	if len(errs) > 0 {
-		return nil, errs, tc.DataConflictError
-	}
-
-	query := selectDivisionsQuery() + 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()
-
-	o := []tc.Division{}
-	for rows.Next() {
-		var d tc.Division
-		if err = rows.StructScan(&d); err != nil {
-			return nil, []error{fmt.Errorf("getting divisions: %v", err)}, tc.SystemError
-		}
-		o = append(o, d)
-	}
-	return o, nil, tc.NoError
-}
-
-func selectDivisionsQuery() string {
-
-	query := `SELECT
-id,
-last_updated,
-name 
-
-FROM division d`
-	return query
-}
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 5936fc8ba3..58f2a54fc2 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -30,9 +30,11 @@ 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"
+	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/division"
 	"github.com/basho/riak-go-client"
 )
 
@@ -54,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{}},
@@ -65,6 +72,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 		{1.2, http.MethodGet, `cdns/routing$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
 
 		{1.2, http.MethodGet, `cdns/{name}/configs/monitoring(\.json)?$`, monitoringHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+
 		//CDN generic handlers:
 		{1.3, http.MethodGet, `cdns/?(\.json)?$`, api.ReadHandler(cdn.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.3, http.MethodGet, `cdns/{id}$`, api.ReadHandler(cdn.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
@@ -85,15 +93,22 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 		{1.3, http.MethodPost, `deliveryservices/{xmlID}/urisignkeys$`, saveDeliveryServiceURIKeysHandler(d.DB, d.Config), auth.PrivLevelAdmin, Authenticated, nil},
 		{1.3, http.MethodPut, `deliveryservices/{xmlID}/urisignkeys$`, saveDeliveryServiceURIKeysHandler(d.DB, d.Config), auth.PrivLevelAdmin, Authenticated, nil},
 		{1.3, http.MethodDelete, `deliveryservices/{xmlID}/urisignkeys$`, removeDeliveryServiceURIKeysHandler(d.DB, d.Config), auth.PrivLevelAdmin, Authenticated, nil},
+
 		//Divisions
-		{1.2, http.MethodGet, `divisions/?(\.json)?$`, divisionsHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.2, http.MethodGet, `divisions/?(\.json)?$`, api.ReadHandler(division.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.3, http.MethodGet, `divisions/{id}$`, api.ReadHandler(division.GetRefType(), d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.3, http.MethodPost, `divisions/?$`, api.CreateHandler(division.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
+
 		//HwInfo
 		{1.2, http.MethodGet, `hwinfo-wip/?(\.json)?$`, hwInfoHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+
 		//Parameters
 		{1.3, http.MethodGet, `parameters/?(\.json)?$`, parametersHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+
 		//Regions
 		{1.2, http.MethodGet, `regions/?(\.json)?$`, regionsHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
 		{1.2, http.MethodGet, `regions/{id}$`, regionsHandler(d.DB), auth.PrivLevelReadOnly, Authenticated, nil},
+
 		//Servers
 		// explicitly passed to legacy system until fully implemented.  Auth handled by legacy system.
 		{1.2, http.MethodGet, `servers/checks$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},


 

----------------------------------------------------------------
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:
users@infra.apache.org


With regards,
Apache Git Services