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

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 139bfb4522efbf9afa6f6053f0b4f2cbc4ff02e3
Author: mattjackson220 <[email protected]>
AuthorDate: Thu Oct 29 17:14:10 2020 -0600

    removed tenancy from service categories (#5225)
    
    * removed tenancy from service categories
    
    * show message on SC update
    
    * updates per comments
    
    * updates per comments
    
    * limited what characters are ok in service category
    
    * update per comment
    
    (cherry picked from commit 806a72484708e26e9cbf3c1dddcad9b40dd5436e)
---
 docs/source/api/v3/servicecategories.rst           |  16 +---
 lib/go-tc/service_category.go                      |   2 -
 lib/go-tc/tovalidate/rules.go                      |   6 ++
 ...03000000000_remove_service_category_tenancy.sql |  19 ++++
 .../testing/api/v3/servicecategories_test.go       |  93 ++++---------------
 .../deliveryservice/deliveryservices.go            |  44 ---------
 traffic_ops/traffic_ops_golang/routing/routes.go   |   2 +-
 .../servicecategory/servicecategories.go           | 102 +++++++++------------
 traffic_ops/traffic_ops_golang/tenant/tenancy.go   |  29 ------
 .../app/src/common/api/ServiceCategoryService.js   |   7 +-
 .../FormServiceCategoryController.js               |  24 +----
 .../edit/FormEditServiceCategoryController.js      |   2 +-
 .../serviceCategory/form.serviceCategory.tpl.html  |  15 +--
 .../new/FormNewServiceCategoryController.js        |   4 +-
 .../table.serviceCategories.tpl.html               |   2 -
 15 files changed, 101 insertions(+), 266 deletions(-)

diff --git a/docs/source/api/v3/servicecategories.rst 
b/docs/source/api/v3/servicecategories.rst
index 8508ff9..79de8d6 100644
--- a/docs/source/api/v3/servicecategories.rst
+++ b/docs/source/api/v3/servicecategories.rst
@@ -37,8 +37,6 @@ Request Structure
     
+===========+===============================================================================================================+
     | name      | Filter for :term:`Service Categories` with this name         
                                                 |
     
+-----------+---------------------------------------------------------------------------------------------------------------+
-    | tenant    | Return only :term:`Service Categories` belonging to the 
tenant identified by this integral, unique identifier |
-    
+-----------+---------------------------------------------------------------------------------------------------------------+
     | orderby   | Choose the ordering of the results - must be the name of one 
of the fields of the objects in the ``response`` |
     |           | array                                                        
                                                 |
     
+-----------+---------------------------------------------------------------------------------------------------------------+
@@ -66,8 +64,6 @@ Response Structure
 ------------------
 :name:        This :term:`Service Category`'s name
 :lastUpdated: The date and time at which this :term:`Service Category` was 
last modified, in ISO format
-:tenantId:    An integral, unique identifier for the :term:`Tenant` that owns 
this :term:`Service Category`
-:tenant:      The name of the :term:`Tenant` that owns this :term:`Service 
Category`
 
 .. code-block:: http
     :caption: Response Example
@@ -88,9 +84,7 @@ Response Structure
         "response": [
             {
                 "lastUpdated": "2020-03-04 15:46:20-07",
-                "name": "SERVICE_CATEGORY_NAME",
-                "tenantId": 1,
-                "tenant": "TENANT_NAME"
+                "name": "SERVICE_CATEGORY_NAME"
             }
         ]
     }
@@ -106,7 +100,6 @@ Create a new service category.
 Request Structure
 -----------------
 :name:        This :term:`Service Category`'s name
-:tenantId:    An integral, unique identifier for the :term:`Tenant` that owns 
this :term:`Service Category`
 
 .. code-block:: http
     :caption: Request Example
@@ -121,15 +114,12 @@ Request Structure
 
     {
         "name": "SERVICE_CATEGORY_NAME",
-        "tenantId": 1,
     }
 
 Response Structure
 ------------------
 :name:        This :term:`Service Category`'s name
 :lastUpdated: The date and time at which this :term:`Service Category` was 
last modified, in ISO format
-:tenantId:    An integral, unique identifier for the :term:`Tenant` that owns 
this :term:`Service Category`
-:tenant:      The name of the :term:`Tenant` that owns this :term:`Service 
Category`
 
 .. code-block:: http
     :caption: Response Example
@@ -155,9 +145,7 @@ Response Structure
         ],
         "response": {
             "lastUpdated": "2020-03-11 14:12:20-06",
-            "name": "SERVICE_CATEGORY_NAME",
-            "tenantId": 1,
-            "tenant": null
+            "name": "SERVICE_CATEGORY_NAME"
         }
     }
 
diff --git a/lib/go-tc/service_category.go b/lib/go-tc/service_category.go
index eaf99dd..8b48d2d 100644
--- a/lib/go-tc/service_category.go
+++ b/lib/go-tc/service_category.go
@@ -34,6 +34,4 @@ type ServiceCategoryResponse struct {
 type ServiceCategory struct {
        LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"`
        Name        string    `json:"name" db:"name"`
-       TenantID    int       `json:"tenantId" db:"tenant_id"`
-       TenantName  string    `json:"tenant" db:"tenant"`
 }
diff --git a/lib/go-tc/tovalidate/rules.go b/lib/go-tc/tovalidate/rules.go
index e2f1555..e01b809 100644
--- a/lib/go-tc/tovalidate/rules.go
+++ b/lib/go-tc/tovalidate/rules.go
@@ -22,6 +22,7 @@ import (
 )
 
 var rxAlphanumericUnderscoreDash = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
+var rxAlphanumericDash = regexp.MustCompile(`^[a-zA-Z0-9\-]+$`)
 
 // NoSpaces returns true if the string has no spaces
 func NoSpaces(str string) bool {
@@ -38,6 +39,11 @@ func IsAlphanumericUnderscoreDash(str string) bool {
        return rxAlphanumericUnderscoreDash.MatchString(str)
 }
 
+// IsAlphanumericDash returns true if the string consists of only alphanumeric 
or dash characters.
+func IsAlphanumericDash(str string) bool {
+       return rxAlphanumericDash.MatchString(str)
+}
+
 // NoPeriods returns true if the string has no periods
 func NoPeriods(str string) bool {
        return !strings.ContainsAny(str, ".")
diff --git 
a/traffic_ops/app/db/migrations/2020103000000000_remove_service_category_tenancy.sql
 
b/traffic_ops/app/db/migrations/2020103000000000_remove_service_category_tenancy.sql
new file mode 100644
index 0000000..1b854a7
--- /dev/null
+++ 
b/traffic_ops/app/db/migrations/2020103000000000_remove_service_category_tenancy.sql
@@ -0,0 +1,19 @@
+/*
+       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
+ALTER TABLE IF EXISTS service_category DROP COLUMN tenant_id;
+
+-- +goose Down
+-- SQL section 'Down' is executed when this migration is rolled back
+ALTER TABLE service_category ADD COLUMN tenant_id BIGINT REFERENCES tenant(id);
diff --git a/traffic_ops/testing/api/v3/servicecategories_test.go 
b/traffic_ops/testing/api/v3/servicecategories_test.go
index e8c6d37..e484a30 100644
--- a/traffic_ops/testing/api/v3/servicecategories_test.go
+++ b/traffic_ops/testing/api/v3/servicecategories_test.go
@@ -24,11 +24,10 @@ import (
 
        "github.com/apache/trafficcontrol/lib/go-rfc"
        "github.com/apache/trafficcontrol/lib/go-tc"
-       toclient "github.com/apache/trafficcontrol/traffic_ops/client"
 )
 
 func TestServiceCategories(t *testing.T) {
-       WithObjs(t, []TCObj{Tenants, ServiceCategories, Users}, func() {
+       WithObjs(t, []TCObj{ServiceCategories}, func() {
                GetTestServiceCategoriesIMS(t)
                currentTime := time.Now().UTC().Add(-5 * time.Second)
                time := currentTime.Format(time.RFC1123)
@@ -38,7 +37,6 @@ func TestServiceCategories(t *testing.T) {
                SortTestServiceCategories(t)
                UpdateTestServiceCategories(t)
                GetTestServiceCategories(t)
-               ServiceCategoryTenancyTest(t)
                GetTestServiceCategoriesIMSAfterChange(t, header)
        })
 }
@@ -94,8 +92,6 @@ func GetTestServiceCategoriesIMSAfterChange(t *testing.T, 
header http.Header) {
 func CreateTestServiceCategories(t *testing.T) {
        // loop through service categories, assign FKs and create
        for _, sc := range testData.ServiceCategories {
-               tenant, _, err := TOSession.TenantByName(sc.TenantName)
-               sc.TenantID = tenant.ID
                resp, _, err := TOSession.CreateServiceCategory(sc)
                if err != nil {
                        t.Errorf("could not CREATE service category: %v", err)
@@ -143,14 +139,6 @@ func UpdateTestServiceCategories(t *testing.T) {
                t.Fatalf("cannot UPDATE Service Category, test data does not 
have service categories")
        }
 
-       tenants, _, err := TOSession.Tenants()
-       if err != nil {
-               t.Fatalf("Failed to get tenants: %v", err)
-       }
-       if len(tenants) < 2 {
-               t.Fatalf("Need at least two tenants to test changing tenant; 
got: %d", len(tenants))
-       }
-
        // Retrieve the Service Category by service category so we can get the 
id for the Update
        params := url.Values{}
        params.Add("name", firstServiceCategory.Name)
@@ -160,31 +148,21 @@ func UpdateTestServiceCategories(t *testing.T) {
        }
        if len(resp) > 0 {
                remoteServiceCategory := resp[0]
-
-               originalTenant := remoteServiceCategory.TenantID
-               found := false
-               for _, tenant := range tenants {
-                       if tenant.ID != originalTenant {
-                               remoteServiceCategory.TenantID = tenant.ID
-                               found = true
-                               break
-                       }
-               }
-               if !found {
-                       t.Fatal("Could not find tenant that isn't the same as 
the remote service category's tenant")
-               }
+               remoteServiceCategory.Name = "ServiceCategory2"
 
                var alert tc.Alerts
-               alert, _, err = 
TOSession.UpdateServiceCategoryByName(remoteServiceCategory.Name, 
remoteServiceCategory)
+               alert, _, err = 
TOSession.UpdateServiceCategoryByName(firstServiceCategory.Name, 
remoteServiceCategory)
                if err != nil {
                        t.Errorf("cannot UPDATE Service Category by name: %v - 
%v", err, alert)
                }
                t.Logf("alerts: %v", alert)
 
                // Retrieve the Service Category to check service category got 
updated
+               params := url.Values{}
+               params.Add("name", remoteServiceCategory.Name)
                resp, _, err = TOSession.GetServiceCategories(&params)
                if err != nil {
-                       t.Errorf("cannot GET Service Category by service 
category: %v - %v", firstServiceCategory.Name, err)
+                       t.Errorf("cannot GET Service Category by service 
category: %v - %v", remoteServiceCategory.Name, err)
                }
                if len(resp) < 1 {
                        t.Fatal("empty response getting Service Category after 
update")
@@ -192,57 +170,24 @@ func UpdateTestServiceCategories(t *testing.T) {
                        t.Errorf("expected a name to uniquely identify exactly 
one Service Category, got: %d", len(resp))
                }
 
-               respServiceCategory := resp[0]
-               if respServiceCategory.TenantID != 
remoteServiceCategory.TenantID {
-                       t.Errorf("results do not match; want: %d, got: %d", 
remoteServiceCategory.TenantID, respServiceCategory.TenantID)
-               }
-
-               // Set the name back to the fixture value so we can delete it 
after
-               remoteServiceCategory.TenantID = originalTenant
-               alert, _, err = 
TOSession.UpdateServiceCategoryByName(remoteServiceCategory.Name, 
remoteServiceCategory)
+               // revert back to original name
+               alert, _, err = 
TOSession.UpdateServiceCategoryByName(remoteServiceCategory.Name, 
firstServiceCategory)
                if err != nil {
                        t.Errorf("cannot UPDATE Service Category by name: %v - 
%v", err, alert)
                }
-       }
-}
-
-func ServiceCategoryTenancyTest(t *testing.T) {
-       var alert tc.Alerts
-       tenant3, _, err := TOSession.TenantByName("tenant3")
-       if err != nil {
-               t.Errorf("cannot GET Tenant3: %v", err)
-       }
+               t.Logf("alerts: %v", alert)
 
-       params := url.Values{}
-       serviceCategories, _, err := TOSession.GetServiceCategories(&params)
-       if err != nil {
-               t.Errorf("cannot GET Service Categories: %v", err)
-       }
-       for _, sc := range serviceCategories {
-               if sc.Name == "serviceCategory1" {
-                       alert, _, err = 
TOSession.UpdateServiceCategoryByName(sc.Name, sc)
-                       if err != nil {
-                               t.Errorf("cannot UPDATE Service Category by 
name: %v - %v", err, alert)
-                       }
-                       sc.TenantID = tenant3.ID
+               // Retrieve the Service Category to check service category got 
updated
+               params = url.Values{}
+               params.Add("name", firstServiceCategory.Name)
+               resp, _, err = TOSession.GetServiceCategories(&params)
+               if err != nil {
+                       t.Errorf("cannot GET Service Category by service 
category: %v - %v", firstServiceCategory.Name, err)
                }
-       }
-
-       toReqTimeout := time.Second * 
time.Duration(Config.Default.Session.TimeoutInSecs)
-       tenant4TOClient, _, err := toclient.LoginWithAgent(TOSession.URL, 
"tenant4user", "pa$$word", true, "to-api-v3-client-tests/tenant4user", true, 
toReqTimeout)
-       if err != nil {
-               t.Fatalf("failed to log in with tenant4user: %v", err.Error())
-       }
-
-       serviceCategoriesReadableByTenant4, _, err := 
tenant4TOClient.GetServiceCategories(&params)
-       if err != nil {
-               t.Error("tenant4user cannot GET service categories")
-       }
-
-       // assert that tenant4user cannot read service categories outside of 
its tenant
-       for _, sc := range serviceCategoriesReadableByTenant4 {
-               if sc.Name == "serviceCategory1" {
-                       t.Error("expected tenant4 to be unable to read service 
categories from tenant 3")
+               if len(resp) < 1 {
+                       t.Fatal("empty response getting Service Category after 
update")
+               } else if len(resp) > 1 {
+                       t.Errorf("expected a name to uniquely identify exactly 
one Service Category, got: %d", len(resp))
                }
        }
 }
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index fe7a665..5bd05fd 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -194,28 +194,6 @@ func CreateV30(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       if ds.ServiceCategory != nil {
-               serviceCategoryTenantId, exists, err := 
tenant.GetServiceCategoryTenantIDByNameTx(inf.Tx.Tx, *ds.ServiceCategory)
-               if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("getting service category 
tenancy"))
-                       return
-               }
-               if !exists {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("service category "+*ds.ServiceCategory+" does not exist"), nil)
-                       return
-               }
-
-               ok, err := tenant.CrossReferenceTenancy(inf.Tx.Tx, 
serviceCategoryTenantId, *ds.TenantID)
-               if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("cross referencing service 
category tenancy with delivery service"))
-                       return
-               }
-               if !ok {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("delivery service tenant does not have access to this service 
category"), nil)
-                       return
-               }
-       }
-
        res, status, userErr, sysErr := createV30(w, r, inf, ds)
        if userErr != nil || sysErr != nil {
                api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
@@ -629,28 +607,6 @@ func UpdateV30(w http.ResponseWriter, r *http.Request) {
        }
        ds.ID = &id
 
-       if ds.ServiceCategory != nil {
-               serviceCategoryTenantId, exists, err := 
tenant.GetServiceCategoryTenantIDByNameTx(inf.Tx.Tx, *ds.ServiceCategory)
-               if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("getting service category 
tenancy"))
-                       return
-               }
-               if !exists {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("service category "+*ds.ServiceCategory+" does not exist"), nil)
-                       return
-               }
-
-               ok, err := tenant.CrossReferenceTenancy(inf.Tx.Tx, 
serviceCategoryTenantId, *ds.TenantID)
-               if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("cross referencing service 
category tenancy with delivery service"))
-                       return
-               }
-               if !ok {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("delivery service tenant does not have access to this service 
category"), nil)
-                       return
-               }
-       }
-
        res, status, userErr, sysErr := updateV30(w, r, inf, &ds)
        if userErr != nil || sysErr != nil {
                api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go 
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 9bf5461..4742097 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -420,7 +420,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, 
http.Handler, error) {
 
                //ServiceCategories
                {api.Version{3, 0}, http.MethodGet, `service_categories/?$`, 
api.ReadHandler(&servicecategory.TOServiceCategory{}), auth.PrivLevelReadOnly, 
Authenticated, nil, 1085181543, noPerlBypass},
-               {api.Version{3, 0}, http.MethodPut, 
`service_categories/{name}/?$`, 
api.UpdateHandler(&servicecategory.TOServiceCategory{}), 
auth.PrivLevelOperations, Authenticated, nil, 306369141, noPerlBypass},
+               {api.Version{3, 0}, http.MethodPut, 
`service_categories/{name}/?$`, servicecategory.Update, 
auth.PrivLevelOperations, Authenticated, nil, 306369141, noPerlBypass},
                {api.Version{3, 0}, http.MethodPost, `service_categories/?$`, 
api.CreateHandler(&servicecategory.TOServiceCategory{}), 
auth.PrivLevelOperations, Authenticated, nil, 553713801, noPerlBypass},
                {api.Version{3, 0}, http.MethodDelete, 
`service_categories/{name}$`, 
api.DeleteHandler(&servicecategory.TOServiceCategory{}), 
auth.PrivLevelOperations, Authenticated, nil, 1325382238, noPerlBypass},
 
diff --git 
a/traffic_ops/traffic_ops_golang/servicecategory/servicecategories.go 
b/traffic_ops/traffic_ops_golang/servicecategory/servicecategories.go
index 41ef03f..10b55e5 100644
--- a/traffic_ops/traffic_ops_golang/servicecategory/servicecategories.go
+++ b/traffic_ops/traffic_ops_golang/servicecategory/servicecategories.go
@@ -20,9 +20,10 @@ package servicecategory
  */
 
 import (
+       "database/sql"
+       "encoding/json"
        "errors"
        "net/http"
-       "strconv"
        "time"
 
        "github.com/apache/trafficcontrol/lib/go-tc"
@@ -30,7 +31,6 @@ import (
        "github.com/apache/trafficcontrol/lib/go-util"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
-       "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
        "github.com/go-ozzo/ozzo-validation"
 )
 
@@ -68,9 +68,6 @@ func (serviceCategory TOServiceCategory) GetAuditName() 
string {
        if serviceCategory.Name != "" {
                return serviceCategory.Name
        }
-       if serviceCategory.TenantID != 0 {
-               return strconv.Itoa(serviceCategory.TenantID)
-       }
        return "unknown"
 }
 
@@ -97,9 +94,7 @@ func (serviceCategory TOServiceCategory) GetType() string {
 
 func (serviceCategory *TOServiceCategory) ParamColumns() 
map[string]dbhelpers.WhereColumnInfo {
        return map[string]dbhelpers.WhereColumnInfo{
-               "name":       dbhelpers.WhereColumnInfo{"sc.name", nil},
-               "tenantId":   dbhelpers.WhereColumnInfo{"sc.tenant_id", 
api.IsInt},
-               "tenantName": dbhelpers.WhereColumnInfo{"sc.tenant", nil},
+               "name": dbhelpers.WhereColumnInfo{"sc.name", nil},
        }
 }
 
@@ -111,9 +106,9 @@ func (serviceCategory *TOServiceCategory) 
SelectMaxLastUpdatedQuery(where, order
 }
 
 func (serviceCategory TOServiceCategory) Validate() error {
+       nameRule := validation.NewStringRule(tovalidate.IsAlphanumericDash, 
"must consist of only alphanumeric or dash characters.")
        errs := validation.Errors{
-               "name":     validation.Validate(serviceCategory.Name, 
validation.NotNil, validation.Required),
-               "tenantId": validation.Validate(serviceCategory.TenantID, 
validation.Min(1)),
+               "name": validation.Validate(serviceCategory.Name, 
validation.Required, nameRule),
        }
        return util.JoinErrs(tovalidate.ToErrors(errs))
 }
@@ -123,89 +118,76 @@ func (serviceCategory *TOServiceCategory) Create() 
(error, error, int) {
 }
 
 func (serviceCategory *TOServiceCategory) Read(h http.Header, useIMS bool) 
([]interface{}, error, error, int, *time.Time) {
-       tenantIDs, err := 
tenant.GetUserTenantIDListTx(serviceCategory.APIInfo().Tx.Tx, 
serviceCategory.APIInfo().User.TenantID)
-       if err != nil {
-               return nil, nil, errors.New("getting tenant list for user: " + 
err.Error()), http.StatusInternalServerError, nil
-       }
-
        api.DefaultSort(serviceCategory.APIInfo(), "name")
        serviceCategories, userErr, sysErr, errCode, maxTime := 
api.GenericRead(h, serviceCategory, useIMS)
        if userErr != nil || sysErr != nil {
                return nil, userErr, sysErr, errCode, nil
        }
 
-       filteredServiceCategories := []interface{}{}
-       for _, sc := range serviceCategories {
-               scToCheck := sc.(*tc.ServiceCategory)
-               if checkTenancy(scToCheck, tenantIDs) {
-                       filteredServiceCategories = 
append(filteredServiceCategories, scToCheck)
-               }
+       return serviceCategories, nil, nil, errCode, maxTime
+}
+
+func Update(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"name"}, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
        }
+       defer inf.Close()
 
-       dsIdParam := serviceCategory.APIInfo().Params["dsId"]
+       name := inf.Params["name"]
 
-       if dsIdParam != "" {
-               dsId, err := strconv.Atoi(dsIdParam)
-               if err != nil {
-                       return nil, errors.New("dsId query param must be an 
int"), nil, http.StatusBadRequest, nil
-               }
-               dsTenantId, _, err := 
tenant.GetDSTenantIDByIDTx(serviceCategory.APIInfo().Tx.Tx, dsId)
+       var newSC TOServiceCategory
+       if err := json.NewDecoder(r.Body).Decode(&newSC); err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
+               return
+       }
 
-               dsAllowedTenants, err := 
tenant.GetUserTenantIDListTx(serviceCategory.APIInfo().Tx.Tx, *dsTenantId)
-               if err != nil {
-                       return nil, nil, errors.New("getting tenant list for 
user: " + err.Error()), http.StatusInternalServerError, nil
-               }
+       if err := newSC.Validate(); err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
+               return
+       }
 
-               filteredSCByDS := []interface{}{}
-               for _, sc := range filteredServiceCategories {
-                       scToCheck := sc.(*tc.ServiceCategory)
-                       if checkTenancy(scToCheck, dsAllowedTenants) {
-                               filteredSCByDS = append(filteredSCByDS, 
scToCheck)
-                       }
+       var origSC TOServiceCategory
+       if err := inf.Tx.QueryRow(`SELECT name FROM service_category WHERE name 
= $1`, name).Scan(&origSC.Name); err != nil {
+               if err == sql.ErrNoRows {
+                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("no service category found with name "+name), nil)
+                       return
                }
-
-               return filteredSCByDS, nil, nil, errCode, maxTime
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, err)
+               return
        }
 
-       return filteredServiceCategories, nil, nil, errCode, maxTime
-}
-
-func checkTenancy(category *tc.ServiceCategory, tenantIDs []int) bool {
-       for _, tenantID := range tenantIDs {
-               if tenantID == category.TenantID {
-                       return true
-               }
+       resp, err := inf.Tx.Tx.Exec(updateQuery(), newSC.Name, name)
+       if err != nil {
+               userErr, sysErr, errCode = api.ParseDBError(err)
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
        }
-       return false
+       api.CreateChangeLogRawTx(api.ApiChange, api.Updated+" Service Category 
from "+name+" to "+newSC.Name, inf.User, inf.Tx.Tx)
+       api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Service Category update 
from "+name+" to "+newSC.Name+" was successful.", resp)
 }
 
-func (serviceCategory *TOServiceCategory) Update(h http.Header) (error, error, 
int) {
-       return api.GenericUpdate(h, serviceCategory)
-}
 func (serviceCategory *TOServiceCategory) Delete() (error, error, int) {
        return api.GenericDelete(serviceCategory)
 }
 
 func insertQuery() string {
-       return `INSERT INTO service_category (name, tenant_id) VALUES (:name, 
:tenant_id) RETURNING name, last_updated`
+       return `INSERT INTO service_category (name) VALUES (:name) RETURNING 
name, last_updated`
 }
 
 func selectQuery() string {
        return `SELECT
-sc.tenant_id,
-t.name as tenant,
 sc.last_updated,
 sc.name
-FROM service_category as sc
-LEFT JOIN tenant t ON sc.tenant_id = t.id`
+FROM service_category as sc`
 }
 
 func updateQuery() string {
        return `UPDATE
 service_category SET
-name=:name,
-tenant_id=:tenant_id
-WHERE name=:name RETURNING last_updated`
+name=$1
+WHERE name=$2 RETURNING last_updated`
 }
 
 func deleteQuery() string {
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go 
b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index e7827da..965562e 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -227,32 +227,3 @@ func GetDSTenantIDByIDTx(tx *sql.Tx, id int) (*int, bool, 
error) {
        }
        return tenantID, true, nil
 }
-
-// GetServiceCategoryTenantIDByNameTx returns the tenant ID, whether the 
service category exists, and any error.
-func GetServiceCategoryTenantIDByNameTx(tx *sql.Tx, name string) (int, bool, 
error) {
-       var tenantID int
-       if err := tx.QueryRow(`SELECT tenant_id FROM service_category where 
name = $1`, name).Scan(&tenantID); err != nil {
-               if err == sql.ErrNoRows {
-                       return 0, false, nil
-               }
-               return 0, false, fmt.Errorf("querying tenant ID for service 
category name '%v': %v", name, err)
-       }
-       return tenantID, true, nil
-}
-
-// CrossReferenceTenancy returns whether the tenantId is within the tenancy 
tree hierarchy of the tenantIdToReference
-// and any error.
-func CrossReferenceTenancy(tx *sql.Tx, tenantId int, tenantIdToReference int) 
(bool, error) {
-       allowedTenants, err := GetUserTenantIDListTx(tx, tenantIdToReference)
-       if err != nil {
-               return false, errors.New("getting tenant list: " + err.Error())
-       }
-
-       for _, tenantID := range allowedTenants {
-               if tenantID == tenantId {
-                       return true, nil
-               }
-       }
-       return false, nil
-
-}
diff --git a/traffic_portal/app/src/common/api/ServiceCategoryService.js 
b/traffic_portal/app/src/common/api/ServiceCategoryService.js
index 01b7263..9c1fd3a 100644
--- a/traffic_portal/app/src/common/api/ServiceCategoryService.js
+++ b/traffic_portal/app/src/common/api/ServiceCategoryService.js
@@ -55,10 +55,11 @@ var ServiceCategoryService = function($http, ENV, 
locationUtils, messageModel) {
         );
     };
 
-    this.updateServiceCategory = function(serviceCategory) {
-        return $http.put(ENV.api['root'] + 'service_categories/' + 
encodeURIComponent(serviceCategory.name), serviceCategory).then(
+    this.updateServiceCategory = function(serviceCategory, oldName) {
+        return $http.put(ENV.api['root'] + 'service_categories/' + 
encodeURIComponent(oldName), serviceCategory).then(
             function(result) {
-                messageModel.setMessages(result.data.alerts, false);
+                messageModel.setMessages(result.data.alerts, true);
+                               
locationUtils.navigateToPath('/service-categories/edit?name=' + 
encodeURIComponent(serviceCategory.name));
                 return result;            },
             function(err) {
                 messageModel.setMessages(err.data.alerts, false);
diff --git 
a/traffic_portal/app/src/common/modules/form/serviceCategory/FormServiceCategoryController.js
 
b/traffic_portal/app/src/common/modules/form/serviceCategory/FormServiceCategoryController.js
index 6f1cede..26014e8 100644
--- 
a/traffic_portal/app/src/common/modules/form/serviceCategory/FormServiceCategoryController.js
+++ 
b/traffic_portal/app/src/common/modules/form/serviceCategory/FormServiceCategoryController.js
@@ -17,22 +17,7 @@
  * under the License.
  */
 
-var FormServiceCategoryController = function(serviceCategory, $scope, 
$location, formUtils, stringUtils, locationUtils, tenantService, tenantUtils, 
userModel) {
-
-    var getTenants = function() {
-        tenantService.getTenant(userModel.user.tenantId)
-            .then(function(tenant) {
-                tenantService.getTenants()
-                    .then(function(tenants) {
-                        $scope.tenants = 
tenantUtils.hierarchySort(tenantUtils.groupTenantsByParent(tenants), 
tenant.parentId, []);
-                        tenantUtils.addLevels($scope.tenants);
-                    });
-            });
-    };
-
-    $scope.tenantLabel = function(tenant) {
-        return '-'.repeat(tenant.level) + ' ' + tenant.name;
-    };
+var FormServiceCategoryController = function(serviceCategory, $scope, 
$location, formUtils, stringUtils, locationUtils) {
 
     $scope.serviceCategory = serviceCategory;
 
@@ -48,12 +33,7 @@ var FormServiceCategoryController = 
function(serviceCategory, $scope, $location,
         $location.path('/service-categories/' + 
encodeURIComponent(serviceCategory.name) + '/delivery-services');
     };
 
-    var init = function () {
-        getTenants();
-    };
-    init();
-
 };
 
-FormServiceCategoryController.$inject = ['serviceCategory', '$scope', 
'$location', 'formUtils', 'stringUtils', 'locationUtils', 'tenantService', 
'tenantUtils', 'userModel'];
+FormServiceCategoryController.$inject = ['serviceCategory', '$scope', 
'$location', 'formUtils', 'stringUtils', 'locationUtils'];
 module.exports = FormServiceCategoryController;
diff --git 
a/traffic_portal/app/src/common/modules/form/serviceCategory/edit/FormEditServiceCategoryController.js
 
b/traffic_portal/app/src/common/modules/form/serviceCategory/edit/FormEditServiceCategoryController.js
index 8d078dc..b50d3ab 100644
--- 
a/traffic_portal/app/src/common/modules/form/serviceCategory/edit/FormEditServiceCategoryController.js
+++ 
b/traffic_portal/app/src/common/modules/form/serviceCategory/edit/FormEditServiceCategoryController.js
@@ -37,7 +37,7 @@ var FormEditServiceCategoryController = 
function(serviceCategory, $scope, $contr
     };
 
     $scope.save = function(serviceCategory) {
-        serviceCategoryService.updateServiceCategory(serviceCategory).
+        serviceCategoryService.updateServiceCategory(serviceCategory, 
$scope.serviceCategoryName).
             then(function() {
                 $scope.serviceCategoryName = 
angular.copy(serviceCategory.name);
                 $anchorScroll(); // scrolls window to top
diff --git 
a/traffic_portal/app/src/common/modules/form/serviceCategory/form.serviceCategory.tpl.html
 
b/traffic_portal/app/src/common/modules/form/serviceCategory/form.serviceCategory.tpl.html
index 58094a1..4427441 100644
--- 
a/traffic_portal/app/src/common/modules/form/serviceCategory/form.serviceCategory.tpl.html
+++ 
b/traffic_portal/app/src/common/modules/form/serviceCategory/form.serviceCategory.tpl.html
@@ -34,19 +34,10 @@ under the License.
             <div class="form-group" ng-class="{'has-error': 
hasError(serviceCategoryForm.name), 'has-feedback': 
hasError(serviceCategoryForm.name)}">
                 <label for="name" class="control-label col-md-2 col-sm-2 
col-xs-12">Category Name *</label>
                 <div class="col-md-10 col-sm-10 col-xs-12">
-                    <input id="name" name="name" type="text" 
class="form-control" ng-model="serviceCategory.name" 
ng-disabled="!settings.isNew" required autofocus>
+                    <input id="name" name="name" type="text" 
class="form-control" ng-model="serviceCategory.name" pattern="^[a-zA-Z0-9\-]+$" 
required autofocus>
                     <small class="input-error" 
ng-show="hasPropertyError(serviceCategoryForm.name, 
'required')">Required</small>
-                    <span ng-show="hasError(serviceCategoryForm.name)" 
class="form-control-feedback"><i class="fa fa-times"></i></span>
-                </div>
-            </div>
-            <div class="form-group" ng-class="{'has-error': 
hasError(serviceCategoryForm.tenantId), 'has-feedback': 
hasError(serviceCategoryForm.tenantId)}">
-                <label class="control-label col-md-2 col-sm-2 
col-xs-12">Tenant</label>
-                <div class="col-md-10 col-sm-10 col-xs-12">
-                    <select name="tenantId" class="form-control" 
ng-model="serviceCategory.tenantId" ng-options="tenant.id as 
tenantLabel(tenant) for tenant in tenants" required>
-                        <option hidden disabled selected 
value="">Select...</option>
-                    </select>
-                    <small class="input-error" 
ng-show="hasPropertyError(serviceCategoryForm.tenantId, 
'required')">Required</small>
-                    <small ng-show="serviceCategory.tenantId"><a 
href="/#!/tenants/{{serviceCategory.tenantId}}" target="_blank">View 
Details&nbsp;&nbsp;<i class="fa fs-xs fa-external-link"></i></a></small>
+                                       <small class="input-error" 
ng-show="hasPropertyError(serviceCategoryForm.name, 'pattern')">Must be a valid 
service category (no special characters, periods, underscores, or spaces and 
cannot begin or end with a hyphen)</small>
+                                       <span 
ng-show="hasError(serviceCategoryForm.name)" class="form-control-feedback"><i 
class="fa fa-times"></i></span>
                 </div>
             </div>
             <div class="modal-footer">
diff --git 
a/traffic_portal/app/src/common/modules/form/serviceCategory/new/FormNewServiceCategoryController.js
 
b/traffic_portal/app/src/common/modules/form/serviceCategory/new/FormNewServiceCategoryController.js
index b44c4e7..6830ada 100644
--- 
a/traffic_portal/app/src/common/modules/form/serviceCategory/new/FormNewServiceCategoryController.js
+++ 
b/traffic_portal/app/src/common/modules/form/serviceCategory/new/FormNewServiceCategoryController.js
@@ -29,8 +29,8 @@ var FormNewServiceCategoryController = 
function(serviceCategory, $scope, $contro
         saveLabel: 'Create'
     };
 
-    $scope.save = function(division) {
-        serviceCategoryService.createServiceCategory(serviceCategory)
+    $scope.save = function() {
+        serviceCategoryService.createServiceCategory(serviceCategory);
     };
 
 };
diff --git 
a/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
 
b/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
index db04b46..9b95dea 100644
--- 
a/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
+++ 
b/traffic_portal/app/src/common/modules/table/serviceCategories/table.serviceCategories.tpl.html
@@ -34,13 +34,11 @@ under the License.
             <thead>
             <tr class="headings">
                 <th>Name</th>
-                <th>Tenant</th>
             </tr>
             </thead>
             <tbody>
             <tr ng-click="editServiceCategory(sc.name)" ng-repeat="sc in 
::serviceCategories">
                 <td name="name" 
data-search="^{{::sc.name}}$">{{::sc.name}}</td>
-                <td name="tenant" 
data-search="^{{::sc.tenant}}$">{{::sc.tenant}}</td>
             </tr>
             </tbody>
         </table>

Reply via email to