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(¶ms) 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(¶ms) - 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(¶ms) + 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(¶ms) - 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 <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>
