dewrich closed pull request #2103: Add a Coordinates API
URL: https://github.com/apache/incubator-trafficcontrol/pull/2103
 
 
   

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/CHANGELOG.md b/CHANGELOG.md
index 54f645012..7ad7d7548 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
   - /api/1.3/deliveryservice_requests `(GET,POST,PUT,DELETE)`
   - /api/1.3/divisions `(GET,POST,PUT,DELETE)`
   - /api/1.3/hwinfos `(GET)`
+  - /api/1.3/coordinates `(GET,POST,PUT,DELETE)`
   - /api/1.3/parameters `(GET,POST,PUT,DELETE)`
   - /api/1.3/profileparameters `(GET,POST,PUT,DELETE)`
   - /api/1.3/phys_locations `(GET,POST,PUT,DELETE)`
diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst
index cd619bce1..6cdf32ed8 100644
--- a/docs/source/api/index.rst
+++ b/docs/source/api/index.rst
@@ -24,4 +24,5 @@ A guide to external RESTful APIs for Traffic Ops
   routes
   v11/index
   v12/index
+  v13/index
   
diff --git a/docs/source/api/v13/coordinate.rst 
b/docs/source/api/v13/coordinate.rst
new file mode 100644
index 000000000..5a18e502b
--- /dev/null
+++ b/docs/source/api/v13/coordinate.rst
@@ -0,0 +1,272 @@
+.. 
+.. 
+.. 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.
+.. 
+
+.. _to-api-v13-coordinate:
+
+Coordinate
+==========
+
+.. _to-api-v13-coordinates-route:
+
+/api/1.3/coordinates
+++++++++++++++++++++
+
+**GET /api/1.3/coordinates**
+
+  Authentication Required: Yes
+
+  Role(s) Required: None
+
+  **Request Query Parameters**
+
+  
+-----------------+----------+---------------------------------------------------+
+  | Name            | Required | Description                                   
    |
+  
+=================+==========+===================================================+
+  | ``id``          | no       | Filter Coordinates by ID.                     
    |
+  
+-----------------+----------+---------------------------------------------------+
+  | ``name``        | no       | Filter Coordinates by name.                   
    |
+  
+-----------------+----------+---------------------------------------------------+
+
+  **Response Properties**
+
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+  | Parameter                         | Type   | Description                   
                                           |
+  
+===================================+========+==========================================================================+
+  | ``id``                            | int    | Local unique identifier for 
the Coordinate                               |
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+  | ``lastUpdated``                   | string | The Time / Date this entry 
was last updated                              |
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+  | ``latitude``                      | float  | Latitude of the Coordinate    
                                           |
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+  | ``longitude``                     | float  | Longitude of the Coordinate   
                                           |
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+  | ``name``                          | string | The name of the Coordinate    
                                           |
+  
+-----------------------------------+--------+--------------------------------------------------------------------------+
+
+  **Response Example** ::
+
+    {
+     "response": [
+        {
+           "id": 21,
+           "lastUpdated": "2012-09-25 20:27:28",
+           "latitude": 0,
+           "longitude": 0,
+           "name": "dc-chicago"
+        },
+        {
+           "id": 22,
+           "lastUpdated": "2012-09-25 20:27:28",
+           "latitude": 0,
+           "longitude": 0,
+           "name": "dc-chicago-1"
+        }
+     ]
+    }
+
+|
+
+**POST /api/1.3/coordinates**
+
+  Create Coordinate.
+
+  Authentication Required: Yes
+
+  Role(s) Required: admin or oper
+
+  **Request Parameters**
+
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | Name                            | Required | Description                   
                                    |
+  
+=================================+==========+===================================================================+
+  | ``name``                        | yes      | The name of the Coordinate 
entry                                  |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | ``latitude``                    | no       | Latitude of the Coordinate    
                                    |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | ``longitude``                   | no       | Longitude of the Coordinate   
                                    |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+
+  **Request Example** ::
+
+    {
+        "name": "my_coordinate",
+        "latitude": 1.2,
+        "longitude": 4.5
+    }
+
+  **Response Properties**
+
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | Parameter                          | Type   | Description                  
                                     |
+  
+====================================+========+===================================================================+
+  | ``id``                             | int    | The id of the Coordinate     
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``name``                           | string | The name of the Coordinate   
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``latitude``                       | float  | Latitude of the Coordinate   
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``longitude``                      | float  | Longitude of the Coordinate  
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``lastUpdated``                    | string | The Time / Date this entry 
was last updated                       |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``alerts``                         | array  | A collection of alert 
messages.                                   |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``>level``                         | string | Success, info, warning or 
error.                                  |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``>text``                          | string | Alert message.               
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+
+  **Response Example** ::
+
+    {
+        "alerts": [
+                  {
+                          "level": "success",
+                          "text": "coordinate was created"
+                  }
+          ],
+        "response": {
+            'longitude' : 4.5,
+            'lastUpdated' : '2016-01-25 13:55:30',
+            'name' : 'my_coordinate',
+            'latitude' : 1.2,
+            'id' : 1
+        }
+    }
+   
+|
+
+**PUT /api/1.3/coordinates**
+
+  Update coordinate.
+
+  Authentication Required: Yes
+
+  Role(s) Required: admin or oper
+
+  **Request Query Parameters**
+
+  +------+----------+------------------------------------+
+  | Name | Required | Description                        |
+  +======+==========+====================================+
+  | id   | yes      | The id of the coordinate to edit.  |
+  +------+----------+------------------------------------+
+
+  **Request Parameters**
+
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | Name                            | Required | Description                   
                                    |
+  
+=================================+==========+===================================================================+
+  | ``id``                          | yes      | The id of the Coordinate      
                                    |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | ``name``                        | yes      | The name of the Coordinate 
entry                                  |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | ``latitude``                    | no       | Latitude of the Coordinate    
                                    |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+  | ``longitude``                   | no       | Longitude of the Coordinate   
                                    |
+  
+---------------------------------+----------+-------------------------------------------------------------------+
+
+  **Request Example** ::
+
+    {
+        "id": 1,
+        "name": "my_coordinate",
+        "latitude": 12,
+        "longitude": 45
+    }
+
+  **Response Properties**
+
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | Parameter                          | Type   | Description                  
                                     |
+  
+====================================+========+===================================================================+
+  | ``id``                             | int    | The id of the Coordinate     
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``name``                           | string | The name of the Coordinate   
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``latitude``                       | float  | Latitude of the Coordinate   
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``longitude``                      | float  | Longitude of the Coordinate  
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``lastUpdated``                    | string | The Time / Date this entry 
was last updated                       |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``alerts``                         | array  | A collection of alert 
messages.                                   |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``>level``                         | string | Success, info, warning or 
error.                                  |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+  | ``>text``                          | string | Alert message.               
                                     |
+  
+------------------------------------+--------+-------------------------------------------------------------------+
+
+  **Response Example** ::
+
+    {
+        "alerts": [
+                  {
+                          "level": "success",
+                          "text": "coordinate was updated"
+                  }
+          ],
+        "response": {
+            'longitude' : 45,
+            'lastUpdated' : '2016-01-25 13:55:30',
+            'name' : 'my_coordinate',
+            'latitude' : 12,
+            'id' : 1
+        }
+    }
+
+|
+
+**DELETE /api/1.3/coordinates**
+
+  Delete coordinate.
+
+  Authentication Required: Yes
+
+  Role(s) Required: admin or oper
+
+  **Request Query Parameters**
+
+  +------+----------+--------------------------------------+
+  | Name | Required | Description                          |
+  +======+==========+======================================+
+  | id   | yes      | The id of the coordinate to delete.  |
+  +------+----------+--------------------------------------+
+  
+  **Response Properties**
+
+  +-------------+--------+----------------------------------+
+  |  Parameter  |  Type  |           Description            |
+  +=============+========+==================================+
+  | ``alerts``  | array  | A collection of alert messages.  |
+  +-------------+--------+----------------------------------+
+  | ``>level``  | string | Success, info, warning or error. |
+  +-------------+--------+----------------------------------+
+  | ``>text``   | string | Alert message.                   |
+  +-------------+--------+----------------------------------+
+
+  **Response Example** ::
+
+    {
+          "alerts": [
+                    {
+                            "level": "success",
+                            "text": "coordinate was deleted"
+                    }
+            ]
+    }
+
+|
+
diff --git a/docs/source/api/v13/index.rst b/docs/source/api/v13/index.rst
new file mode 100644
index 000000000..4624560fb
--- /dev/null
+++ b/docs/source/api/v13/index.rst
@@ -0,0 +1,24 @@
+..
+..
+.. 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.
+..
+
+
+API 1.3 Reference
+*****************
+Traffic Ops API V1.3
+
+.. toctree::
+  :maxdepth: 1
+
+  coordinate
diff --git a/lib/go-tc/v13/coordinates.go b/lib/go-tc/v13/coordinates.go
new file mode 100644
index 000000000..bb669821b
--- /dev/null
+++ b/lib/go-tc/v13/coordinates.go
@@ -0,0 +1,98 @@
+package v13
+
+import tc "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// A List of Coordinates Response
+// swagger:response CoordinatesResponse
+// in: body
+type CoordinatesResponse struct {
+       // in: body
+       Response []Coordinate `json:"response"`
+}
+
+// A Single Coordinate Response for Update and Create to depict what changed
+// swagger:response CoordinateResponse
+// in: body
+type CoordinateResponse struct {
+       // in: body
+       Response Coordinate `json:"response"`
+}
+
+// Coordinate ...
+type Coordinate struct {
+
+       // The Coordinate to retrieve
+       //
+       // ID of the Coordinate
+       //
+       // required: true
+       ID int `json:"id" db:"id"`
+
+       // Name of the Coordinate
+       //
+       // required: true
+       Name string `json:"name" db:"name"`
+
+       // the latitude of the Coordinate
+       //
+       // required: true
+       Latitude float64 `json:"latitude" db:"latitude"`
+
+       // the latitude of the Coordinate
+       //
+       // required: true
+       Longitude float64 `json:"longitude" db:"longitude"`
+
+       // LastUpdated
+       //
+       LastUpdated tc.TimeNoMod `json:"lastUpdated" db:"last_updated"`
+}
+
+// CoordinateNullable ...
+type CoordinateNullable struct {
+
+       // The Coordinate to retrieve
+       //
+       // ID of the Coordinate
+       //
+       // required: true
+       ID *int `json:"id" db:"id"`
+
+       // Name of the Coordinate
+       //
+       // required: true
+       Name *string `json:"name" db:"name"`
+
+       // the latitude of the Coordinate
+       //
+       // required: true
+       Latitude *float64 `json:"latitude" db:"latitude"`
+
+       // the latitude of the Coordinate
+       //
+       // required: true
+       Longitude *float64 `json:"longitude" db:"longitude"`
+
+       // LastUpdated
+       //
+       LastUpdated *tc.TimeNoMod `json:"lastUpdated" db:"last_updated"`
+}
diff --git a/traffic_ops/app/db/migrations/20180420000000_add_coordinate.sql 
b/traffic_ops/app/db/migrations/20180420000000_add_coordinate.sql
new file mode 100644
index 000000000..89a0f6657
--- /dev/null
+++ b/traffic_ops/app/db/migrations/20180420000000_add_coordinate.sql
@@ -0,0 +1,31 @@
+/*
+
+    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.
+*/
+
+-- +goose Up
+-- SQL in section 'Up' is executed when this migration is applied
+
+CREATE TABLE coordinate (
+    id bigserial primary key NOT NULL,
+    name text UNIQUE NOT NULL,
+    latitude numeric NOT NULL DEFAULT 0.0,
+    longitude numeric NOT NULL DEFAULT 0.0,
+    last_updated timestamp WITH time zone NOT NULL DEFAULT now()
+);
+
+CREATE TRIGGER on_update_current_timestamp BEFORE UPDATE ON coordinate FOR 
EACH ROW EXECUTE PROCEDURE on_update_current_timestamp_last_updated();
+
+-- +goose Down
+-- SQL section 'Down' is executed when this migration is rolled back
+DROP TABLE coordinate;
diff --git a/traffic_ops/client/v13/coordinate.go 
b/traffic_ops/client/v13/coordinate.go
new file mode 100644
index 000000000..3dc8ea15d
--- /dev/null
+++ b/traffic_ops/client/v13/coordinate.go
@@ -0,0 +1,133 @@
+/*
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package v13
+
+import (
+       "encoding/json"
+       "fmt"
+       "net"
+       "net/http"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc/v13"
+)
+
+const (
+       API_v13_Coordinates = "/api/1.3/coordinates"
+)
+
+// Create a Coordinate
+func (to *Session) CreateCoordinate(coordinate v13.Coordinate) (tc.Alerts, 
ReqInf, error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(coordinate)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       resp, remoteAddr, err := to.request(http.MethodPost, 
API_v13_Coordinates, 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 Coordinate by ID
+func (to *Session) UpdateCoordinateByID(id int, coordinate v13.Coordinate) 
(tc.Alerts, ReqInf, error) {
+
+       var remoteAddr net.Addr
+       reqBody, err := json.Marshal(coordinate)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return tc.Alerts{}, reqInf, err
+       }
+       route := fmt.Sprintf("%s?id=%d", API_v13_Coordinates, 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 Coordinates
+func (to *Session) GetCoordinates() ([]v13.Coordinate, ReqInf, error) {
+       resp, remoteAddr, err := to.request(http.MethodGet, 
API_v13_Coordinates, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if err != nil {
+               return nil, reqInf, err
+       }
+       defer resp.Body.Close()
+
+       var data v13.CoordinatesResponse
+       err = json.NewDecoder(resp.Body).Decode(&data)
+       return data.Response, reqInf, nil
+}
+
+// GET a Coordinate by the Coordinate id
+func (to *Session) GetCoordinateByID(id int) ([]v13.Coordinate, ReqInf, error) 
{
+       route := fmt.Sprintf("%s?id=%d", API_v13_Coordinates, 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 v13.CoordinatesResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// GET a Coordinate by the Coordinate name
+func (to *Session) GetCoordinateByName(name string) ([]v13.Coordinate, ReqInf, 
error) {
+       url := fmt.Sprintf("%s?name=%s", API_v13_Coordinates, 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 v13.CoordinatesResponse
+       if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+               return nil, reqInf, err
+       }
+
+       return data.Response, reqInf, nil
+}
+
+// DELETE a Coordinate by ID
+func (to *Session) DeleteCoordinateByID(id int) (tc.Alerts, ReqInf, error) {
+       route := fmt.Sprintf("%s?id=%d", API_v13_Coordinates, id)
+       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/v13/coordinates_test.go 
b/traffic_ops/testing/api/v13/coordinates_test.go
new file mode 100644
index 000000000..708bc98ed
--- /dev/null
+++ b/traffic_ops/testing/api/v13/coordinates_test.go
@@ -0,0 +1,129 @@
+package v13
+
+/*
+
+ 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.
+*/
+
+import (
+       "testing"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       tc "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+)
+
+func TestCoordinates(t *testing.T) {
+       CreateTestCoordinates(t)
+       GetTestCoordinates(t)
+       UpdateTestCoordinates(t)
+       DeleteTestCoordinates(t)
+}
+
+func CreateTestCoordinates(t *testing.T) {
+       failed := false
+
+       for _, coord := range testData.Coordinates {
+
+               _, _, err := TOSession.CreateCoordinate(coord)
+               if err != nil {
+                       t.Errorf("could not CREATE coordinates: %v\n", err)
+                       failed = true
+               }
+       }
+       if !failed {
+               log.Debugln("CreateTestCoordinates() PASSED: ")
+       }
+}
+
+func GetTestCoordinates(t *testing.T) {
+       failed := false
+       for _, coord := range testData.Coordinates {
+               resp, _, err := TOSession.GetCoordinateByName(coord.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Coordinate: %v - %v\n", err, resp)
+                       failed = true
+               }
+       }
+       if !failed {
+               log.Debugln("GetTestCoordinates() PASSED: ")
+       }
+}
+
+func UpdateTestCoordinates(t *testing.T) {
+       failed := false
+       firstCoord := testData.Coordinates[0]
+       resp, _, err := TOSession.GetCoordinateByName(firstCoord.Name)
+       if err != nil {
+               t.Errorf("cannot GET Coordinate by name: %v - %v\n", 
firstCoord.Name, err)
+               failed = true
+       }
+       coord := resp[0]
+       expectedLat := 12.34
+       coord.Latitude = expectedLat
+
+       var alert tc.Alerts
+       alert, _, err = TOSession.UpdateCoordinateByID(coord.ID, coord)
+       if err != nil {
+               t.Errorf("cannot UPDATE Coordinate by id: %v - %v\n", err, 
alert)
+               failed = true
+       }
+
+       // Retrieve the Coordinate to check Coordinate name got updated
+       resp, _, err = TOSession.GetCoordinateByID(coord.ID)
+       if err != nil {
+               t.Errorf("cannot GET Coordinate by name: '$%s', %v\n", 
firstCoord.Name, err)
+               failed = true
+       }
+       coord = resp[0]
+       if coord.Latitude != expectedLat {
+               t.Errorf("results do not match actual: %s, expected: %f\n", 
coord.Name, expectedLat)
+       }
+       if !failed {
+               log.Debugln("UpdateTestCoordinates() PASSED: ")
+       }
+}
+
+func DeleteTestCoordinates(t *testing.T) {
+       failed := false
+
+       for _, coord := range testData.Coordinates {
+               // Retrieve the Coordinate by name so we can get the id for the 
Update
+               resp, _, err := TOSession.GetCoordinateByName(coord.Name)
+               if err != nil {
+                       t.Errorf("cannot GET Coordinate by name: %v - %v\n", 
coord.Name, err)
+                       failed = true
+               }
+               if len(resp) > 0 {
+                       respCoord := resp[0]
+                       _, _, err := 
TOSession.DeleteCoordinateByID(respCoord.ID)
+                       if err != nil {
+                               t.Errorf("cannot DELETE Coordinate by name: 
'%s' %v\n", respCoord.Name, err)
+                               failed = true
+                       }
+                       // Retrieve the Coordinate to see if it got deleted
+                       coords, _, err := 
TOSession.GetCoordinateByName(coord.Name)
+                       if err != nil {
+                               t.Errorf("error deleting Coordinate name: 
%s\n", err.Error())
+                               failed = true
+                       }
+                       if len(coords) > 0 {
+                               t.Errorf("expected Coordinate name: %s to be 
deleted\n", coord.Name)
+                               failed = true
+                       }
+               }
+       }
+
+       if !failed {
+               log.Debugln("DeleteTestCoordinates() PASSED: ")
+       }
+}
diff --git a/traffic_ops/testing/api/v13/tc-fixtures.json 
b/traffic_ops/testing/api/v13/tc-fixtures.json
index 290dcc186..cecc13e99 100644
--- a/traffic_ops/testing/api/v13/tc-fixtures.json
+++ b/traffic_ops/testing/api/v13/tc-fixtures.json
@@ -183,6 +183,18 @@
             "name": "division2"
         }
     ],
+    "coordinates": [
+        {
+            "latitude": 1.1,
+            "longitude": 2.2,
+            "name": "coordinate1"
+        },
+        {
+            "latitude": 3.3,
+            "longitude": 4.4,
+            "name": "coordinate2"
+        }
+    ],
     "parameters": [
         {
             "configFile": "rascal.properties",
diff --git a/traffic_ops/testing/api/v13/traffic_control.go 
b/traffic_ops/testing/api/v13/traffic_control.go
index 015089e43..5e01a4ac8 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -29,6 +29,7 @@ type TrafficControl struct {
        DeliveryServiceRequestComments []v12.DeliveryServiceRequestComment 
`json:"deliveryServiceRequestComments"`
        DeliveryServices               []v12.DeliveryService               
`json:"deliveryservices"`
        Divisions                      []v12.Division                      
`json:"divisions"`
+       Coordinates                    []v13.Coordinate                    
`json:"coordinates"`
        Profiles                       []v13.Profile                       
`json:"profiles"`
        Parameters                     []v12.Parameter                     
`json:"parameters"`
        ProfileParameters              []v13.ProfileParameter              
`json:"profileParameters"`
diff --git a/traffic_ops/traffic_ops_golang/coordinate/coordinates.go 
b/traffic_ops/traffic_ops_golang/coordinate/coordinates.go
new file mode 100644
index 000000000..331e48ee6
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/coordinate/coordinates.go
@@ -0,0 +1,369 @@
+package coordinate
+
+/*
+ * 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"
+       "strings"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-log"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc/v13"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
+       validation "github.com/go-ozzo/ozzo-validation"
+       "github.com/jmoiron/sqlx"
+       "github.com/lib/pq"
+)
+
+//we need a type alias to define functions on
+type TOCoordinate v13.CoordinateNullable
+
+//the refType is passed into the handlers where a copy of its type is used to 
decode the json.
+var refType = TOCoordinate{}
+
+func GetRefType() *TOCoordinate {
+       return &refType
+}
+
+func (coordinate TOCoordinate) GetKeyFieldsInfo() []api.KeyFieldInfo {
+       return []api.KeyFieldInfo{{"id", api.GetIntKey}}
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (coordinate TOCoordinate) GetKeys() (map[string]interface{}, bool) {
+       if coordinate.ID == nil {
+               return map[string]interface{}{"id": 0}, false
+       }
+       return map[string]interface{}{"id": *coordinate.ID}, true
+}
+
+func (coordinate TOCoordinate) GetAuditName() string {
+       if coordinate.Name != nil {
+               return *coordinate.Name
+       }
+       if coordinate.ID != nil {
+               return strconv.Itoa(*coordinate.ID)
+       }
+       return "0"
+}
+
+func (coordinate TOCoordinate) GetType() string {
+       return "coordinate"
+}
+
+func (coordinate *TOCoordinate) SetKeys(keys map[string]interface{}) {
+       i, _ := keys["id"].(int) //this utilizes the non panicking type 
assertion, if the thrown away ok variable is false i will be the zero of the 
type, 0 here.
+       coordinate.ID = &i
+}
+
+func isValidCoordinateChar(r rune) bool {
+       if r >= 'a' && r <= 'z' {
+               return true
+       }
+       if r >= 'A' && r <= 'Z' {
+               return true
+       }
+       if r >= '0' && r <= '9' {
+               return true
+       }
+       if r == '.' || r == '-' || r == '_' {
+               return true
+       }
+       return false
+}
+
+// IsValidCoordinateName returns true if the name contains only characters 
valid for a Coordinate name
+func IsValidCoordinateName(str string) bool {
+       i := strings.IndexFunc(str, func(r rune) bool { return 
!isValidCoordinateChar(r) })
+       return i == -1
+}
+
+// Validate fulfills the api.Validator interface
+func (coordinate TOCoordinate) Validate(db *sqlx.DB) []error {
+       validName := validation.NewStringRule(IsValidCoordinateName, "invalid 
characters found - Use alphanumeric . or - or _ .")
+       latitudeErr := "Must be a floating point number within the range +-90"
+       longitudeErr := "Must be a floating point number within the range +-180"
+       errs := validation.Errors{
+               "name":      validation.Validate(coordinate.Name, 
validation.Required, validName),
+               "latitude":  validation.Validate(coordinate.Latitude, 
validation.Min(-90.0).Error(latitudeErr), 
validation.Max(90.0).Error(latitudeErr)),
+               "longitude": validation.Validate(coordinate.Longitude, 
validation.Min(-180.0).Error(longitudeErr), 
validation.Max(180.0).Error(longitudeErr)),
+       }
+       return tovalidate.ToErrors(errs)
+}
+
+//The TOCoordinate implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a coordinate 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 
coordinate and have
+//to be added to the struct
+func (coordinate *TOCoordinate) 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(), coordinate)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a coordinate 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.TimeNoMod
+       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 coordinate 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 coordinate insert")
+               log.Errorln(err)
+               return tc.DBError, tc.SystemError
+       }
+       coordinate.SetKeys(map[string]interface{}{"id": id})
+       coordinate.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 (coordinate *TOCoordinate) 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{
+               "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 Coordinate: %v", err)
+               return nil, []error{tc.DBError}, tc.SystemError
+       }
+       defer rows.Close()
+
+       Coordinates := []interface{}{}
+       for rows.Next() {
+               var s TOCoordinate
+               if err = rows.StructScan(&s); err != nil {
+                       log.Errorf("error parsing Coordinate rows: %v", err)
+                       return nil, []error{tc.DBError}, tc.SystemError
+               }
+               Coordinates = append(Coordinates, s)
+       }
+
+       return Coordinates, []error{}, tc.NoError
+}
+
+//The TOCoordinate implementation of the Updater interface
+//all implementations of Updater should use transactions and return the proper 
errorType
+//ParsePQUniqueConstraintError is used to determine if a coordinate 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 (coordinate *TOCoordinate) 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 coordinate: %++v", 
updateQuery(), coordinate)
+       resultRows, err := tx.NamedQuery(updateQuery(), coordinate)
+       if err != nil {
+               if pqErr, ok := err.(*pq.Error); ok {
+                       err, eType := 
dbhelpers.ParsePQUniqueConstraintError(pqErr)
+                       if eType == tc.DataConflictError {
+                               return errors.New("a coordinate 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.TimeNoMod
+       rowsAffected := 0
+       for resultRows.Next() {
+               rowsAffected++
+               if err := resultRows.Scan(&lastUpdated); err != nil {
+                       log.Error.Printf("could not scan lastUpdated from 
insert: %s\n", err)
+                       return tc.DBError, tc.SystemError
+               }
+       }
+       log.Debugf("lastUpdated: %++v", lastUpdated)
+       coordinate.LastUpdated = &lastUpdated
+       if rowsAffected != 1 {
+               if rowsAffected < 1 {
+                       return errors.New("no coordinate 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 Coordinate implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper 
errorType
+func (coordinate *TOCoordinate) 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 coordinate: %++v", 
deleteQuery(), coordinate)
+       result, err := tx.NamedExec(deleteQuery(), coordinate)
+       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 coordinate with that id found"), 
tc.DataMissingError
+               } else {
+                       return fmt.Errorf("this delete affected too many rows: 
%d", rowsAffected), tc.SystemError
+               }
+       }
+       err = tx.Commit()
+       if err != nil {
+               log.Errorln("Could not commit transaction: ", err)
+               return tc.DBError, tc.SystemError
+       }
+       rollbackTransaction = false
+       return nil, tc.NoError
+}
+
+func selectQuery() string {
+       query := `SELECT
+id,
+latitude,
+longitude,
+last_updated,
+name
+
+FROM coordinate c`
+       return query
+}
+
+func updateQuery() string {
+       query := `UPDATE
+coordinate SET
+latitude=:latitude,
+longitude=:longitude,
+name=:name
+WHERE id=:id RETURNING last_updated`
+       return query
+}
+
+func insertQuery() string {
+       query := `INSERT INTO coordinate (
+latitude,
+longitude,
+name) VALUES (
+:latitude,
+:longitude,
+:name) RETURNING id,last_updated`
+       return query
+}
+
+func deleteQuery() string {
+       query := `DELETE FROM coordinate
+WHERE id=:id`
+       return query
+}
diff --git a/traffic_ops/traffic_ops_golang/coordinate/coordinates_test.go 
b/traffic_ops/traffic_ops_golang/coordinate/coordinates_test.go
new file mode 100644
index 000000000..2fe851ffc
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/coordinate/coordinates_test.go
@@ -0,0 +1,176 @@
+package coordinate
+
+/*
+ * 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"
+       "reflect"
+       "strings"
+       "testing"
+       "time"
+
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc"
+       "github.com/apache/incubator-trafficcontrol/lib/go-tc/v13"
+       
"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"
+
+       sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func getTestCoordinates() []v13.Coordinate {
+       coords := []v13.Coordinate{}
+       testCoord1 := v13.Coordinate{
+               ID:          1,
+               Name:        "coordinate1",
+               Latitude:    38.7,
+               Longitude:   90.7,
+               LastUpdated: tc.TimeNoMod{Time: time.Now()},
+       }
+       coords = append(coords, testCoord1)
+
+       testCoord2 := v13.Coordinate{
+               ID:          2,
+               Name:        "coordinate2",
+               Latitude:    38.7,
+               Longitude:   90.7,
+               LastUpdated: tc.TimeNoMod{Time: time.Now()},
+       }
+       coords = append(coords, testCoord2)
+
+       return coords
+}
+
+func TestReadCoordinates(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)
+       }
+       defer mockDB.Close()
+
+       db := sqlx.NewDb(mockDB, "sqlmock")
+       defer db.Close()
+
+       refType := GetRefType()
+
+       testCoords := getTestCoordinates()
+       cols := test.ColsFromStructByTag("db", v13.Coordinate{})
+       rows := sqlmock.NewRows(cols)
+
+       for _, ts := range testCoords {
+               rows = rows.AddRow(
+                       ts.ID,
+                       ts.Name,
+                       ts.Latitude,
+                       ts.Longitude,
+                       ts.LastUpdated,
+               )
+       }
+       mock.ExpectQuery("SELECT").WillReturnRows(rows)
+       v := map[string]string{"id": "1"}
+
+       coordinates, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+       if len(errs) > 0 {
+               t.Errorf("coordinate.Read expected: no errors, actual: %v", 
errs)
+       }
+
+       if len(coordinates) != 2 {
+               t.Errorf("coordinate.Read expected: len(coordinates) == 2, 
actual: %v", len(coordinates))
+       }
+}
+
+func TestFuncs(t *testing.T) {
+       if strings.Index(selectQuery(), "SELECT") != 0 {
+               t.Errorf("expected selectQuery to start with SELECT")
+       }
+       if strings.Index(insertQuery(), "INSERT") != 0 {
+               t.Errorf("expected insertQuery to start with INSERT")
+       }
+       if strings.Index(updateQuery(), "UPDATE") != 0 {
+               t.Errorf("expected updateQuery to start with UPDATE")
+       }
+       if strings.Index(deleteQuery(), "DELETE") != 0 {
+               t.Errorf("expected deleteQuery to start with DELETE")
+       }
+}
+
+func TestInterfaces(t *testing.T) {
+       var i interface{}
+       i = &TOCoordinate{}
+
+       if _, ok := i.(api.Creator); !ok {
+               t.Errorf("coordinate must be creator")
+       }
+       if _, ok := i.(api.Reader); !ok {
+               t.Errorf("coordinate must be reader")
+       }
+       if _, ok := i.(api.Updater); !ok {
+               t.Errorf("coordinate must be updater")
+       }
+       if _, ok := i.(api.Deleter); !ok {
+               t.Errorf("coordinate must be deleter")
+       }
+       if _, ok := i.(api.Identifier); !ok {
+               t.Errorf("coordinate must be Identifier")
+       }
+}
+
+func TestValidate(t *testing.T) {
+       // invalid name, latitude, and longitude
+       id := 1
+       nm := "not!a!valid!name"
+       la := -190.0
+       lo := -190.0
+       lu := tc.TimeNoMod{Time: time.Now()}
+       c := TOCoordinate{ID: &id,
+               Name:        &nm,
+               Latitude:    &la,
+               Longitude:   &lo,
+               LastUpdated: &lu,
+       }
+       errs := test.SortErrors(c.Validate(nil))
+
+       expectedErrs := []error{
+               errors.New(`'latitude' Must be a floating point number within 
the range +-90`),
+               errors.New(`'longitude' Must be a floating point number within 
the range +-180`),
+               errors.New(`'name' invalid characters found - Use alphanumeric 
. or - or _ .`),
+       }
+
+       if !reflect.DeepEqual(expectedErrs, errs) {
+               t.Errorf("expected %s, got %s", expectedErrs, errs)
+       }
+
+       //  valid name, latitude, longitude
+       nm = "This.is.2.a-Valid---Coordinate."
+       la = 90.0
+       lo = 90.0
+       c = TOCoordinate{ID: &id,
+               Name:        &nm,
+               Latitude:    &la,
+               Longitude:   &lo,
+               LastUpdated: &lu,
+       }
+       expectedErrs = []error{}
+       errs = c.Validate(nil)
+       if !reflect.DeepEqual(expectedErrs, errs) {
+               t.Errorf("expected %s, got %s", expectedErrs, errs)
+       }
+}
diff --git a/traffic_ops/traffic_ops_golang/routes.go 
b/traffic_ops/traffic_ops_golang/routes.go
index 3453d19d7..4ba5bee39 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -35,7 +35,7 @@ import (
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/cachegroup"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/cdn"
-
+       
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/coordinate"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/crconfig"
        dsrequest 
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice/request"
        
"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice/request/comment"
@@ -184,6 +184,13 @@ func Routes(d ServerData) ([]Route, []RawRoute, 
http.Handler, error) {
                //About
                {1.3, http.MethodGet, `about/?(\.json)?$`, about.Handler(), 
auth.PrivLevelReadOnly, Authenticated, nil},
 
+               //Coordinates
+               {1.3, http.MethodGet, `coordinates/?(\.json)?$`, 
api.ReadHandler(coordinate.GetRefType(), d.DB), auth.PrivLevelReadOnly, 
Authenticated, nil},
+               {1.3, http.MethodGet, `coordinates/?$`, 
api.ReadHandler(coordinate.GetRefType(), d.DB), auth.PrivLevelReadOnly, 
Authenticated, nil},
+               {1.3, http.MethodPut, `coordinates/?$`, 
api.UpdateHandler(coordinate.GetRefType(), d.DB), auth.PrivLevelOperations, 
Authenticated, nil},
+               {1.3, http.MethodPost, `coordinates/?$`, 
api.CreateHandler(coordinate.GetRefType(), d.DB), auth.PrivLevelOperations, 
Authenticated, nil},
+               {1.3, http.MethodDelete, `coordinates/?$`, 
api.DeleteHandler(coordinate.GetRefType(), d.DB), auth.PrivLevelOperations, 
Authenticated, nil},
+
                //Delivery service request: CRUD
                {1.3, http.MethodGet, `deliveryservice_requests/?(\.json)?$`, 
api.ReadHandler(dsrequest.GetRefType(), d.DB), auth.PrivLevelReadOnly, 
Authenticated, nil},
                {1.3, http.MethodPut, `deliveryservice_requests/?$`, 
api.UpdateHandler(dsrequest.GetRefType(), d.DB), auth.PrivLevelPortal, 
Authenticated, nil},


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to