You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ro...@apache.org on 2018/05/07 18:59:10 UTC

[incubator-trafficcontrol] branch master updated (d5ddd6c -> fa6518e)

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

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


    from d5ddd6c  Fix TO Go ASNs unit tests for renamed symbols
     new 394ba97  added DeliveryServicesService Get API in Go
     new b50539c  added api to DeliveryserviceServer Get in Go
     new fa6518e  removed unimplemented routes and unneeded comments

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 lib/go-tc/deliveryservice_servers.go               |  80 ++++++++
 lib/go-tc/deliveryservices.go                      |  13 --
 .../servers/servers.go}                            | 220 ++++++++++++---------
 .../deliveryservice/servers/servers_test.go        |   8 +-
 traffic_ops/traffic_ops_golang/routes.go           |   4 +
 5 files changed, 219 insertions(+), 106 deletions(-)
 create mode 100644 lib/go-tc/deliveryservice_servers.go
 copy traffic_ops/traffic_ops_golang/{profileparameter/profile_parameters.go => deliveryservice/servers/servers.go} (50%)
 copy traffic_portal/app/src/assets/css/custom.css => traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go (95%)

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

[incubator-trafficcontrol] 01/03: added DeliveryServicesService Get API in Go

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 394ba97e884517e9621582dcc30201cefd4da3ee
Author: ASchmidt <An...@comcast.com>
AuthorDate: Fri May 4 14:53:55 2018 -0600

    added DeliveryServicesService Get API in Go
---
 lib/go-tc/deliveryservice_servers.go               | 193 +++++++++++++
 .../deliveryservice/servers/servers.go             | 300 +++++++++++++++++++++
 .../deliveryservice/servers/servers_test.go        | 111 ++++++++
 3 files changed, 604 insertions(+)

diff --git a/lib/go-tc/deliveryservice_servers.go b/lib/go-tc/deliveryservice_servers.go
new file mode 100644
index 0000000..9c7e6ae
--- /dev/null
+++ b/lib/go-tc/deliveryservice_servers.go
@@ -0,0 +1,193 @@
+package tc
+
+/*
+
+   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 (
+	"database/sql/driver"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
+	log "github.com/apache/incubator-trafficcontrol/lib/go-log"
+)
+
+// IDNoMod type is used to suppress JSON unmarshalling
+type IDNoMod int
+
+// DeliveryServiceRequest is used as part of the workflow to create,
+// modify, or delete a delivery service.
+type DeliveryServiceRequest struct {
+	AssigneeID      int             `json:"assigneeId,omitempty"`
+	Assignee        string          `json:"assignee,omitempty"`
+	AuthorID        IDNoMod         `json:"authorId"`
+	Author          string          `json:"author"`
+	ChangeType      string          `json:"changeType"`
+	CreatedAt       *TimeNoMod      `json:"createdAt"`
+	ID              int             `json:"id"`
+	LastEditedBy    string          `json:"lastEditedBy,omitempty"`
+	LastEditedByID  IDNoMod         `json:"lastEditedById,omitempty"`
+	LastUpdated     *TimeNoMod      `json:"lastUpdated"`
+	DeliveryService DeliveryService `json:"deliveryService"`
+	Status          RequestStatus   `json:"status"`
+	XMLID           string          `json:"-" db:"xml_id"`
+}
+
+// DeliveryServiceRequestNullable is used as part of the workflow to create,
+// modify, or delete a delivery service.
+type DeliveryServiceRequestNullable struct {
+	AssigneeID      *int                     `json:"assigneeId,omitempty" db:"assignee_id"`
+	Assignee        *string                  `json:"assignee,omitempty"`
+	AuthorID        *IDNoMod                 `json:"authorId" db:"author_id"`
+	Author          *string                  `json:"author"`
+	ChangeType      *string                  `json:"changeType" db:"change_type"`
+	CreatedAt       *TimeNoMod               `json:"createdAt" db:"created_at"`
+	ID              *int                     `json:"id" db:"id"`
+	LastEditedBy    *string                  `json:"lastEditedBy"`
+	LastEditedByID  *IDNoMod                 `json:"lastEditedById" db:"last_edited_by_id"`
+	LastUpdated     *TimeNoMod               `json:"lastUpdated" db:"last_updated"`
+	DeliveryService *DeliveryServiceNullable `json:"deliveryService" db:"deliveryservice"`
+	Status          *RequestStatus           `json:"status" db:"status"`
+	XMLID           *string                  `json:"-" db:"xml_id"`
+}
+
+// UnmarshalJSON implements the json.Unmarshaller interface to suppress unmarshalling for IDNoMod
+func (a *IDNoMod) UnmarshalJSON([]byte) error {
+	return nil
+}
+
+// RequestStatus captures where in the workflow this request is
+type RequestStatus string
+
+const (
+	// RequestStatusInvalid -- invalid state
+	RequestStatusInvalid = RequestStatus("invalid")
+	// RequestStatusDraft -- newly created; not ready to be reviewed
+	RequestStatusDraft = RequestStatus("draft")
+	// RequestStatusSubmitted -- newly created; ready to be reviewed
+	RequestStatusSubmitted = RequestStatus("submitted")
+	// RequestStatusRejected -- reviewed, but problems found
+	RequestStatusRejected = RequestStatus("rejected")
+	// RequestStatusPending -- reviewed and locked; ready to be implemented
+	RequestStatusPending = RequestStatus("pending")
+	// RequestStatusComplete -- implemented and locked
+	RequestStatusComplete = RequestStatus("complete")
+)
+
+// RequestStatuses -- user-visible string associated with each of the above
+var RequestStatuses = []RequestStatus{
+	// "invalid" -- don't list here..
+	"draft",
+	"submitted",
+	"rejected",
+	"pending",
+	"complete",
+}
+
+// UnmarshalJSON implements json.Unmarshaller
+func (r *RequestStatus) UnmarshalJSON(b []byte) error {
+	u, err := strconv.Unquote(string(b))
+	if err != nil {
+		return err
+	}
+
+	// just check to see if the string represents a valid requeststatus
+	_, err = RequestStatusFromString(u)
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(b, (*string)(r))
+}
+
+// MarshalJSON implements json.Marshaller
+func (r RequestStatus) MarshalJSON() ([]byte, error) {
+	return json.Marshal(string(r))
+}
+
+// Value implements driver.Valuer
+func (r *RequestStatus) Value() (driver.Value, error) {
+	v, err := json.Marshal(r)
+	log.Debugf("value is %v; err is %v", v, err)
+	v = []byte(strings.Trim(string(v), `"`))
+	return v, err
+}
+
+// Scan implements sql.Scanner
+func (r *RequestStatus) Scan(src interface{}) error {
+	b, ok := src.([]byte)
+	if !ok {
+		return fmt.Errorf("expected requeststatus in byte array form; got %T", src)
+	}
+	b = []byte(`"` + string(b) + `"`)
+	return json.Unmarshal(b, r)
+}
+
+// RequestStatusFromString gets the status enumeration from a string
+func RequestStatusFromString(rs string) (RequestStatus, error) {
+	if rs == "" {
+		return RequestStatusDraft, nil
+	}
+	for _, s := range RequestStatuses {
+		if string(s) == rs {
+			return s, nil
+		}
+	}
+	return RequestStatusInvalid, errors.New(rs + " is not a valid RequestStatus name")
+}
+
+// ValidTransition returns nil if the transition is allowed for the workflow, an error if not
+func (r RequestStatus) ValidTransition(to RequestStatus) error {
+	if r == RequestStatusRejected || r == RequestStatusComplete {
+		// once rejected or completed,  no changes allowed
+		return errors.New(string(r) + " request cannot be changed")
+	}
+
+	if r == to {
+		// no change -- always allowed
+		return nil
+	}
+
+	// indicate if valid transitioning to this RequestStatus
+	switch to {
+	case RequestStatusDraft:
+		// can go back to draft if submitted or rejected
+		if r == RequestStatusSubmitted {
+			return nil
+		}
+	case RequestStatusSubmitted:
+		// can go be submitted if draft or rejected
+		if r == RequestStatusDraft {
+			return nil
+		}
+	case RequestStatusRejected:
+		// only submitted can be rejected
+		if r == RequestStatusSubmitted {
+			return nil
+		}
+	case RequestStatusPending:
+		// only submitted can move to pending
+		if r == RequestStatusSubmitted {
+			return nil
+		}
+	case RequestStatusComplete:
+		// only pending can be completed.  Completed can never change.
+		if r == RequestStatusPending {
+			return nil
+		}
+	}
+	return errors.New("invalid transition from " + string(r) + " to " + string(to))
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
new file mode 100644
index 0000000..463ff06
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -0,0 +1,300 @@
+package profileparameter
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+
+	"github.com/apache/incubator-trafficcontrol/lib/go-log"
+	tc "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"
+)
+
+const (
+	ProfileIDQueryParam   = "profileId"
+	ParameterIDQueryParam = "parameterId"
+)
+
+//we need a type alias to define functions on
+type TOProfileParameter v13.ProfileParameterNullable
+
+//the refType is passed into the handlers where a copy of its type is used to decode the json.
+var refType = TOProfileParameter(v13.ProfileParameterNullable{})
+
+func GetRefType() *TOProfileParameter {
+	return &refType
+}
+
+func (pp TOProfileParameter) GetKeyFieldsInfo() []api.KeyFieldInfo {
+	return []api.KeyFieldInfo{{ProfileIDQueryParam, api.GetIntKey}, {ParameterIDQueryParam, api.GetIntKey}}
+}
+
+//Implementation of the Identifier, Validator interface functions
+func (pp TOProfileParameter) GetKeys() (map[string]interface{}, bool) {
+	if pp.ProfileID == nil {
+		return map[string]interface{}{ProfileIDQueryParam: 0}, false
+	}
+	if pp.ParameterID == nil {
+		return map[string]interface{}{ParameterIDQueryParam: 0}, false
+	}
+	keys := make(map[string]interface{})
+	profileID := *pp.ProfileID
+	parameterID := *pp.ParameterID
+
+	keys[ProfileIDQueryParam] = profileID
+	keys[ParameterIDQueryParam] = parameterID
+	return keys, true
+}
+
+func (pp *TOProfileParameter) GetAuditName() string {
+	if pp.ProfileID != nil {
+		return strconv.Itoa(*pp.ProfileID) + "-" + strconv.Itoa(*pp.ParameterID)
+	}
+	return "unknown"
+}
+
+func (pp *TOProfileParameter) GetType() string {
+	return "profileParameter"
+}
+
+func (pp *TOProfileParameter) SetKeys(keys map[string]interface{}) {
+	profId, _ := keys[ProfileIDQueryParam].(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.
+	pp.ProfileID = &profId
+
+	paramId, _ := keys[ParameterIDQueryParam].(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.
+	pp.ParameterID = &paramId
+}
+
+// Validate fulfills the api.Validator interface
+func (pp *TOProfileParameter) Validate(db *sqlx.DB) []error {
+
+	errs := validation.Errors{
+		"profile":   validation.Validate(pp.ProfileID, validation.Required),
+		"parameter": validation.Validate(pp.ParameterID, validation.Required),
+	}
+
+	return tovalidate.ToErrors(errs)
+}
+
+//The TOProfileParameter implementation of the Creator interface
+//all implementations of Creator should use transactions and return the proper errorType
+//ParsePQUniqueConstraintError is used to determine if a profileparameter with conflicting values exists
+//if so, it will return an errorType of DataConflict and the type should be appended to the
+//generic error message returned
+//The insert sql returns the profile and lastUpdated values of the newly inserted profileparameter and have
+//to be added to the struct
+func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+	rollbackTransaction := true
+	tx, err := db.Beginx()
+	defer func() {
+		if tx == nil || !rollbackTransaction {
+			return
+		}
+		err := tx.Rollback()
+		if err != nil {
+			log.Errorln(errors.New("rolling back transaction: " + err.Error()))
+		}
+	}()
+
+	if err != nil {
+		log.Error.Printf("could not begin transaction: %v", err)
+		return tc.DBError, tc.SystemError
+	}
+	resultRows, err := tx.NamedQuery(insertQuery(), pp)
+	if err != nil {
+		if pqErr, ok := err.(*pq.Error); ok {
+			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
+			if eType == tc.DataConflictError {
+				return errors.New("a parameter with " + err.Error()), eType
+			}
+			return err, eType
+		}
+		log.Errorf("received non pq error: %++v from create execution", err)
+		return tc.DBError, tc.SystemError
+	}
+	defer resultRows.Close()
+
+	var profile int
+	var parameter int
+	var lastUpdated tc.TimeNoMod
+	rowsAffected := 0
+	for resultRows.Next() {
+		rowsAffected++
+		if err := resultRows.Scan(&profile, &parameter, &lastUpdated); err != nil {
+			log.Error.Printf("could not scan profile from insert: %s\n", err)
+			return tc.DBError, tc.SystemError
+		}
+	}
+	if rowsAffected == 0 {
+		err = errors.New("no profile_parameter was inserted, no profile+parameter was returned")
+		log.Errorln(err)
+		return tc.DBError, tc.SystemError
+	}
+	if rowsAffected > 1 {
+		err = errors.New("too many ids returned from parameter insert")
+		log.Errorln(err)
+		return tc.DBError, tc.SystemError
+	}
+
+	pp.SetKeys(map[string]interface{}{ProfileIDQueryParam: profile, ParameterIDQueryParam: parameter})
+	pp.LastUpdated = &lastUpdated
+	err = tx.Commit()
+	if err != nil {
+		log.Errorln("Could not commit transaction: ", err)
+		return tc.DBError, tc.SystemError
+	}
+	rollbackTransaction = false
+	return nil, tc.NoError
+}
+
+func insertQuery() string {
+	query := `INSERT INTO profile_parameter (
+profile,
+parameter) VALUES (
+:profile_id,
+:parameter_id) RETURNING profile, parameter, last_updated`
+	return query
+}
+
+func (pp *TOProfileParameter) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+	var rows *sqlx.Rows
+
+	// Query Parameters to Database Query column mappings
+	// see the fields mapped in the SQL query
+	queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+		"profileId":   dbhelpers.WhereColumnInfo{"pp.profile", nil},
+		"parameterId": dbhelpers.WhereColumnInfo{"pp.parameter", nil},
+		"lastUpdated": dbhelpers.WhereColumnInfo{"pp.last_updated", nil},
+	}
+
+	where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols)
+	if len(errs) > 0 {
+		return nil, errs, tc.DataConflictError
+	}
+
+	query := selectQuery() + where + orderBy
+	log.Debugln("Query is ", query)
+
+	rows, err := db.NamedQuery(query, queryValues)
+	if err != nil {
+		log.Errorf("Error querying Parameters: %v", err)
+		return nil, []error{tc.DBError}, tc.SystemError
+	}
+	defer rows.Close()
+
+	params := []interface{}{}
+	for rows.Next() {
+		var p v13.ProfileParameterNullable
+		if err = rows.StructScan(&p); err != nil {
+			log.Errorf("error parsing pp rows: %v", err)
+			return nil, []error{tc.DBError}, tc.SystemError
+		}
+		params = append(params, p)
+	}
+
+	return params, []error{}, tc.NoError
+
+}
+
+//The Parameter implementation of the Deleter interface
+//all implementations of Deleter should use transactions and return the proper errorType
+func (pp *TOProfileParameter) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+	rollbackTransaction := true
+	tx, err := db.Beginx()
+	defer func() {
+		if tx == nil || !rollbackTransaction {
+			return
+		}
+		err := tx.Rollback()
+		if err != nil {
+			log.Errorln(errors.New("rolling back transaction: " + err.Error()))
+		}
+	}()
+
+	if err != nil {
+		log.Error.Printf("could not begin transaction: %v", err)
+		return tc.DBError, tc.SystemError
+	}
+	log.Debugf("about to run exec query: %s with parameter: %++v", deleteQuery(), pp)
+	result, err := tx.NamedExec(deleteQuery(), pp)
+	if err != nil {
+		log.Errorf("received error: %++v from delete execution", err)
+		return tc.DBError, tc.SystemError
+	}
+	rowsAffected, err := result.RowsAffected()
+	if err != nil {
+		return tc.DBError, tc.SystemError
+	}
+	if rowsAffected < 1 {
+		return errors.New("no parameter with that id found"), tc.DataMissingError
+	}
+	if rowsAffected > 1 {
+		return fmt.Errorf("this create affected too many rows: %d", rowsAffected), tc.SystemError
+	}
+
+	err = tx.Commit()
+	if err != nil {
+		log.Errorln("Could not commit transaction: ", err)
+		return tc.DBError, tc.SystemError
+	}
+	rollbackTransaction = false
+	return nil, tc.NoError
+}
+
+func selectQuery() string {
+
+	query := `SELECT
+pp.last_updated,
+pp.profile profile_id,
+pp.parameter parameter_id,
+prof.name profile,
+param.name parameter
+FROM profile_parameter pp
+JOIN profile prof ON prof.id = pp.profile
+JOIN parameter param ON param.id = pp.parameter`
+	return query
+}
+
+func updateQuery() string {
+	query := `UPDATE
+profile_parameter SET
+profile=:profile_id,
+parameter=:parameter_id
+WHERE profile=:profile_id AND 
+      parameter = :parameter_id 
+      RETURNING last_updated`
+	return query
+}
+
+func deleteQuery() string {
+	query := `DELETE FROM profile_parameter
+	WHERE profile=:profile_id and parameter=:parameter_id`
+	return query
+}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
new file mode 100644
index 0000000..9e7ce20
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
@@ -0,0 +1,111 @@
+package profileparameter
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"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 getTestProfileParameters() []v13.ProfileParameterNullable {
+	pps := []v13.ProfileParameterNullable{}
+	lastUpdated := tc.TimeNoMod{}
+	lastUpdated.Scan(time.Now())
+	profileID := 1
+	parameterID := 1
+
+	pp := v13.ProfileParameterNullable{
+		LastUpdated: &lastUpdated,
+		ProfileID:   &profileID,
+		ParameterID: &parameterID,
+	}
+	pps = append(pps, pp)
+
+	pp2 := pp
+	pp2.ProfileID = &profileID
+	pp2.ParameterID = &parameterID
+	pps = append(pps, pp2)
+
+	return pps
+}
+
+func TestGetProfileParameters(t *testing.T) {
+	mockDB, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer mockDB.Close()
+
+	db := sqlx.NewDb(mockDB, "sqlmock")
+	defer db.Close()
+
+	testPPs := getTestProfileParameters()
+	cols := test.ColsFromStructByTag("db", v13.ProfileParameterNullable{})
+	rows := sqlmock.NewRows(cols)
+
+	for _, ts := range testPPs {
+		rows = rows.AddRow(
+			ts.LastUpdated,
+			ts.Profile,
+			ts.ProfileID,
+			ts.Parameter,
+			ts.ParameterID,
+		)
+	}
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+	v := map[string]string{"profile": "1"}
+
+	pps, errs, _ := refType.Read(db, v, auth.CurrentUser{})
+	if len(errs) > 0 {
+		t.Errorf("profileparameter.Read expected: no errors, actual: %v", errs)
+	}
+
+	if len(pps) != 2 {
+		t.Errorf("profileparameter.Read expected: len(pps) == 2, actual: %v", len(pps))
+	}
+
+}
+
+func TestInterfaces(t *testing.T) {
+	var i interface{}
+	i = &TOProfileParameter{}
+
+	if _, ok := i.(api.Creator); !ok {
+		t.Errorf("ProfileParameter must be Creator")
+	}
+	if _, ok := i.(api.Reader); !ok {
+		t.Errorf("ProfileParameter must be Reader")
+	}
+	if _, ok := i.(api.Deleter); !ok {
+		t.Errorf("ProfileParameter must be Deleter")
+	}
+	if _, ok := i.(api.Identifier); !ok {
+		t.Errorf("ProfileParameter must be Identifier")
+	}
+}

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

[incubator-trafficcontrol] 03/03: removed unimplemented routes and unneeded comments

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fa6518ecff3e0bc6a8cece1048a0d8d8ba57bb99
Author: ASchmidt <An...@comcast.com>
AuthorDate: Mon May 7 11:46:09 2018 -0600

    removed unimplemented routes and unneeded comments
---
 lib/go-tc/deliveryservice_servers.go                | 21 ---------------------
 .../deliveryservice/servers/servers.go              | 19 -------------------
 traffic_ops/traffic_ops_golang/routes.go            |  4 +---
 3 files changed, 1 insertion(+), 43 deletions(-)

diff --git a/lib/go-tc/deliveryservice_servers.go b/lib/go-tc/deliveryservice_servers.go
index bf6a3f2..aa0ec99 100644
--- a/lib/go-tc/deliveryservice_servers.go
+++ b/lib/go-tc/deliveryservice_servers.go
@@ -17,21 +17,6 @@ import "time"
    limitations under the License.
 */
 
-/*
-# get all delivery services associated with a server (from deliveryservice_server table)
-$r->get( "/api/$version/servers/:id/deliveryservices" => [ id => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'Deliveryservice#get_deliveryservices_by_serverId', namespace => $namespace );
-
-# delivery service / server assignments
-$r->post("/api/$version/deliveryservices/:xml_id/servers")->over( authenticated => 1, not_ldap => 1 )
-->to( 'Deliveryservice#assign_servers', namespace => $namespace );
-$r->delete("/api/$version/deliveryservice_server/:dsId/:serverId" => [ dsId => qr/\d+/, serverId => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#remove_server_from_ds', namespace => $namespace );
-	# -- DELIVERYSERVICES: SERVERS
-	# Supports ?orderby=key
-	$r->get("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#index', namespace => $namespace );
-	$r->post("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#assign_servers_to_ds', namespace => $namespace );
-
-*/
-
 // DeliveryServiceServerResponse ...
 type DeliveryServiceServerResponse struct {
 	Response []DeliveryServiceServer `json:"response"`
@@ -93,9 +78,3 @@ type DssServer struct {
 	TypeID           *int                 `json:"typeId" db:"server_type_id"`
 	UpdPending       *bool                `json:"updPending" db:"upd_pending"`
 }
-
-// UnmarshalJSON implements the json.Unmarshaller interface to suppress unmarshalling for IDNoMod
-//func (a *IDNoMod) UnmarshalJSON([]byte) error {
-	//return nil
-//}
-
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
index 768dab0..f212937 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -46,25 +46,6 @@ func GetRefType() *TODeliveryServiceServer {
 	return &refType
 }
 
-/*
-# get all delivery services associated with a server (from deliveryservice_server table)
-$r->get( "/api/$version/servers/:id/deliveryservices" => [ id => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'Deliveryservice#get_deliveryservices_by_serverId', namespace => $namespace );
-
-# delivery service / server assignments
-$r->post("/api/$version/deliveryservices/:xml_id/servers")->over( authenticated => 1, not_ldap => 1 )
-->to( 'Deliveryservice#assign_servers', namespace => $namespace );
-$r->delete("/api/$version/deliveryservice_server/:dsId/:serverId" => [ dsId => qr/\d+/, serverId => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#remove_server_from_ds', namespace => $namespace );
-	# -- DELIVERYSERVICES: SERVERS
-	# Supports ?orderby=key
-	$r->get("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#index', namespace => $namespace );
-	$r->post("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#assign_servers_to_ds', namespace => $namespace );
-
-		{1.2, http.MethodGet, `deliveryservices/{id}/servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
-		{1.2, http.MethodGet, `deliveryservices/{id}/unassigned_servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
-		{1.2, http.MethodGet, `deliveryservices/{id}/servers/eligible$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
-
-*/
-
 func (dss TODeliveryServiceServer) GetKeyFieldsInfo() []api.KeyFieldInfo {
 	return []api.KeyFieldInfo{{"deliveryservice", api.GetIntKey}, {"server", api.GetIntKey}}
 }
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 125f7ef..5763f70 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -150,9 +150,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{1.1, http.MethodDelete, `regions/{id}$`, api.DeleteHandler(region.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
 
 		// get all edge servers associated with a delivery service (from deliveryservice_server table)
-		{1.2, http.MethodGet, `deliveryservices/{id}/servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
-		{1.2, http.MethodGet, `deliveryservices/{id}/unassigned_servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
-		{1.2, http.MethodGet, `deliveryservices/{id}/servers/eligible$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.1, http.MethodGet, `deliveryservices/{id}/servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
 
 		//Server
 		{1.1, http.MethodGet, `servers/checks$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},

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

[incubator-trafficcontrol] 02/03: added api to DeliveryserviceServer Get in Go

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b50539c49e61b7ec189f9b71511166f822e73625
Author: ASchmidt <An...@comcast.com>
AuthorDate: Fri May 4 15:03:15 2018 -0600

    added api to DeliveryserviceServer Get in Go
---
 lib/go-tc/deliveryservice_servers.go               | 240 +++++++--------------
 lib/go-tc/deliveryservices.go                      |  13 --
 .../deliveryservice/servers/servers.go             | 239 ++++++++++++--------
 .../deliveryservice/servers/servers_test.go        |  91 +-------
 traffic_ops/traffic_ops_golang/routes.go           |   6 +
 5 files changed, 230 insertions(+), 359 deletions(-)

diff --git a/lib/go-tc/deliveryservice_servers.go b/lib/go-tc/deliveryservice_servers.go
index 9c7e6ae..bf6a3f2 100644
--- a/lib/go-tc/deliveryservice_servers.go
+++ b/lib/go-tc/deliveryservice_servers.go
@@ -1,5 +1,7 @@
 package tc
 
+import "time"
+
 /*
 
    Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,179 +17,85 @@ package tc
    limitations under the License.
 */
 
-import (
-	"database/sql/driver"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"strconv"
-	"strings"
-
-	log "github.com/apache/incubator-trafficcontrol/lib/go-log"
-)
-
-// IDNoMod type is used to suppress JSON unmarshalling
-type IDNoMod int
-
-// DeliveryServiceRequest is used as part of the workflow to create,
-// modify, or delete a delivery service.
-type DeliveryServiceRequest struct {
-	AssigneeID      int             `json:"assigneeId,omitempty"`
-	Assignee        string          `json:"assignee,omitempty"`
-	AuthorID        IDNoMod         `json:"authorId"`
-	Author          string          `json:"author"`
-	ChangeType      string          `json:"changeType"`
-	CreatedAt       *TimeNoMod      `json:"createdAt"`
-	ID              int             `json:"id"`
-	LastEditedBy    string          `json:"lastEditedBy,omitempty"`
-	LastEditedByID  IDNoMod         `json:"lastEditedById,omitempty"`
-	LastUpdated     *TimeNoMod      `json:"lastUpdated"`
-	DeliveryService DeliveryService `json:"deliveryService"`
-	Status          RequestStatus   `json:"status"`
-	XMLID           string          `json:"-" db:"xml_id"`
-}
-
-// DeliveryServiceRequestNullable is used as part of the workflow to create,
-// modify, or delete a delivery service.
-type DeliveryServiceRequestNullable struct {
-	AssigneeID      *int                     `json:"assigneeId,omitempty" db:"assignee_id"`
-	Assignee        *string                  `json:"assignee,omitempty"`
-	AuthorID        *IDNoMod                 `json:"authorId" db:"author_id"`
-	Author          *string                  `json:"author"`
-	ChangeType      *string                  `json:"changeType" db:"change_type"`
-	CreatedAt       *TimeNoMod               `json:"createdAt" db:"created_at"`
-	ID              *int                     `json:"id" db:"id"`
-	LastEditedBy    *string                  `json:"lastEditedBy"`
-	LastEditedByID  *IDNoMod                 `json:"lastEditedById" db:"last_edited_by_id"`
-	LastUpdated     *TimeNoMod               `json:"lastUpdated" db:"last_updated"`
-	DeliveryService *DeliveryServiceNullable `json:"deliveryService" db:"deliveryservice"`
-	Status          *RequestStatus           `json:"status" db:"status"`
-	XMLID           *string                  `json:"-" db:"xml_id"`
-}
-
-// UnmarshalJSON implements the json.Unmarshaller interface to suppress unmarshalling for IDNoMod
-func (a *IDNoMod) UnmarshalJSON([]byte) error {
-	return nil
-}
-
-// RequestStatus captures where in the workflow this request is
-type RequestStatus string
-
-const (
-	// RequestStatusInvalid -- invalid state
-	RequestStatusInvalid = RequestStatus("invalid")
-	// RequestStatusDraft -- newly created; not ready to be reviewed
-	RequestStatusDraft = RequestStatus("draft")
-	// RequestStatusSubmitted -- newly created; ready to be reviewed
-	RequestStatusSubmitted = RequestStatus("submitted")
-	// RequestStatusRejected -- reviewed, but problems found
-	RequestStatusRejected = RequestStatus("rejected")
-	// RequestStatusPending -- reviewed and locked; ready to be implemented
-	RequestStatusPending = RequestStatus("pending")
-	// RequestStatusComplete -- implemented and locked
-	RequestStatusComplete = RequestStatus("complete")
-)
-
-// RequestStatuses -- user-visible string associated with each of the above
-var RequestStatuses = []RequestStatus{
-	// "invalid" -- don't list here..
-	"draft",
-	"submitted",
-	"rejected",
-	"pending",
-	"complete",
-}
-
-// UnmarshalJSON implements json.Unmarshaller
-func (r *RequestStatus) UnmarshalJSON(b []byte) error {
-	u, err := strconv.Unquote(string(b))
-	if err != nil {
-		return err
-	}
+/*
+# get all delivery services associated with a server (from deliveryservice_server table)
+$r->get( "/api/$version/servers/:id/deliveryservices" => [ id => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'Deliveryservice#get_deliveryservices_by_serverId', namespace => $namespace );
+
+# delivery service / server assignments
+$r->post("/api/$version/deliveryservices/:xml_id/servers")->over( authenticated => 1, not_ldap => 1 )
+->to( 'Deliveryservice#assign_servers', namespace => $namespace );
+$r->delete("/api/$version/deliveryservice_server/:dsId/:serverId" => [ dsId => qr/\d+/, serverId => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#remove_server_from_ds', namespace => $namespace );
+	# -- DELIVERYSERVICES: SERVERS
+	# Supports ?orderby=key
+	$r->get("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#index', namespace => $namespace );
+	$r->post("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#assign_servers_to_ds', namespace => $namespace );
 
-	// just check to see if the string represents a valid requeststatus
-	_, err = RequestStatusFromString(u)
-	if err != nil {
-		return err
-	}
-	return json.Unmarshal(b, (*string)(r))
-}
+*/
 
-// MarshalJSON implements json.Marshaller
-func (r RequestStatus) MarshalJSON() ([]byte, error) {
-	return json.Marshal(string(r))
+// DeliveryServiceServerResponse ...
+type DeliveryServiceServerResponse struct {
+	Response []DeliveryServiceServer `json:"response"`
+	Size     int                     `json:"size"`
+	OrderBy  string                  `json:"orderby"`
+	Limit    int                     `json:"limit"`
 }
 
-// Value implements driver.Valuer
-func (r *RequestStatus) Value() (driver.Value, error) {
-	v, err := json.Marshal(r)
-	log.Debugf("value is %v; err is %v", v, err)
-	v = []byte(strings.Trim(string(v), `"`))
-	return v, err
+// DeliveryServiceServer ...
+type DeliveryServiceServer struct {
+	Server          *int             `json:"server"`
+	DeliveryService *int             `json:"deliveryService"`
+	LastUpdated     *TimeNoMod       `json:"lastUpdated" db:"last_updated"`
 }
 
-// Scan implements sql.Scanner
-func (r *RequestStatus) Scan(src interface{}) error {
-	b, ok := src.([]byte)
-	if !ok {
-		return fmt.Errorf("expected requeststatus in byte array form; got %T", src)
-	}
-	b = []byte(`"` + string(b) + `"`)
-	return json.Unmarshal(b, r)
-}
 
-// RequestStatusFromString gets the status enumeration from a string
-func RequestStatusFromString(rs string) (RequestStatus, error) {
-	if rs == "" {
-		return RequestStatusDraft, nil
-	}
-	for _, s := range RequestStatuses {
-		if string(s) == rs {
-			return s, nil
-		}
-	}
-	return RequestStatusInvalid, errors.New(rs + " is not a valid RequestStatus name")
+type DssServer struct {
+	Cachegroup       *string              `json:"cachegroup" db:"cachegroup"`
+	CachegroupID     *int                 `json:"cachegroupId" db:"cachegroup_id"`
+	CDNID            *int                 `json:"cdnId" db:"cdn_id"`
+	CDNName          *string              `json:"cdnName" db:"cdn_name"`
+	DeliveryServices *map[string][]string `json:"deliveryServices,omitempty"`
+	DomainName       *string              `json:"domainName" db:"domain_name"`
+	FQDN             *string              `json:"fqdn,omitempty"`
+	FqdnTime         time.Time            `json:"-"`
+	GUID             *string              `json:"guid" db:"guid"`
+	HostName         *string              `json:"hostName" db:"host_name"`
+	HTTPSPort        *int                 `json:"httpsPort" db:"https_port"`
+	ID               *int                 `json:"id" db:"id"`
+	ILOIPAddress     *string              `json:"iloIpAddress" db:"ilo_ip_address"`
+	ILOIPGateway     *string              `json:"iloIpGateway" db:"ilo_ip_gateway"`
+	ILOIPNetmask     *string              `json:"iloIpNetmask" db:"ilo_ip_netmask"`
+	ILOPassword      *string              `json:"iloPassword" db:"ilo_password"`
+	ILOUsername      *string              `json:"iloUsername" db:"ilo_username"`
+	InterfaceMtu     *int                 `json:"interfaceMtu" db:"interface_mtu"`
+	InterfaceName    *string              `json:"interfaceName" db:"interface_name"`
+	IP6Address       *string              `json:"ip6Address" db:"ip6_address"`
+	IP6Gateway       *string              `json:"ip6Gateway" db:"ip6_gateway"`
+	IPAddress        *string              `json:"ipAddress" db:"ip_address"`
+	IPGateway        *string              `json:"ipGateway" db:"ip_gateway"`
+	IPNetmask        *string              `json:"ipNetmask" db:"ip_netmask"`
+	LastUpdated      *TimeNoMod           `json:"lastUpdated" db:"last_updated"`
+	MgmtIPAddress    *string              `json:"mgmtIpAddress" db:"mgmt_ip_address"`
+	MgmtIPGateway    *string              `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
+	MgmtIPNetmask    *string              `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
+	OfflineReason    *string              `json:"offlineReason" db:"offline_reason"`
+	PhysLocation     *string              `json:"physLocation" db:"phys_location"`
+	PhysLocationID   *int                 `json:"physLocationId" db:"phys_location_id"`
+	Profile          *string              `json:"profile" db:"profile"`
+	ProfileDesc      *string              `json:"profileDesc" db:"profile_desc"`
+	ProfileID        *int                 `json:"profileId" db:"profile_id"`
+	Rack             *string              `json:"rack" db:"rack"`
+	RouterHostName   *string              `json:"routerHostName" db:"router_host_name"`
+	RouterPortName   *string              `json:"routerPortName" db:"router_port_name"`
+	Status           *string              `json:"status" db:"status"`
+	StatusID         *int                 `json:"statusId" db:"status_id"`
+	TCPPort          *int                 `json:"tcpPort" db:"tcp_port"`
+	Type             string               `json:"type" db:"server_type"`
+	TypeID           *int                 `json:"typeId" db:"server_type_id"`
+	UpdPending       *bool                `json:"updPending" db:"upd_pending"`
 }
 
-// ValidTransition returns nil if the transition is allowed for the workflow, an error if not
-func (r RequestStatus) ValidTransition(to RequestStatus) error {
-	if r == RequestStatusRejected || r == RequestStatusComplete {
-		// once rejected or completed,  no changes allowed
-		return errors.New(string(r) + " request cannot be changed")
-	}
-
-	if r == to {
-		// no change -- always allowed
-		return nil
-	}
+// UnmarshalJSON implements the json.Unmarshaller interface to suppress unmarshalling for IDNoMod
+//func (a *IDNoMod) UnmarshalJSON([]byte) error {
+	//return nil
+//}
 
-	// indicate if valid transitioning to this RequestStatus
-	switch to {
-	case RequestStatusDraft:
-		// can go back to draft if submitted or rejected
-		if r == RequestStatusSubmitted {
-			return nil
-		}
-	case RequestStatusSubmitted:
-		// can go be submitted if draft or rejected
-		if r == RequestStatusDraft {
-			return nil
-		}
-	case RequestStatusRejected:
-		// only submitted can be rejected
-		if r == RequestStatusSubmitted {
-			return nil
-		}
-	case RequestStatusPending:
-		// only submitted can move to pending
-		if r == RequestStatusSubmitted {
-			return nil
-		}
-	case RequestStatusComplete:
-		// only pending can be completed.  Completed can never change.
-		if r == RequestStatusPending {
-			return nil
-		}
-	}
-	return errors.New("invalid transition from " + string(r) + " to " + string(to))
-}
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index c352882..a01b921 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -281,17 +281,4 @@ type DeliveryServiceRouting struct {
 	RegionalDenied    int     `json:"regionalDenied"`
 }
 
-// DeliveryServiceServerResponse ...
-type DeliveryServiceServerResponse struct {
-	Response []DeliveryServiceServer `json:"response"`
-	Size     int                     `json:"size"`
-	OrderBy  string                  `json:"orderby"`
-	Limit    int                     `json:"limit"`
-}
 
-// DeliveryServiceServer ...
-type DeliveryServiceServer struct {
-	LastUpdated     string `json:"lastUpdated"`
-	Server          int    `json:"server"`
-	DeliveryService int    `json:"deliveryService"`
-}
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
index 463ff06..768dab0 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go
@@ -1,4 +1,4 @@
-package profileparameter
+package servers
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -25,92 +25,105 @@ import (
 	"strconv"
 
 	"github.com/apache/incubator-trafficcontrol/lib/go-log"
-	tc "github.com/apache/incubator-trafficcontrol/lib/go-tc"
-	"github.com/apache/incubator-trafficcontrol/lib/go-tc/v13"
+	"github.com/apache/incubator-trafficcontrol/lib/go-tc"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/auth"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/tovalidate"
-	validation "github.com/go-ozzo/ozzo-validation"
+	"github.com/go-ozzo/ozzo-validation"
 
 	"github.com/jmoiron/sqlx"
 	"github.com/lib/pq"
 )
 
-const (
-	ProfileIDQueryParam   = "profileId"
-	ParameterIDQueryParam = "parameterId"
-)
-
-//we need a type alias to define functions on
-type TOProfileParameter v13.ProfileParameterNullable
+// TODeliveryServiceRequest provides a type alias to define functions on
+type TODeliveryServiceServer tc.DeliveryServiceServer
 
 //the refType is passed into the handlers where a copy of its type is used to decode the json.
-var refType = TOProfileParameter(v13.ProfileParameterNullable{})
+var refType = TODeliveryServiceServer(tc.DeliveryServiceServer{})
 
-func GetRefType() *TOProfileParameter {
+func GetRefType() *TODeliveryServiceServer {
 	return &refType
 }
 
-func (pp TOProfileParameter) GetKeyFieldsInfo() []api.KeyFieldInfo {
-	return []api.KeyFieldInfo{{ProfileIDQueryParam, api.GetIntKey}, {ParameterIDQueryParam, api.GetIntKey}}
+/*
+# get all delivery services associated with a server (from deliveryservice_server table)
+$r->get( "/api/$version/servers/:id/deliveryservices" => [ id => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'Deliveryservice#get_deliveryservices_by_serverId', namespace => $namespace );
+
+# delivery service / server assignments
+$r->post("/api/$version/deliveryservices/:xml_id/servers")->over( authenticated => 1, not_ldap => 1 )
+->to( 'Deliveryservice#assign_servers', namespace => $namespace );
+$r->delete("/api/$version/deliveryservice_server/:dsId/:serverId" => [ dsId => qr/\d+/, serverId => qr/\d+/ ] )->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#remove_server_from_ds', namespace => $namespace );
+	# -- DELIVERYSERVICES: SERVERS
+	# Supports ?orderby=key
+	$r->get("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#index', namespace => $namespace );
+	$r->post("/api/$version/deliveryserviceserver")->over( authenticated => 1, not_ldap => 1 )->to( 'DeliveryServiceServer#assign_servers_to_ds', namespace => $namespace );
+
+		{1.2, http.MethodGet, `deliveryservices/{id}/servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.2, http.MethodGet, `deliveryservices/{id}/unassigned_servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.2, http.MethodGet, `deliveryservices/{id}/servers/eligible$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+
+*/
+
+func (dss TODeliveryServiceServer) GetKeyFieldsInfo() []api.KeyFieldInfo {
+	return []api.KeyFieldInfo{{"deliveryservice", api.GetIntKey}, {"server", api.GetIntKey}}
 }
 
 //Implementation of the Identifier, Validator interface functions
-func (pp TOProfileParameter) GetKeys() (map[string]interface{}, bool) {
-	if pp.ProfileID == nil {
-		return map[string]interface{}{ProfileIDQueryParam: 0}, false
+func (dss TODeliveryServiceServer) GetKeys() (map[string]interface{}, bool) {
+	if dss.DeliveryService == nil {
+		return map[string]interface{}{"deliveryservice": 0}, false
 	}
-	if pp.ParameterID == nil {
-		return map[string]interface{}{ParameterIDQueryParam: 0}, false
+	if dss.Server == nil {
+		return map[string]interface{}{"server": 0}, false
 	}
 	keys := make(map[string]interface{})
-	profileID := *pp.ProfileID
-	parameterID := *pp.ParameterID
+	ds_id := *dss.DeliveryService
+	server_id := *dss.Server
 
-	keys[ProfileIDQueryParam] = profileID
-	keys[ParameterIDQueryParam] = parameterID
+	keys["deliveryservice"] = ds_id
+	keys["server"] = server_id
 	return keys, true
 }
 
-func (pp *TOProfileParameter) GetAuditName() string {
-	if pp.ProfileID != nil {
-		return strconv.Itoa(*pp.ProfileID) + "-" + strconv.Itoa(*pp.ParameterID)
+func (dss *TODeliveryServiceServer) GetAuditName() string {
+	if dss.DeliveryService != nil {
+		return strconv.Itoa(*dss.DeliveryService) + "-" + strconv.Itoa(*dss.Server)
 	}
 	return "unknown"
 }
 
-func (pp *TOProfileParameter) GetType() string {
-	return "profileParameter"
+func (dss *TODeliveryServiceServer) GetType() string {
+	return "deliveryserviceServers"
 }
 
-func (pp *TOProfileParameter) SetKeys(keys map[string]interface{}) {
-	profId, _ := keys[ProfileIDQueryParam].(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.
-	pp.ProfileID = &profId
+func (dss *TODeliveryServiceServer) SetKeys(keys map[string]interface{}) {
+	ds_id, _ := keys["deliveryservice"].(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.
+	dss.DeliveryService = &ds_id
 
-	paramId, _ := keys[ParameterIDQueryParam].(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.
-	pp.ParameterID = &paramId
+	server_id, _ := keys["server"].(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.
+	dss.Server = &server_id
 }
 
 // Validate fulfills the api.Validator interface
-func (pp *TOProfileParameter) Validate(db *sqlx.DB) []error {
+func (dss *TODeliveryServiceServer) Validate(db *sqlx.DB) []error {
 
 	errs := validation.Errors{
-		"profile":   validation.Validate(pp.ProfileID, validation.Required),
-		"parameter": validation.Validate(pp.ParameterID, validation.Required),
+		"deliveryservice": validation.Validate(dss.DeliveryService, validation.Required),
+		"server":          validation.Validate(dss.Server, validation.Required),
 	}
 
 	return tovalidate.ToErrors(errs)
 }
 
-//The TOProfileParameter implementation of the Creator interface
+//The TODeliveryServiceServer implementation of the Creator interface
 //all implementations of Creator should use transactions and return the proper errorType
 //ParsePQUniqueConstraintError is used to determine if a profileparameter with conflicting values exists
 //if so, it will return an errorType of DataConflict and the type should be appended to the
 //generic error message returned
 //The insert sql returns the profile and lastUpdated values of the newly inserted profileparameter and have
 //to be added to the struct
-func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+func (dss *TODeliveryServiceServer) Create(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
 	rollbackTransaction := true
 	tx, err := db.Beginx()
 	defer func() {
@@ -127,7 +140,7 @@ func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error,
 		log.Error.Printf("could not begin transaction: %v", err)
 		return tc.DBError, tc.SystemError
 	}
-	resultRows, err := tx.NamedQuery(insertQuery(), pp)
+	resultRows, err := tx.NamedQuery(insertQuery(), dss)
 	if err != nil {
 		if pqErr, ok := err.(*pq.Error); ok {
 			err, eType := dbhelpers.ParsePQUniqueConstraintError(pqErr)
@@ -141,19 +154,19 @@ func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error,
 	}
 	defer resultRows.Close()
 
-	var profile int
-	var parameter int
+	var ds_id int
+	var server_id int
 	var lastUpdated tc.TimeNoMod
 	rowsAffected := 0
 	for resultRows.Next() {
 		rowsAffected++
-		if err := resultRows.Scan(&profile, &parameter, &lastUpdated); err != nil {
-			log.Error.Printf("could not scan profile from insert: %s\n", err)
+		if err := resultRows.Scan(&ds_id, &server_id, &lastUpdated); err != nil {
+			log.Error.Printf("could not scan dss from insert: %s\n", err)
 			return tc.DBError, tc.SystemError
 		}
 	}
 	if rowsAffected == 0 {
-		err = errors.New("no profile_parameter was inserted, no profile+parameter was returned")
+		err = errors.New("no deliveryServiceServer was inserted, nothing to return")
 		log.Errorln(err)
 		return tc.DBError, tc.SystemError
 	}
@@ -163,8 +176,8 @@ func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error,
 		return tc.DBError, tc.SystemError
 	}
 
-	pp.SetKeys(map[string]interface{}{ProfileIDQueryParam: profile, ParameterIDQueryParam: parameter})
-	pp.LastUpdated = &lastUpdated
+	dss.SetKeys(map[string]interface{}{"deliveryservice": ds_id, "server": server_id})
+	dss.LastUpdated = &lastUpdated
 	err = tx.Commit()
 	if err != nil {
 		log.Errorln("Could not commit transaction: ", err)
@@ -175,57 +188,59 @@ func (pp *TOProfileParameter) Create(db *sqlx.DB, user auth.CurrentUser) (error,
 }
 
 func insertQuery() string {
-	query := `INSERT INTO profile_parameter (
-profile,
-parameter) VALUES (
-:profile_id,
-:parameter_id) RETURNING profile, parameter, last_updated`
+	query := `INSERT INTO deliveryservice_server (
+deliveryservice,
+server) VALUES (
+:ds_id,
+:server_id) RETURNING deliveryservice, server, last_updated`
 	return query
 }
 
-func (pp *TOProfileParameter) Read(db *sqlx.DB, parameters map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
-	var rows *sqlx.Rows
+func (dss *TODeliveryServiceServer) Read(db *sqlx.DB, params map[string]string, user auth.CurrentUser) ([]interface{}, []error, tc.ApiErrorType) {
+	idstr, ok := params["id"]
 
-	// Query Parameters to Database Query column mappings
-	// see the fields mapped in the SQL query
-	queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
-		"profileId":   dbhelpers.WhereColumnInfo{"pp.profile", nil},
-		"parameterId": dbhelpers.WhereColumnInfo{"pp.parameter", nil},
-		"lastUpdated": dbhelpers.WhereColumnInfo{"pp.last_updated", nil},
+	if !ok {
+		log.Errorf("Deliveryservice Server Id missing")
+		return nil, []error{errors.New("Deliverservice id is required.")}, tc.DataMissingError
 	}
+	id, err := strconv.Atoi(idstr)
 
-	where, orderBy, queryValues, errs := dbhelpers.BuildWhereAndOrderBy(parameters, queryParamsToQueryCols)
-	if len(errs) > 0 {
-		return nil, errs, tc.DataConflictError
+	if err != nil {
+		log.Errorf("Deliveryservice Server Id is not an integer")
+		return nil, []error{errors.New("Deliverservice id is not an integer.")}, tc.SystemError
 	}
 
-	query := selectQuery() + where + orderBy
+	query := selectQuery()
 	log.Debugln("Query is ", query)
 
-	rows, err := db.NamedQuery(query, queryValues)
+	rows, err := db.Queryx(query, id)
 	if err != nil {
-		log.Errorf("Error querying Parameters: %v", err)
+		log.Errorf("Error querying DeliveryserviceServers: %v", err)
 		return nil, []error{tc.DBError}, tc.SystemError
 	}
 	defer rows.Close()
 
-	params := []interface{}{}
+	servers := []interface{}{}
 	for rows.Next() {
-		var p v13.ProfileParameterNullable
-		if err = rows.StructScan(&p); err != nil {
-			log.Errorf("error parsing pp rows: %v", err)
+		var s tc.DssServer
+		if err = rows.StructScan(&s); err != nil {
+			log.Errorf("error parsing dss rows: %v", err)
 			return nil, []error{tc.DBError}, tc.SystemError
 		}
-		params = append(params, p)
+		hiddenField := ""
+		if user.PrivLevel < auth.PrivLevelAdmin {
+			s.ILOPassword = &hiddenField
+		}
+		servers = append(servers, s)
 	}
 
-	return params, []error{}, tc.NoError
+	return servers, []error{}, tc.NoError
 
 }
 
 //The Parameter implementation of the Deleter interface
 //all implementations of Deleter should use transactions and return the proper errorType
-func (pp *TOProfileParameter) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
+func (dss *TODeliveryServiceServer) Delete(db *sqlx.DB, user auth.CurrentUser) (error, tc.ApiErrorType) {
 	rollbackTransaction := true
 	tx, err := db.Beginx()
 	defer func() {
@@ -242,8 +257,8 @@ func (pp *TOProfileParameter) Delete(db *sqlx.DB, user auth.CurrentUser) (error,
 		log.Error.Printf("could not begin transaction: %v", err)
 		return tc.DBError, tc.SystemError
 	}
-	log.Debugf("about to run exec query: %s with parameter: %++v", deleteQuery(), pp)
-	result, err := tx.NamedExec(deleteQuery(), pp)
+	log.Debugf("about to run exec query: %s with parameter: %++v", deleteQuery(), dss)
+	result, err := tx.NamedExec(deleteQuery(), dss)
 	if err != nil {
 		log.Errorf("received error: %++v from delete execution", err)
 		return tc.DBError, tc.SystemError
@@ -267,27 +282,71 @@ func (pp *TOProfileParameter) Delete(db *sqlx.DB, user auth.CurrentUser) (error,
 	rollbackTransaction = false
 	return nil, tc.NoError
 }
-
 func selectQuery() string {
 
-	query := `SELECT
-pp.last_updated,
-pp.profile profile_id,
-pp.parameter parameter_id,
-prof.name profile,
-param.name parameter
-FROM profile_parameter pp
-JOIN profile prof ON prof.id = pp.profile
-JOIN parameter param ON param.id = pp.parameter`
-	return query
+	const JumboFrameBPS = 9000
+
+	// COALESCE is needed to default values that are nil in the database
+	// because Go does not allow that to marshal into the struct
+	selectStmt := `SELECT
+	cg.name as cachegroup,
+	s.cachegroup as cachegroup_id,
+	s.cdn_id,
+	cdn.name as cdn_name,
+	s.domain_name,
+	s.guid,
+	s.host_name,
+	s.https_port,
+	s.id,
+	s.ilo_ip_address,
+	s.ilo_ip_gateway,
+	s.ilo_ip_netmask,
+	s.ilo_password,
+	s.ilo_username,
+	COALESCE(s.interface_mtu, ` + strconv.Itoa(JumboFrameBPS) + `) as interface_mtu,
+	s.interface_name,
+	s.ip6_address,
+	s.ip6_gateway,
+	s.ip_address,
+	s.ip_gateway,
+	s.ip_netmask,
+	s.last_updated,
+	s.mgmt_ip_address,
+	s.mgmt_ip_gateway,
+	s.mgmt_ip_netmask,
+	s.offline_reason,
+	pl.name as phys_location,
+	s.phys_location as phys_location_id,
+	p.name as profile,
+	p.description as profile_desc,
+	s.profile as profile_id,
+	s.rack,
+	s.router_host_name,
+	s.router_port_name,
+	st.name as status,
+	s.status as status_id,
+	s.tcp_port,
+	t.name as server_type,
+	s.type as server_type_id,
+	s.upd_pending as upd_pending
+	FROM server s
+	JOIN cachegroup cg ON s.cachegroup = cg.id
+	JOIN cdn cdn ON s.cdn_id = cdn.id
+	JOIN phys_location pl ON s.phys_location = pl.id
+	JOIN profile p ON s.profile = p.id
+	JOIN status st ON s.status = st.id
+	JOIN type t ON s.type = t.id
+	WHERE s.id in (select server from deliveryservice_server where deliveryservice = $1)`
+
+	return selectStmt
 }
 
 func updateQuery() string {
 	query := `UPDATE
-profile_parameter SET
-profile=:profile_id,
-parameter=:parameter_id
-WHERE profile=:profile_id AND 
+	profile_parameter SET
+	profile=:profile_id,
+	parameter=:parameter_id
+	WHERE profile=:profile_id AND 
       parameter = :parameter_id 
       RETURNING last_updated`
 	return query
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
index 9e7ce20..58d556e 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers_test.go
@@ -1,4 +1,4 @@
-package profileparameter
+package servers
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,93 +19,4 @@ package profileparameter
  * under the License.
  */
 
-import (
-	"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 getTestProfileParameters() []v13.ProfileParameterNullable {
-	pps := []v13.ProfileParameterNullable{}
-	lastUpdated := tc.TimeNoMod{}
-	lastUpdated.Scan(time.Now())
-	profileID := 1
-	parameterID := 1
-
-	pp := v13.ProfileParameterNullable{
-		LastUpdated: &lastUpdated,
-		ProfileID:   &profileID,
-		ParameterID: &parameterID,
-	}
-	pps = append(pps, pp)
-
-	pp2 := pp
-	pp2.ProfileID = &profileID
-	pp2.ParameterID = &parameterID
-	pps = append(pps, pp2)
-
-	return pps
-}
-
-func TestGetProfileParameters(t *testing.T) {
-	mockDB, mock, err := sqlmock.New()
-	if err != nil {
-		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
-	}
-	defer mockDB.Close()
-
-	db := sqlx.NewDb(mockDB, "sqlmock")
-	defer db.Close()
-
-	testPPs := getTestProfileParameters()
-	cols := test.ColsFromStructByTag("db", v13.ProfileParameterNullable{})
-	rows := sqlmock.NewRows(cols)
-
-	for _, ts := range testPPs {
-		rows = rows.AddRow(
-			ts.LastUpdated,
-			ts.Profile,
-			ts.ProfileID,
-			ts.Parameter,
-			ts.ParameterID,
-		)
-	}
-	mock.ExpectQuery("SELECT").WillReturnRows(rows)
-	v := map[string]string{"profile": "1"}
-
-	pps, errs, _ := refType.Read(db, v, auth.CurrentUser{})
-	if len(errs) > 0 {
-		t.Errorf("profileparameter.Read expected: no errors, actual: %v", errs)
-	}
-
-	if len(pps) != 2 {
-		t.Errorf("profileparameter.Read expected: len(pps) == 2, actual: %v", len(pps))
-	}
-
-}
-
-func TestInterfaces(t *testing.T) {
-	var i interface{}
-	i = &TOProfileParameter{}
-
-	if _, ok := i.(api.Creator); !ok {
-		t.Errorf("ProfileParameter must be Creator")
-	}
-	if _, ok := i.(api.Reader); !ok {
-		t.Errorf("ProfileParameter must be Reader")
-	}
-	if _, ok := i.(api.Deleter); !ok {
-		t.Errorf("ProfileParameter must be Deleter")
-	}
-	if _, ok := i.(api.Identifier); !ok {
-		t.Errorf("ProfileParameter must be Identifier")
-	}
-}
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index b82fa59..125f7ef 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -38,6 +38,7 @@ import (
 	"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"
+	dsserver "github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice/servers"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice/request/comment"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/division"
 	"github.com/apache/incubator-trafficcontrol/traffic_ops/traffic_ops_golang/hwinfo"
@@ -148,6 +149,11 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{1.1, http.MethodPost, `regions/?$`, api.CreateHandler(region.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
 		{1.1, http.MethodDelete, `regions/{id}$`, api.DeleteHandler(region.GetRefType(), d.DB), auth.PrivLevelOperations, Authenticated, nil},
 
+		// get all edge servers associated with a delivery service (from deliveryservice_server table)
+		{1.2, http.MethodGet, `deliveryservices/{id}/servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.2, http.MethodGet, `deliveryservices/{id}/unassigned_servers$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+		{1.2, http.MethodGet, `deliveryservices/{id}/servers/eligible$`, api.ReadHandler(dsserver.GetRefType(), d.DB),auth.PrivLevelReadOnly, Authenticated, nil},
+
 		//Server
 		{1.1, http.MethodGet, `servers/checks$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},
 		{1.1, http.MethodGet, `servers/details$`, handlerToFunc(proxyHandler), 0, NoAuth, []Middleware{}},

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