From d4ad7fd687c9b44c0a0d85291a50af0d8bc4f9e0 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 5 Aug 2019 13:08:06 -0600 Subject: [PATCH 01/28] Rewrote capabilities endpoints to GO --- lib/go-tc/capabilities.go | 26 ++ .../capabilities/capabilities.go | 301 ++++++++++++++++++ .../traffic_ops_golang/routing/routes.go | 7 + 3 files changed, 334 insertions(+) create mode 100644 lib/go-tc/capabilities.go create mode 100644 traffic_ops/traffic_ops_golang/capabilities/capabilities.go diff --git a/lib/go-tc/capabilities.go b/lib/go-tc/capabilities.go new file mode 100644 index 0000000000..009c7f3de4 --- /dev/null +++ b/lib/go-tc/capabilities.go @@ -0,0 +1,26 @@ +package tc + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +type Capability struct { + Description string `json:"description"` + LastUpdated TimeNoMod `json:"lastUpdated"` + Name string `json:"name"` +} diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go new file mode 100644 index 0000000000..114ea602db --- /dev/null +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -0,0 +1,301 @@ +package capabilities + +/* + * 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 "database/sql" +import "encoding/json" +import "errors" +import "fmt" +import "net/http" + +import "github.com/apache/trafficcontrol/lib/go-tc" +import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" +// import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" + +const readQuery = ` +SELECT description, + last_updated, + name +FROM capability +` + +const createQuery = ` +INSERT INTO capability (name, description) +VALUES ($1, $2) +RETURNING description, last_updated, name +` + +const replaceQuery = ` +UPDATE capability +SET name=$1, description=$2 +WHERE name=$3 +RETURNING description, last_updated, name +` + +const deleteQuery = ` +DELETE FROM capability +WHERE name=$1 +RETURNING description, last_updated, name +` + +func Read(w http.ResponseWriter, r *http.Request) { + inf, sysErr, userErr, errCode := api.NewInfo(r, nil, nil) + tx := inf.Tx.Tx + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + + var rows *sql.Rows + var err error + if name, ok := inf.Params["name"]; ok { + rows, err = tx.Query(readQuery + "WHERE name=$1", name) + } else { + rows, err = tx.Query(readQuery) + } + if err != nil && err != sql.ErrNoRows { + errCode = http.StatusInternalServerError + sysErr = fmt.Errorf("querying capabilities: %v", err) + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + defer rows.Close() + + caps := []tc.Capability{} + for rows.Next() { + cap := tc.Capability{} + if err := rows.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { + errCode = http.StatusInternalServerError + sysErr = fmt.Errorf("Parsing database response: %v", err) + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + caps = append(caps, cap) + } + + respBts, err := json.Marshal(struct{R []tc.Capability `json:"response"`}{caps}) + if err != nil { + errCode = http.StatusInternalServerError + sysErr = fmt.Errorf("Marshaling response: %v", err) + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + w.Header().Set(tc.ContentType, tc.ApplicationJson) + w.Write(respBts) + w.Write([]byte{'\n'}) +} + +func Create(w http.ResponseWriter, r *http.Request) { + inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name", "description"}, nil) + tx := inf.Tx.Tx + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + decoder := json.NewDecoder(r.Body) + var cap tc.Capability + if err := decoder.Decode(&cap); err != nil { + sysErr = fmt.Errorf("Decoding request body: %v") + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + if cap.Name == "" { + userErr = errors.New("'name' must be defined! (and not empty)") + errCode = http.StatusBadRequest + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + + if cap.Description == "" { + userErr = errors.New("'description' must be defined! (and not empty)") + errCode = http.StatusBadRequest + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + + if ok, err := capabilityNameExists(cap.Name, tx); err != nil { + sysErr = fmt.Errorf("Checking for capability %s's existence: %v", cap.Name, err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } else if ok { + userErr = fmt.Errorf("Capability '%s' already exists!", cap.Name) + errCode = http.StatusConflict + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + + row := tx.QueryRow(createQuery, cap.Name, cap.Description) + if err := row.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { + sysErr = fmt.Errorf("Inserting capability: %v", err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability created."}} + responseObj := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} + respBts, err := json.Marshal(responseObj) + if err != nil { + errCode = http.StatusInternalServerError + sysErr = fmt.Errorf("Marshaling response: %v", err) + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + w.Header().Set(tc.ContentType, tc.ApplicationJson) + w.Write(respBts) + w.Write([]byte{'\n'}) +} + +func Replace(w http.ResponseWriter, r *http.Request) { + inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name"}, nil) + tx := inf.Tx.Tx + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + decoder := json.NewDecoder(r.Body) + var cap tc.Capability + if err := decoder.Decode(&cap); err != nil { + userErr = fmt.Errorf("Couldn't parse capacity: %v", err) + errCode = http.StatusBadRequest + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + + if cap.Name != inf.Params["name"] { + if ok, err := capabilityNameExists(cap.Name, tx); err != nil { + sysErr = fmt.Errorf("Checking for capability %s's existence: %v", cap.Name, err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } else if ok { + errCode = http.StatusConflict + userErr = fmt.Errorf("A capability named '%s' already exists!", cap.Name) + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + } + + row := tx.QueryRow(replaceQuery, cap.Name, cap.Description, inf.Params["name"]) + if err := row.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { + if err == sql.ErrNoRows { + errCode = http.StatusNotFound + userErr = fmt.Errorf("No capability '%s' found!", inf.Params["name"]) + } else { + errCode = http.StatusInternalServerError + sysErr = fmt.Errorf("Replacing capability '%s' with '%s': %v", inf.Params["name"], cap.Name, err) + } + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + + alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability was updated."}} + resp := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} + respBts, err := json.Marshal(resp) + if err != nil { + sysErr = fmt.Errorf("Marshaling response: %v", err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + w.Header().Set(tc.ContentType, tc.ApplicationJson) + w.Write(respBts) + w.Write([]byte{'\n'}) +} + +func Delete(w http.ResponseWriter, r *http.Request) { + inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name"}, nil) + tx := inf.Tx.Tx + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + capName := inf.Params["name"] + + row := tx.QueryRow(`SELECT COUNT(*) FROM api_capability WHERE capability=$1`, capName) + var num uint + if err := row.Scan(&num); err != nil && err != sql.ErrNoRows { + sysErr = fmt.Errorf("Checking for API routes linked to capability %s: %v", capName, err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + if num > 0 { + userErr = fmt.Errorf("Capability '%s' is used by %d API routes; cannot be deleted!", capName, num) + errCode = http.StatusConflict + api.HandleErr(w, r, tx, errCode, userErr, nil) + return + } + + row = tx.QueryRow(deleteQuery, capName) + var cap tc.Capability + if err := row.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { + if err == sql.ErrNoRows { + errCode = http.StatusNotFound + userErr = fmt.Errorf("No capability '%s' found!", capName) + } else { + sysErr = fmt.Errorf("Deleting capability %s: %v", capName, err) + errCode = http.StatusInternalServerError + } + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + + alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability deleted."}} + resp := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} + respBts, err := json.Marshal(resp) + if err != nil { + sysErr = fmt.Errorf("Marshaling response: %v", err) + errCode = http.StatusInternalServerError + api.HandleErr(w, r, tx, errCode, nil, sysErr) + return + } + + w.Header().Set(tc.ContentType, tc.ApplicationJson) + w.Write(append(respBts, '\n')) +} + +func capabilityNameExists(c string, tx *sql.Tx) (bool, error) { + row := tx.QueryRow(`SELECT name FROM capability WHERE name=$1`, c) + var n string + if err := row.Scan(&n); err != nil { + if err == sql.ErrNoRows { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 50f691b4e6..9d86d0b25e 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -46,6 +46,7 @@ import ( "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachegroup" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachegroupparameter" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachesstats" + "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/capabilities" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cdn" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cdnfederation" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/coordinate" @@ -168,6 +169,12 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) { {1.1, http.MethodGet, `cachegroups/{id}/unassigned_parameters/?(\.json)?$`, api.ReadHandler(&cachegroupparameter.TOCacheGroupUnassignedParameter{}), auth.PrivLevelReadOnly, Authenticated, nil, 1457339250, perlBypass}, {1.1, http.MethodDelete, `cachegroupparameters/{cachegroupID}/{parameterId}$`, api.DeleteHandler(&cachegroupparameter.TOCacheGroupParameter{}), auth.PrivLevelOperations, Authenticated, nil, 912449733, perlBypass}, + //Capabilities + {1.1, http.MethodGet, `capabilities(/|\.json)?$`, capabilities.Read, auth.PrivLevelReadOnly, Authenticated, nil}, + {1.1, http.MethodPost, `capabilities(/|\.json)?$`, capabilities.Create, auth.PrivLevelOperations, Authenticated, nil}, + {1.4, http.MethodPut, `capabilities$`, capabilities.Replace, auth.PrivLevelOperations, Authenticated, nil}, + {1.4, http.MethodDelete, `capabilities$`, capabilities.Delete, auth.PrivLevelOperations, Authenticated, nil}, + //CDN {1.1, http.MethodGet, `cdns/name/{name}/sslkeys/?(\.json)?$`, cdn.GetSSLKeys, auth.PrivLevelAdmin, Authenticated, nil, 1278581772, noPerlBypass}, {1.1, http.MethodGet, `cdns/metric_types`, notImplementedHandler, 0, NoAuth, nil, 683165463, noPerlBypass}, // MUST NOT end in $, because the 1.x route is longer From 5068b25c5b21d3e30238c404213283c6064c31fb Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 13 Aug 2019 12:01:45 -0600 Subject: [PATCH 02/28] Fixed incorrect parameter contraint --- traffic_ops/traffic_ops_golang/capabilities/capabilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 114ea602db..fb33e7ffa4 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -107,7 +107,7 @@ func Read(w http.ResponseWriter, r *http.Request) { } func Create(w http.ResponseWriter, r *http.Request) { - inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name", "description"}, nil) + inf, sysErr, userErr, errCode := api.NewInfo(r, nil, nil) tx := inf.Tx.Tx if userErr != nil || sysErr != nil { api.HandleErr(w, r, tx, errCode, userErr, sysErr) From ad39fd556980398c2d96b1ed503b52e4f2db4f18 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 13 Aug 2019 12:39:43 -0600 Subject: [PATCH 03/28] Added godoc --- lib/go-tc/capabilities.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/go-tc/capabilities.go b/lib/go-tc/capabilities.go index 009c7f3de4..d5d0d68365 100644 --- a/lib/go-tc/capabilities.go +++ b/lib/go-tc/capabilities.go @@ -19,8 +19,20 @@ package tc * under the License. */ +// Capability reflects the ability of a user in ATC to perform some operation. +// +// In practice, they are assigned to relevant Traffic Ops API endpoints - to describe the +// capabilites of said endpoint - and to user permission Roles - to describe the capabilities +// afforded by said Role. Note that enforcement of Capability-based permisions is not currently +// implemented. type Capability struct { Description string `json:"description"` LastUpdated TimeNoMod `json:"lastUpdated"` Name string `json:"name"` } + +// CapabilitiesResponse models the structure of a minimal response from the Capabilities API in +// Traffic Ops. +type CapabilitiesResponse struct { + Response []Capability `json:"response"` +} From b0d2294b27bdcdf035915fcd5756c0951e47e614 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 13 Aug 2019 13:05:50 -0600 Subject: [PATCH 04/28] Added support in the Go Client for capabilities --- traffic_ops/client/capability.go | 121 +++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 traffic_ops/client/capability.go diff --git a/traffic_ops/client/capability.go b/traffic_ops/client/capability.go new file mode 100644 index 0000000000..71c5c53e7f --- /dev/null +++ b/traffic_ops/client/capability.go @@ -0,0 +1,121 @@ +package client + +/* + * 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 "encoding/json" +import "fmt" +import "net" +import "net/http" + +import "github.com/apache/trafficcontrol/lib/go-tc" + +const API_v4_CAPABILITIES = "/api/1.4/capabilities" + +// CreateCapability creates the passed capability. +func (to *Session) CreateCapability(c tc.Capability) (tc.Alerts, ReqInf, error) { + var remoteAddr net.Addr + reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} + + reqBody, err := json.Marshal(c) + if err != nil { + return tc.Alerts{}, reqInf, err + } + + resp, remoteAddr, err := to.request(http.MethodPost, API_v4_CAPABILITIES, reqBody) + if err != nil { + return tc.Alerts{}, reqInf, err + } + defer resp.Body.Close() + reqInf.RemoteAddr = remoteAddr + + var alerts tc.Alerts + err = json.NewDecoder(resp.Body).Decode(&alerts) + return alerts, reqInf, err +} + +// ReplaceCapabilityByName replaces the capability named 'name' with the one passed as 'c'. +func (to *Session) ReplaceCapabilityByName(name string, c tc.Capability) (tc.Alerts, ReqInf, error) { + var remoteAddr net.Addr + reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} + + reqBody, err := json.Marshal(c) + if err != nil { + return tc.Alerts{}, reqInf, err + } + + endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, name) + + var resp *http.Response + resp, reqInf.RemoteAddr, err = to.request(http.MethodPut, endpoint, reqBody) + if err != nil { + return tc.Alerts{}, reqInf, err + } + defer resp.Body.Close() + + var alerts tc.Alerts + err = json.NewDecoder(resp.Body).Decode(&alerts) + return alerts, reqInf, err +} + +// GetCapabilities retrieves all capabilities. +func (to *Session) GetCapabilities() ([]tc.Capability, ReqInf, error) { + resp, remoteAddr, err := to.request(http.MethodGet, API_v4_CAPABILITIES, nil) + reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} + if err != nil { + return nil, reqInf, err + } + defer resp.Body.Close() + + var data tc.CapabilitiesResponse + err = json.NewDecoder(resp.Body).Decode(&data) + return data.Response, reqInf, err +} + +// GetCapability retrieves only the capability named 'c' +func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { + endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, c) + resp, remoteAddr, err := to.request(http.MethodGet, endpoint, nil) + reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} + if err != nil { + return tc.Capability{}, reqInf, err + } + defer resp.Body.Close() + + var data tc.CapabilitiesResponse + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return tc.Capability{}, reqInf, err + } else if data.Response == nil || len(data.Response) < 1 { + return tc.Capability{}, reqInf, fmt.Errorf("Invalid response - no capability returned!") + } + + return data.Response[0], reqInf, nil +} + +// DeleteCapability deletes the capability named 'c'. +func (to *Session) DeleteCapability(c string) (alerts tc.Alerts, reqInf ReqInf, err error) { + reqInf.CacheHitStatus = CacheHitStatusMiss + endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, c) + + var resp *http.Response + resp, reqInf.RemoteAddr, err = to.request(http.MethodDelete, endpoint, nil) + if err != nil { + return + } + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(&alerts) + return +} From 7da227f2d1ff9070d93cdbcb0d5c3bf5898adb2d Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 13 Aug 2019 13:06:14 -0600 Subject: [PATCH 05/28] Added API/client tests --- traffic_ops/testing/api/v1/tc-fixtures.json | 10 ++ .../testing/api/v1/traffic_control_test.go | 1 + traffic_ops/testing/api/v1/withobjs_test.go | 2 + .../testing/api/v14/capabilities_test.go | 159 ++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 traffic_ops/testing/api/v14/capabilities_test.go diff --git a/traffic_ops/testing/api/v1/tc-fixtures.json b/traffic_ops/testing/api/v1/tc-fixtures.json index ff8f06081f..be9210d854 100644 --- a/traffic_ops/testing/api/v1/tc-fixtures.json +++ b/traffic_ops/testing/api/v1/tc-fixtures.json @@ -2616,5 +2616,15 @@ "statValue": 1000, "summaryTime": "2019-01-01T00:00:00-06:00" } + ], + "capabilities": [ + { + "name": "test", + "description": "quest" + }, + { + "name": "foo", + "description": "bar" + } ] } diff --git a/traffic_ops/testing/api/v1/traffic_control_test.go b/traffic_ops/testing/api/v1/traffic_control_test.go index 4a419891ec..bc4f3b1f50 100644 --- a/traffic_ops/testing/api/v1/traffic_control_test.go +++ b/traffic_ops/testing/api/v1/traffic_control_test.go @@ -25,6 +25,7 @@ type TrafficControl struct { CDNs []tc.CDN `json:"cdns"` CacheGroups []tc.CacheGroupNullable `json:"cachegroups"` CacheGroupParameterRequests []tc.CacheGroupParameterRequest `json:"cachegroupParameters"` + Capabilities []tc.Capability `json:"capability"` Coordinates []tc.Coordinate `json:"coordinates"` DeliveryServiceRequests []tc.DeliveryServiceRequest `json:"deliveryServiceRequests"` DeliveryServiceRequestComments []tc.DeliveryServiceRequestComment `json:"deliveryServiceRequestComments"` diff --git a/traffic_ops/testing/api/v1/withobjs_test.go b/traffic_ops/testing/api/v1/withobjs_test.go index be722e1045..496c6fd5c2 100644 --- a/traffic_ops/testing/api/v1/withobjs_test.go +++ b/traffic_ops/testing/api/v1/withobjs_test.go @@ -39,6 +39,7 @@ const ( CacheGroups TCObj = iota CacheGroupsDeliveryServices CacheGroupParameters + Capabilities CDNs CDNFederations Coordinates @@ -79,6 +80,7 @@ var withFuncs = map[TCObj]TCObjFuncs{ CacheGroups: {CreateTestCacheGroups, DeleteTestCacheGroups}, CacheGroupsDeliveryServices: {CreateTestCachegroupsDeliveryServices, DeleteTestCachegroupsDeliveryServices}, CacheGroupParameters: {CreateTestCacheGroupParameters, DeleteTestCacheGroupParameters}, + Capabilities: {CreateTestCapabilities, DeleteTestCapabilities}, CDNs: {CreateTestCDNs, DeleteTestCDNs}, CDNFederations: {CreateTestCDNFederations, DeleteTestCDNFederations}, Coordinates: {CreateTestCoordinates, DeleteTestCoordinates}, diff --git a/traffic_ops/testing/api/v14/capabilities_test.go b/traffic_ops/testing/api/v14/capabilities_test.go new file mode 100644 index 0000000000..24f2afdf04 --- /dev/null +++ b/traffic_ops/testing/api/v14/capabilities_test.go @@ -0,0 +1,159 @@ +package v14 + +/* + * 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" + +import "github.com/apache/trafficcontrol/lib/go-log" +import "github.com/apache/trafficcontrol/lib/go-tc" + +// These capabilities are defined during the setup process in todb.go. +// ANY TIME THOSE ARE CHANGED THIS MUST BE UPDATED. +var staticCapabilities = []tc.Capability { + tc.Capability{ + Name: "all-read", + Description: "Full read access", + }, + tc.Capability { + Name: "all-write", + Description: "Full write access", + }, + tc.Capability { + Name: "cdn-read", + Description: "View CDN configuration", + }, +} + +func TestCapabilities(t *testing.T) { + WithObjs(t, []TCObj{Capabilities}, func() { + ReplaceTestCapability(t) + GetTestCapabilities(t) + }) +} + +func CreateTestCapabilities(t *testing.T) { + for _,c := range testData.Capabilities { + resp, _, err := TOSession.CreateCapability(c) + log.Debugln("Response: ", c.Name, " ", resp) + if err != nil { + t.Errorf("could not create capability: %v", err) + } + } +} + +func GetTestCapabilities(t *testing.T) { + testDataLen := len(testData.Capabilities) + len(staticCapabilities) + capMap := make(map[string]string, testDataLen) + + for _,c := range testData.Capabilities { + capMap[c.Name] = c.Description + cap, _, err := TOSession.GetCapability(c.Name) + if err != nil { + t.Errorf("could not get capability '%s': %v", c.Name, err) + continue + } + + if cap.Name != c.Name { + t.Errorf("requested capacity '%s' but got a capacity with the name '%s'", c.Name, cap.Name) + } + if cap.Description != c.Description { + t.Errorf("capacity '%s' has the wrong description, want '%s' but got '%s'", c.Name, c.Description, cap.Description) + } + } + + // Hopefully this won't need to be done for much longer + for _,c := range staticCapabilities { + capMap[c.Name] = c.Description + } + + + caps, _, err := TOSession.GetCapabilities() + if err != nil { + t.Fatalf("could not get all capabilities: %v", err) + } + log.Debugln("capacities:", caps) + if len(caps) != testDataLen { + t.Errorf("response returned different number of capabilities than those that exist; got %d, want %d", len(caps), testDataLen) + return // we can't FATAL because this testing.T object is shared + } + + for _,c := range caps { + if desc, ok := capMap[c.Name]; !ok { + t.Errorf("capability '%s' found in response, but not in test data!", c.Name) + } else { + if desc != c.Description { + t.Errorf("capability '%s' has description '%s' in response, but had '%s' in the test data", c.Name, c.Description, desc) + } + delete(capMap, c.Name) + } + } + + for c,_ := range capMap { + t.Errorf("Capability '%s' existed in the test data but didn't appear in the response!", c) + } +} + +func ReplaceTestCapability(t *testing.T) { + if len(testData.Capabilities) < 1 { + t.Skip("There aren't any test capabilities, skipping (this should probably be rectified!)") + } + + c := testData.Capabilities[0] + c.Name += "TEST" + c.Description += "REPLACE TEST" + + alerts, _, err := TOSession.ReplaceCapabilityByName(testData.Capabilities[0].Name, c) + log.Debugln("alerts:", alerts) + if err != nil { + t.Errorf("Failed to replace capability %s: %v", testData.Capabilities[0].Name, err) + return // we can't FATAL because this testing.T object is shared + } + + // The old one shouldn't exist anymore + resp, _, err := TOSession.GetCapability(testData.Capabilities[0].Name) + if err == nil { + t.Errorf("Expected an error, but got response: %v", resp) + } + + // ... but the new one should + resp, _, err = TOSession.GetCapability(c.Name) + if err != nil { + t.Errorf("Expected to get new capability, error: %v", err) + } + + // Now we need to replace it again, so the DELETE can catch it + alerts, _, err = TOSession.ReplaceCapabilityByName(c.Name, testData.Capabilities[0]) + log.Debugln("alerts:", alerts) + if err != nil { + t.Errorf("Failed to re-replace capability: %s", err) + } + + log.Debugln("response:", resp) +} + +func DeleteTestCapabilities(t *testing.T) { + for _,c := range testData.Capabilities { + resp, _, err := TOSession.DeleteCapability(c.Name) + log.Debugln("Response: ", c.Name, " ", resp) + if err != nil { + t.Errorf("could not delete capability: %v", err) + } + } +} From e0e9e9ab1e4871feb42fdcc0ad7a64bcb880b419 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 14 Aug 2019 09:47:41 -0600 Subject: [PATCH 06/28] Added Python client support --- .../clients/python/trafficops/tosession.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py index 00590ac11a..12cc2cc313 100644 --- a/traffic_control/clients/python/trafficops/tosession.py +++ b/traffic_control/clients/python/trafficops/tosession.py @@ -540,6 +540,55 @@ def get_cache_stats(self, query_params=None): :raises: Union[LoginError, OperationError] """ + # + # Capabilities + # + @api_request(u'get', u'capabilities', (u'1.1', u'1.2', u'1.3', u'1.4')) + def get_capabilities(self, query_params=None): + """ + Retrieves capabilities + :ref:`to-api-capabilities` + :param query_params: See API page for more information on accepted parameters + :type query_params: Dict[str, Any] + :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] + :raises: Union[LoginError, OperationError] + """ + + @api_request(u'post', u'capabilities', (u'1.1', u'1.2', u'1.3', u'1.4')) + def create_capability(self, data=None): + """ + Creates a capability + :ref:`to-api-capabilities` + :param data: See API page for more information on accepted request body data + :type data: Any + :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] + :raises: Union[LoginError, OperationError] + """ + + @api_request(u'put', u'capabilities', ( u'1.4',)) + def update_capability(self, query_params=None, data=None): + """ + Replaces a capability + :ref:`to-api-capabilities` + :param query_params: See API page for more information on accepted parameters + :type query_params: Dict[str, Any] + :param data: See API page for more information on accepted request body data + :type data: Any + :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] + :raises: Union[LoginError, OperationError] + """ + + @api_request(u'delete', u'capabilities', (u'1.4',)) + def delete_capability(self, query_params=None): + """ + Deletes a capability + :ref:`to-api-capabilities` + :param query_params: See API page for more information on accepted parameters + :type query_params: Dict[str, Any] + :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] + :raises: Union[LoginError, OperationError] + """ + # # CDN # From 6939eb09243f57264247452dbd9ff142c647b9c4 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Thu, 15 Aug 2019 11:33:05 -0600 Subject: [PATCH 07/28] Updated docs --- docs/source/api/capabilities.rst | 197 ++++++++++++++++++++++---- docs/source/api/capabilities_name.rst | 3 + 2 files changed, 170 insertions(+), 30 deletions(-) diff --git a/docs/source/api/capabilities.rst b/docs/source/api/capabilities.rst index 2f61110e27..2189bcb866 100644 --- a/docs/source/api/capabilities.rst +++ b/docs/source/api/capabilities.rst @@ -29,41 +29,51 @@ Get all capabilities. Request Structure ----------------- -No available parameters +.. table:: Request Query Parameters + + +------+----------+-----------------------------------------------+ + | Name | Required | Description | + +======+==========+===============================================+ + | name | no | Return only the capability that has this name | + +------+----------+-----------------------------------------------+ + +.. code-block:: http + :caption: Request Example + + GET /api/1.4/capabilities?name=test HTTP/1.1 + Host: trafficops.infra.ciab.test + User-Agent: curl/7.47.0 + Accept: */* + Cookie: mojolicious=... Response Structure ------------------ :name: Name of the capability -:description: Describes the APIs covered by the capability. -:lastUpdated: Date and time of the last update made to this capability, in ISO format +:description: Describes the permissions covered by the capability. +:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format .. code-block:: http :caption: Response Example HTTP/1.1 200 OK Access-Control-Allow-Credentials: true - Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept + Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE Access-Control-Allow-Origin: * - Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/json - Date: Wed, 14 Nov 2018 20:26:19 GMT - Server: Mojolicious (Perl) - Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly Vary: Accept-Encoding - Whole-Content-Sha512: zmjsQO3Y4r1/xCFOHB+E+8+bbgDyVcvoR0d4gKqqsWTFaUnxp2flIzuFqWjXf+wb4Bbd1e2Ojse4nQKnyIFKGw== Transfer-Encoding: chunked + Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly + Whole-Content-Sha512: c18+GtX2ZI8PoCSwuAzBhl+6w3vDpKQTa/cDJC0WHxdpguOL378KBxGWW5PCSyZfJUb7wPyOL5qKMn6NNTufhg== + X-Server-Name: traffic_ops_golang/ + Date: Thu, 15 Aug 2019 17:20:20 GMT + Content-Length: 161 { "response": [ { - "name": "cdn-read", - "description": "View CDN configuration", - "lastUpdated": "2017-04-02 08:22:43" - }, - { - "name": "cdn-write", - "description": "Create, edit or delete CDN configuration", - "lastUpdated": "2017-04-02 08:22:43" + "description": "This is only a test. If this were a real capability, it might do something", + "lastUpdated": "2019-08-15 17:18:03+00", + "name": "test" } ]} @@ -89,7 +99,7 @@ Request Structure User-Agent: curl/7.47.0 Accept: */* Cookie: mojolicious=... - Content-Length: 109 + Content-Length: 108 Content-Type: application/json { @@ -99,7 +109,8 @@ Request Structure Response Structure ------------------ -:description: Describes the APIs covered by the capability. +:description: Describes the permissions covered by the capability. +:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format :name: Name of the capability .. code-block:: http @@ -107,26 +118,152 @@ Response Structure HTTP/1.1 200 OK Access-Control-Allow-Credentials: true - Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept + Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE Access-Control-Allow-Origin: * - Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/json - Date: Wed, 14 Nov 2018 20:33:00 GMT - Server: Mojolicious (Perl) Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly - Vary: Accept-Encoding - Whole-Content-Sha512: HhhQzw3JBLv90lOeeSGj75uknADanz3fUnQt1E266HAKPTFuTjuIJpf8ni9fb9Chv9LN7mt16utcHMbP8MBHZw== - Content-Length: 183 + Set-Cookie: mojolicious=...; Path=/; HttpOnly + Whole-Content-Sha512: A1rjpDy+O+oooYeer2j09pCEDpPEFk/nt8/AaJye2sLkfy93MtquCsB/Rlgz7sCYputd/EPOPDyi2WkN8UB1Rw== + X-Server-Name: traffic_ops_golang/ + Date: Thu, 15 Aug 2019 17:18:03 GMT + Content-Length: 219 { "alerts": [ { - "level": "success", - "text": "Capability was created." + "text": "Capability created.", + "level": "success" } ], "response": { - "name": "test", - "description": "This is only a test. If this were a real capability, it might do something" + "description": "This is only a test. If this were a real capability, it might do something", + "lastUpdated": "2019-08-15 17:18:03+00", + "name": "test" }} + +``PUT`` +======= +.. versionadded:: 1.4 + +Replace a capability with the one provided. + +:Auth. Required: Yes +:Roles Required: "operations" or "admin" +:Response Type: Array + +Request Structure +----------------- +.. table:: Request Query Parameters + + +------+----------+---------------------------------------------------+ + | Name | Required | Description | + +======+==========+===================================================+ + | name | yes | The (current) name of the capability to be edited | + +------+----------+---------------------------------------------------+ + +.. code-block:: http + :caption: Request Example + + PUT /api/1.4/capabilities?name=test HTTP/1.1 + Host: trafficops.infra.ciab.test + User-Agent: curl/7.47.0 + Accept: */* + Cookie: mojolicious=... + Content-Length: 109 + Content-Type: application/json + +Response Structure +------------------ +:description: Describes the permissions covered by the capability. +:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format +:name: Name of the capability + +.. code-block:: http + :caption: Response Example + + HTTP/1.1 200 OK + Access-Control-Allow-Credentials: true + Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie + Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE + Access-Control-Allow-Origin: * + Content-Type: application/json + Set-Cookie: mojolicious=...; Path=/; HttpOnly + Whole-Content-Sha512: eciuE8oKQqBOtMThcQvSrPEIuJ9gUeutB00eW7g4KSscwO/vzplyOg8i/EVgfR9NFhK9VSVvdrKvxHC7HsG2fg== + X-Server-Name: traffic_ops_golang/ + Date: Thu, 15 Aug 2019 17:21:50 GMT + Content-Length: 224 + + { "alerts": [ + { + "text": "Capability was updated.", + "level": "success" + } + ], + "response": { + "description": "This is only a test. If this were a real capability, it might do something", + "lastUpdated": "2019-08-15 17:21:50+00", + "name": "quest" + }} + +``DELETE`` +========== +.. versionadded:: 1.4 + +Delete a capability. + +:Auth. Required: Yes +:Roles Required: "operations" or "admin" +:Response Type: Array + +Request Structure +----------------- +.. table:: Request Query Parameters + + +------+----------+------------------------------------------+ + | Name | Required | Description | + +======+==========+==========================================+ + | name | yes | The name of the capability to be deleted | + +------+----------+------------------------------------------+ + +.. code-block:: http + :caption: Request Example + + DELETE /api/1.4/capabilities?name=quest HTTP/1.1 + Host: trafficops.infra.ciab.test + User-Agent: curl/7.47.0 + Accept: */* + Cookie: mojolicious=... + +Response Structure +------------------ +:description: Describes the permissions that were covered by the capability. +:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format +:name: Name of the capability + +.. code-block:: http + :caption: Response Example + + HTTP/1.1 200 OK + Access-Control-Allow-Credentials: true + Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie + Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE + Access-Control-Allow-Origin: * + Content-Type: application/json + Set-Cookie: mojolicious=...; Path=/; HttpOnly + Whole-Content-Sha512: 7lWTuaI1BUeXrnTG1fbFeKuvVuqojZJjSQV5MOtT0a++VV1PUAXYSIwe2vUOpoM4uwCKpeAc86J75OJGLgLHdg== + X-Server-Name: traffic_ops_golang/ + Date: Thu, 15 Aug 2019 17:26:00 GMT + Content-Length: 220 + + { "alerts": [ + { + "text": "Capability deleted.", + "level": "success" + } + ], + "response": { + "description": "This is only a test. If this were a real capability, it might do something", + "lastUpdated": "2019-08-15 17:21:50+00", + "name": "quest" + }} diff --git a/docs/source/api/capabilities_name.rst b/docs/source/api/capabilities_name.rst index f82c186810..6afa1a3e92 100644 --- a/docs/source/api/capabilities_name.rst +++ b/docs/source/api/capabilities_name.rst @@ -19,6 +19,9 @@ ``capabilities/{{name}}`` ************************* +.. deprecated:: 1.4 + All of the functionality provided by this endpoint can be accomplished using :ref:`to-api-capabilities` instead. This endpoint will be removed in a future version of :abbr:`ATC (Apache Traffic Control)`. + ``GET`` ======= Get a capability by name. From 42ae23e3498d4f2db7ba94da1157563ee96e3748 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Thu, 15 Aug 2019 11:57:25 -0600 Subject: [PATCH 08/28] Added changelog updates --- .../capabilities/capabilities.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index fb33e7ffa4..9d51a58849 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -27,7 +27,6 @@ import "net/http" import "github.com/apache/trafficcontrol/lib/go-tc" import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" -// import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" const readQuery = ` SELECT description, @@ -102,8 +101,7 @@ func Read(w http.ResponseWriter, r *http.Request) { } w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(respBts) - w.Write([]byte{'\n'}) + w.Write(append(respBts, '\n')) } func Create(w http.ResponseWriter, r *http.Request) { @@ -168,9 +166,10 @@ func Create(w http.ResponseWriter, r *http.Request) { return } + api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Created", cap.Name), inf.User, tx) + w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(respBts) - w.Write([]byte{'\n'}) + w.Write(append(respBts, '\n')) } func Replace(w http.ResponseWriter, r *http.Request) { @@ -228,9 +227,12 @@ func Replace(w http.ResponseWriter, r *http.Request) { return } + msg := "CAPABILITY: %s, ACTION: Replaced with capability '%s' (%s)'" + msg = fmt.Sprintf(msg, inf.Params["name"], cap.Name, cap.Description) + api.CreateChangeLogRawTx(api.ApiChange, msg, inf.User, tx) + w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(respBts) - w.Write([]byte{'\n'}) + w.Write(append(respBts, '\n')) } func Delete(w http.ResponseWriter, r *http.Request) { @@ -284,6 +286,8 @@ func Delete(w http.ResponseWriter, r *http.Request) { return } + api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Deleted", capName), inf.User, tx) + w.Header().Set(tc.ContentType, tc.ApplicationJson) w.Write(append(respBts, '\n')) } From f47dea24e0bd71246e6e2c702019cd0030b5acad Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Thu, 15 Aug 2019 12:04:15 -0600 Subject: [PATCH 09/28] Traffic Portal changes to reflect deprecation --- .../app/src/common/api/CapabilityService.js | 16 ++-------------- .../modules/private/capabilities/edit/index.js | 2 +- .../private/capabilities/endpoints/index.js | 2 +- .../modules/private/capabilities/users/index.js | 2 +- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/traffic_portal/app/src/common/api/CapabilityService.js b/traffic_portal/app/src/common/api/CapabilityService.js index a2440ff5d4..16e34ec66b 100644 --- a/traffic_portal/app/src/common/api/CapabilityService.js +++ b/traffic_portal/app/src/common/api/CapabilityService.js @@ -30,18 +30,6 @@ var CapabilityService = function($http, messageModel, ENV) { ); }; - // todo: change to use query param when it is supported - this.getCapability = function(name) { - return $http.get(ENV.api['root'] + 'capabilities/' + name).then( - function(result) { - return result.data.response[0]; - }, - function(err) { - throw err; - } - ); - }; - this.createCapability = function(cap) { return $http.post(ENV.api['root'] + "capabilities", cap).then( function(result) { @@ -56,7 +44,7 @@ var CapabilityService = function($http, messageModel, ENV) { // todo: change to use query param when it is supported this.updateCapability = function(cap) { - return $http.put(ENV.api['root'] + "capabilities/" + cap.name, cap).then( + return $http.put(ENV.api['root'] + "capabilities", {params: {"name": cap.name}, data: cap}).then( function(result) { return result.data; }, @@ -69,7 +57,7 @@ var CapabilityService = function($http, messageModel, ENV) { // todo: change to use query param when it is supported this.deleteCapability = function(cap) { - return $http.delete(ENV.api['root'] + "capabilities/" + cap.name).then( + return $http.delete(ENV.api['root'] + "capabilities", {params: {"name": cap.name}}).then( function(result) { return result.data; }, diff --git a/traffic_portal/app/src/modules/private/capabilities/edit/index.js b/traffic_portal/app/src/modules/private/capabilities/edit/index.js index b51cd6627c..5f64793599 100644 --- a/traffic_portal/app/src/modules/private/capabilities/edit/index.js +++ b/traffic_portal/app/src/modules/private/capabilities/edit/index.js @@ -28,7 +28,7 @@ module.exports = angular.module('trafficPortal.private.capabilities.edit', []) controller: 'FormEditCapabilityController', resolve: { capability: function($stateParams, capabilityService) { - return capabilityService.getCapability($stateParams.capName); + return capabilityService.getCapabilities({"name": $stateParams.capName}); } } } diff --git a/traffic_portal/app/src/modules/private/capabilities/endpoints/index.js b/traffic_portal/app/src/modules/private/capabilities/endpoints/index.js index 323a9fb110..48758166b3 100644 --- a/traffic_portal/app/src/modules/private/capabilities/endpoints/index.js +++ b/traffic_portal/app/src/modules/private/capabilities/endpoints/index.js @@ -28,7 +28,7 @@ module.exports = angular.module('trafficPortal.private.capabilities.endpoints', controller: 'TableCapabilityEndpointsController', resolve: { capability: function($stateParams, capabilityService) { - return capabilityService.getCapability($stateParams.capName); + return capabilityService.getCapabilities({"name": $stateParams.capName}); }, capEndpoints: function($stateParams, endpointService) { return endpointService.getEndpoints({ capability: $stateParams.capName }); diff --git a/traffic_portal/app/src/modules/private/capabilities/users/index.js b/traffic_portal/app/src/modules/private/capabilities/users/index.js index 42f4d554da..b1aa6ccda4 100644 --- a/traffic_portal/app/src/modules/private/capabilities/users/index.js +++ b/traffic_portal/app/src/modules/private/capabilities/users/index.js @@ -28,7 +28,7 @@ module.exports = angular.module('trafficPortal.private.capabilities.users', []) controller: 'TableCapabilityUsersController', resolve: { capability: function($stateParams, capabilityService) { - return capabilityService.getCapability($stateParams.capName); + return capabilityService.getCapabilities({"name": $stateParams.capName}); }, capUsers: function($stateParams, userService) { return userService.getUsers({ capability: $stateParams.capName }); From 0786c0d9bf17028ebafcdffd674e244ec5dfaabf Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 11:09:28 -0600 Subject: [PATCH 10/28] Fix API route definition for consistency --- traffic_ops/client/capability.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/traffic_ops/client/capability.go b/traffic_ops/client/capability.go index 71c5c53e7f..c62e07ad65 100644 --- a/traffic_ops/client/capability.go +++ b/traffic_ops/client/capability.go @@ -21,7 +21,7 @@ import "net/http" import "github.com/apache/trafficcontrol/lib/go-tc" -const API_v4_CAPABILITIES = "/api/1.4/capabilities" +const API_v14_CAPABILITIES = "/api/1.4/capabilities" // CreateCapability creates the passed capability. func (to *Session) CreateCapability(c tc.Capability) (tc.Alerts, ReqInf, error) { @@ -33,7 +33,7 @@ func (to *Session) CreateCapability(c tc.Capability) (tc.Alerts, ReqInf, error) return tc.Alerts{}, reqInf, err } - resp, remoteAddr, err := to.request(http.MethodPost, API_v4_CAPABILITIES, reqBody) + resp, remoteAddr, err := to.request(http.MethodPost, API_v14_CAPABILITIES, reqBody) if err != nil { return tc.Alerts{}, reqInf, err } @@ -55,7 +55,7 @@ func (to *Session) ReplaceCapabilityByName(name string, c tc.Capability) (tc.Ale return tc.Alerts{}, reqInf, err } - endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, name) + endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, name) var resp *http.Response resp, reqInf.RemoteAddr, err = to.request(http.MethodPut, endpoint, reqBody) @@ -71,7 +71,7 @@ func (to *Session) ReplaceCapabilityByName(name string, c tc.Capability) (tc.Ale // GetCapabilities retrieves all capabilities. func (to *Session) GetCapabilities() ([]tc.Capability, ReqInf, error) { - resp, remoteAddr, err := to.request(http.MethodGet, API_v4_CAPABILITIES, nil) + resp, remoteAddr, err := to.request(http.MethodGet, API_v14_CAPABILITIES, nil) reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} if err != nil { return nil, reqInf, err @@ -85,7 +85,7 @@ func (to *Session) GetCapabilities() ([]tc.Capability, ReqInf, error) { // GetCapability retrieves only the capability named 'c' func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { - endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, c) + endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, c) resp, remoteAddr, err := to.request(http.MethodGet, endpoint, nil) reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} if err != nil { @@ -107,7 +107,7 @@ func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { // DeleteCapability deletes the capability named 'c'. func (to *Session) DeleteCapability(c string) (alerts tc.Alerts, reqInf ReqInf, err error) { reqInf.CacheHitStatus = CacheHitStatusMiss - endpoint := fmt.Sprintf("%s?name=%s", API_v4_CAPABILITIES, c) + endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, c) var resp *http.Response resp, reqInf.RemoteAddr, err = to.request(http.MethodDelete, endpoint, nil) From aef5700a890bd0bfbbebbcab2e732fbb704e2352 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 11:12:27 -0600 Subject: [PATCH 11/28] Remove unnecessary debug line --- traffic_ops/testing/api/v14/capabilities_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/traffic_ops/testing/api/v14/capabilities_test.go b/traffic_ops/testing/api/v14/capabilities_test.go index 24f2afdf04..96fbbad2f6 100644 --- a/traffic_ops/testing/api/v14/capabilities_test.go +++ b/traffic_ops/testing/api/v14/capabilities_test.go @@ -88,7 +88,6 @@ func GetTestCapabilities(t *testing.T) { if err != nil { t.Fatalf("could not get all capabilities: %v", err) } - log.Debugln("capacities:", caps) if len(caps) != testDataLen { t.Errorf("response returned different number of capabilities than those that exist; got %d, want %d", len(caps), testDataLen) return // we can't FATAL because this testing.T object is shared From 9182cdaa586f537eca7e5ddbc8ca686cfaec0e7b Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 11:14:02 -0600 Subject: [PATCH 12/28] Use Fatalf when warranted --- traffic_ops/testing/api/v14/capabilities_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/traffic_ops/testing/api/v14/capabilities_test.go b/traffic_ops/testing/api/v14/capabilities_test.go index 96fbbad2f6..94cb7ee898 100644 --- a/traffic_ops/testing/api/v14/capabilities_test.go +++ b/traffic_ops/testing/api/v14/capabilities_test.go @@ -89,8 +89,7 @@ func GetTestCapabilities(t *testing.T) { t.Fatalf("could not get all capabilities: %v", err) } if len(caps) != testDataLen { - t.Errorf("response returned different number of capabilities than those that exist; got %d, want %d", len(caps), testDataLen) - return // we can't FATAL because this testing.T object is shared + t.Fatalf("response returned different number of capabilities than those that exist; got %d, want %d", len(caps), testDataLen) } for _,c := range caps { @@ -111,7 +110,7 @@ func GetTestCapabilities(t *testing.T) { func ReplaceTestCapability(t *testing.T) { if len(testData.Capabilities) < 1 { - t.Skip("There aren't any test capabilities, skipping (this should probably be rectified!)") + t.Fatalf("No test capabilities!") } c := testData.Capabilities[0] @@ -121,8 +120,7 @@ func ReplaceTestCapability(t *testing.T) { alerts, _, err := TOSession.ReplaceCapabilityByName(testData.Capabilities[0].Name, c) log.Debugln("alerts:", alerts) if err != nil { - t.Errorf("Failed to replace capability %s: %v", testData.Capabilities[0].Name, err) - return // we can't FATAL because this testing.T object is shared + t.Fatalf("Failed to replace capability %s: %v", testData.Capabilities[0].Name, err) } // The old one shouldn't exist anymore From 91e1d6abdc233ce86ee2b026515ed892c03d346a Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 11:36:19 -0600 Subject: [PATCH 13/28] Use API functions to write responses --- .../capabilities/capabilities.go | 55 ++----------------- 1 file changed, 4 insertions(+), 51 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 9d51a58849..4a80de8669 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -92,16 +92,7 @@ func Read(w http.ResponseWriter, r *http.Request) { caps = append(caps, cap) } - respBts, err := json.Marshal(struct{R []tc.Capability `json:"response"`}{caps}) - if err != nil { - errCode = http.StatusInternalServerError - sysErr = fmt.Errorf("Marshaling response: %v", err) - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } - - w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(append(respBts, '\n')) + api.WriteResp(w, r, caps) } func Create(w http.ResponseWriter, r *http.Request) { @@ -156,20 +147,8 @@ func Create(w http.ResponseWriter, r *http.Request) { return } - alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability created."}} - responseObj := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} - respBts, err := json.Marshal(responseObj) - if err != nil { - errCode = http.StatusInternalServerError - sysErr = fmt.Errorf("Marshaling response: %v", err) - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } - + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability created.", cap) api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Created", cap.Name), inf.User, tx) - - w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(append(respBts, '\n')) } func Replace(w http.ResponseWriter, r *http.Request) { @@ -217,22 +196,8 @@ func Replace(w http.ResponseWriter, r *http.Request) { return } - alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability was updated."}} - resp := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} - respBts, err := json.Marshal(resp) - if err != nil { - sysErr = fmt.Errorf("Marshaling response: %v", err) - errCode = http.StatusInternalServerError - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } - - msg := "CAPABILITY: %s, ACTION: Replaced with capability '%s' (%s)'" - msg = fmt.Sprintf(msg, inf.Params["name"], cap.Name, cap.Description) api.CreateChangeLogRawTx(api.ApiChange, msg, inf.User, tx) - - w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(append(respBts, '\n')) + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability was updated.", cap) } func Delete(w http.ResponseWriter, r *http.Request) { @@ -276,20 +241,8 @@ func Delete(w http.ResponseWriter, r *http.Request) { return } - alert := []tc.Alert{tc.Alert{Level: tc.SuccessLevel.String(), Text: "Capability deleted."}} - resp := struct{A []tc.Alert `json:"alerts"`; R tc.Capability `json:"response"`}{alert, cap} - respBts, err := json.Marshal(resp) - if err != nil { - sysErr = fmt.Errorf("Marshaling response: %v", err) - errCode = http.StatusInternalServerError - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } - + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability deleted.", cap) api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Deleted", capName), inf.User, tx) - - w.Header().Set(tc.ContentType, tc.ApplicationJson) - w.Write(append(respBts, '\n')) } func capabilityNameExists(c string, tx *sql.Tx) (bool, error) { From 3c1becf618fa68fe6e0fc5ca61d4823a87efa751 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 12:21:50 -0600 Subject: [PATCH 14/28] fix compilation error --- traffic_ops/traffic_ops_golang/capabilities/capabilities.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 4a80de8669..85daf34768 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -196,6 +196,8 @@ func Replace(w http.ResponseWriter, r *http.Request) { return } + msg := "CAPABILITY: %s, ACTION: Replaced with capability '%s' (%s)'" + msg = fmt.Sprintf(msg, inf.Params["name"], cap.Name, cap.Description) api.CreateChangeLogRawTx(api.ApiChange, msg, inf.User, tx) api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability was updated.", cap) } From d61f28dccba25da1b21aa83b68bec4a64cab4c3c Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 12:22:30 -0600 Subject: [PATCH 15/28] Switch URL building to use proper net/url encoding --- traffic_ops/client/capability.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/traffic_ops/client/capability.go b/traffic_ops/client/capability.go index c62e07ad65..9275df5014 100644 --- a/traffic_ops/client/capability.go +++ b/traffic_ops/client/capability.go @@ -15,9 +15,10 @@ package client */ import "encoding/json" -import "fmt" +import "errors" import "net" import "net/http" +import "net/url" import "github.com/apache/trafficcontrol/lib/go-tc" @@ -55,7 +56,9 @@ func (to *Session) ReplaceCapabilityByName(name string, c tc.Capability) (tc.Ale return tc.Alerts{}, reqInf, err } - endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, name) + var v url.Values + v.Add("name", name) + endpoint := API_v14_CAPABILITIES + "?" + v.Encode() var resp *http.Response resp, reqInf.RemoteAddr, err = to.request(http.MethodPut, endpoint, reqBody) @@ -85,7 +88,9 @@ func (to *Session) GetCapabilities() ([]tc.Capability, ReqInf, error) { // GetCapability retrieves only the capability named 'c' func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { - endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, c) + var v url.Values + v.Add("name", c) + endpoint := API_v14_CAPABILITIES + "?" + v.Encode() resp, remoteAddr, err := to.request(http.MethodGet, endpoint, nil) reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} if err != nil { @@ -98,7 +103,7 @@ func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { if err != nil { return tc.Capability{}, reqInf, err } else if data.Response == nil || len(data.Response) < 1 { - return tc.Capability{}, reqInf, fmt.Errorf("Invalid response - no capability returned!") + return tc.Capability{}, reqInf, errors.New("Invalid response - no capability returned!") } return data.Response[0], reqInf, nil @@ -107,7 +112,9 @@ func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { // DeleteCapability deletes the capability named 'c'. func (to *Session) DeleteCapability(c string) (alerts tc.Alerts, reqInf ReqInf, err error) { reqInf.CacheHitStatus = CacheHitStatusMiss - endpoint := fmt.Sprintf("%s?name=%s", API_v14_CAPABILITIES, c) + var v url.Values + v.Add("name", c) + endpoint := API_v14_CAPABILITIES + "?" + v.Encode() var resp *http.Response resp, reqInf.RemoteAddr, err = to.request(http.MethodDelete, endpoint, nil) From c85c365ab9e12a2c295a4e322369896688bcc2b3 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 12:26:03 -0600 Subject: [PATCH 16/28] add db struct tags --- lib/go-tc/capabilities.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/go-tc/capabilities.go b/lib/go-tc/capabilities.go index d5d0d68365..c520e503ff 100644 --- a/lib/go-tc/capabilities.go +++ b/lib/go-tc/capabilities.go @@ -26,9 +26,9 @@ package tc // afforded by said Role. Note that enforcement of Capability-based permisions is not currently // implemented. type Capability struct { - Description string `json:"description"` - LastUpdated TimeNoMod `json:"lastUpdated"` - Name string `json:"name"` + Description string `json:"description" db:"description"` + LastUpdated TimeNoMod `json:"lastUpdated" db:"last_updated"` + Name string `json:"name" db:"name"` } // CapabilitiesResponse models the structure of a minimal response from the Capabilities API in From d314d88143c3714a95c9dfc307aceccc70b10591 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 11 Oct 2019 12:52:28 -0600 Subject: [PATCH 17/28] Add page/where/orderby/limit support --- docs/source/api/capabilities.rst | 23 +++++++++++++++---- .../capabilities/capabilities.go | 21 ++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/source/api/capabilities.rst b/docs/source/api/capabilities.rst index 2189bcb866..5a0e6bb829 100644 --- a/docs/source/api/capabilities.rst +++ b/docs/source/api/capabilities.rst @@ -31,11 +31,24 @@ Request Structure ----------------- .. table:: Request Query Parameters - +------+----------+-----------------------------------------------+ - | Name | Required | Description | - +======+==========+===============================================+ - | name | no | Return only the capability that has this name | - +------+----------+-----------------------------------------------+ + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | Name | Required | Description | + +===========+==========+=====================================================================================================================+ + | name | no | Return only the capability that has this name | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | orderby | no | Choose the ordering of the results - must be the name of one of the fields of the objects in the ``response`` array | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | sortOrder | no | Changes the order of sorting. Either ascending (default or "asc") or descending ("desc") | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | limit | no | Choose the maximum number of results to return | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | offset | no | The number of results to skip before beginning to return results. Must use in conjunction with ``limit`` | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + | page | no | Return the n\ :sup:`th` page of results, where "n" is the value of this parameter, pages are ``limit`` long and the | + | | | first page is 1. If ``offset`` was defined, this query parameter has no effect. ``limit`` must be defined to make | + | | | use of ``page``. | + +-----------+----------+---------------------------------------------------------------------------------------------------------------------+ + .. code-block:: http :caption: Request Example diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 85daf34768..3725da1f84 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -26,7 +26,10 @@ import "fmt" import "net/http" import "github.com/apache/trafficcontrol/lib/go-tc" +import "github.com/apache/trafficcontrol/lib/go-util" + import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" +import "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" const readQuery = ` SELECT description, @@ -63,14 +66,20 @@ func Read(w http.ResponseWriter, r *http.Request) { } defer inf.Close() + cols := map[string]dbhelpers.WhereColumnInfo{ + "name": dbhelpers.WhereColumnInfo{"capability.name", nil}, + } - var rows *sql.Rows - var err error - if name, ok := inf.Params["name"]; ok { - rows, err = tx.Query(readQuery + "WHERE name=$1", name) - } else { - rows, err = tx.Query(readQuery) + where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, cols) + if len(errs) > 0 { + errCode = http.StatusBadRequest + userErr = util.JoinErrs(errs) + api.HandleErr(w, r, tx, errCode, userErr, nil) + return } + + query := readQuery + where + orderBy + pagination + rows, err := inf.Tx.NamedQuery(query, queryValues) if err != nil && err != sql.ErrNoRows { errCode = http.StatusInternalServerError sysErr = fmt.Errorf("querying capabilities: %v", err) From 3af65e6186946169ff42106231cdb7c941bd36c2 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 6 Jan 2020 12:26:24 -0700 Subject: [PATCH 18/28] Removed new PUT/DELETE handlers --- docs/source/api/capabilities.rst | 127 ------------------ traffic_ops/client/capability.go | 44 ------ traffic_ops/testing/api/v1/todb_test.go | 1 + .../testing/api/v14/capabilities_test.go | 53 +------- .../capabilities/capabilities.go | 108 --------------- .../traffic_ops_golang/routing/routes.go | 6 +- 6 files changed, 5 insertions(+), 334 deletions(-) diff --git a/docs/source/api/capabilities.rst b/docs/source/api/capabilities.rst index 5a0e6bb829..26eadf0e9a 100644 --- a/docs/source/api/capabilities.rst +++ b/docs/source/api/capabilities.rst @@ -153,130 +153,3 @@ Response Structure "lastUpdated": "2019-08-15 17:18:03+00", "name": "test" }} - - -``PUT`` -======= -.. versionadded:: 1.4 - -Replace a capability with the one provided. - -:Auth. Required: Yes -:Roles Required: "operations" or "admin" -:Response Type: Array - -Request Structure ------------------ -.. table:: Request Query Parameters - - +------+----------+---------------------------------------------------+ - | Name | Required | Description | - +======+==========+===================================================+ - | name | yes | The (current) name of the capability to be edited | - +------+----------+---------------------------------------------------+ - -.. code-block:: http - :caption: Request Example - - PUT /api/1.4/capabilities?name=test HTTP/1.1 - Host: trafficops.infra.ciab.test - User-Agent: curl/7.47.0 - Accept: */* - Cookie: mojolicious=... - Content-Length: 109 - Content-Type: application/json - -Response Structure ------------------- -:description: Describes the permissions covered by the capability. -:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format -:name: Name of the capability - -.. code-block:: http - :caption: Response Example - - HTTP/1.1 200 OK - Access-Control-Allow-Credentials: true - Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie - Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE - Access-Control-Allow-Origin: * - Content-Type: application/json - Set-Cookie: mojolicious=...; Path=/; HttpOnly - Whole-Content-Sha512: eciuE8oKQqBOtMThcQvSrPEIuJ9gUeutB00eW7g4KSscwO/vzplyOg8i/EVgfR9NFhK9VSVvdrKvxHC7HsG2fg== - X-Server-Name: traffic_ops_golang/ - Date: Thu, 15 Aug 2019 17:21:50 GMT - Content-Length: 224 - - { "alerts": [ - { - "text": "Capability was updated.", - "level": "success" - } - ], - "response": { - "description": "This is only a test. If this were a real capability, it might do something", - "lastUpdated": "2019-08-15 17:21:50+00", - "name": "quest" - }} - -``DELETE`` -========== -.. versionadded:: 1.4 - -Delete a capability. - -:Auth. Required: Yes -:Roles Required: "operations" or "admin" -:Response Type: Array - -Request Structure ------------------ -.. table:: Request Query Parameters - - +------+----------+------------------------------------------+ - | Name | Required | Description | - +======+==========+==========================================+ - | name | yes | The name of the capability to be deleted | - +------+----------+------------------------------------------+ - -.. code-block:: http - :caption: Request Example - - DELETE /api/1.4/capabilities?name=quest HTTP/1.1 - Host: trafficops.infra.ciab.test - User-Agent: curl/7.47.0 - Accept: */* - Cookie: mojolicious=... - -Response Structure ------------------- -:description: Describes the permissions that were covered by the capability. -:lastUpdated: Date and time of the last update made to this capability, in an ISO-like format -:name: Name of the capability - -.. code-block:: http - :caption: Response Example - - HTTP/1.1 200 OK - Access-Control-Allow-Credentials: true - Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie - Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE - Access-Control-Allow-Origin: * - Content-Type: application/json - Set-Cookie: mojolicious=...; Path=/; HttpOnly - Whole-Content-Sha512: 7lWTuaI1BUeXrnTG1fbFeKuvVuqojZJjSQV5MOtT0a++VV1PUAXYSIwe2vUOpoM4uwCKpeAc86J75OJGLgLHdg== - X-Server-Name: traffic_ops_golang/ - Date: Thu, 15 Aug 2019 17:26:00 GMT - Content-Length: 220 - - { "alerts": [ - { - "text": "Capability deleted.", - "level": "success" - } - ], - "response": { - "description": "This is only a test. If this were a real capability, it might do something", - "lastUpdated": "2019-08-15 17:21:50+00", - "name": "quest" - }} diff --git a/traffic_ops/client/capability.go b/traffic_ops/client/capability.go index 9275df5014..100c0fab29 100644 --- a/traffic_ops/client/capability.go +++ b/traffic_ops/client/capability.go @@ -46,32 +46,6 @@ func (to *Session) CreateCapability(c tc.Capability) (tc.Alerts, ReqInf, error) return alerts, reqInf, err } -// ReplaceCapabilityByName replaces the capability named 'name' with the one passed as 'c'. -func (to *Session) ReplaceCapabilityByName(name string, c tc.Capability) (tc.Alerts, ReqInf, error) { - var remoteAddr net.Addr - reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr} - - reqBody, err := json.Marshal(c) - if err != nil { - return tc.Alerts{}, reqInf, err - } - - var v url.Values - v.Add("name", name) - endpoint := API_v14_CAPABILITIES + "?" + v.Encode() - - var resp *http.Response - resp, reqInf.RemoteAddr, err = to.request(http.MethodPut, endpoint, reqBody) - if err != nil { - return tc.Alerts{}, reqInf, err - } - defer resp.Body.Close() - - var alerts tc.Alerts - err = json.NewDecoder(resp.Body).Decode(&alerts) - return alerts, reqInf, err -} - // GetCapabilities retrieves all capabilities. func (to *Session) GetCapabilities() ([]tc.Capability, ReqInf, error) { resp, remoteAddr, err := to.request(http.MethodGet, API_v14_CAPABILITIES, nil) @@ -108,21 +82,3 @@ func (to *Session) GetCapability(c string) (tc.Capability, ReqInf, error) { return data.Response[0], reqInf, nil } - -// DeleteCapability deletes the capability named 'c'. -func (to *Session) DeleteCapability(c string) (alerts tc.Alerts, reqInf ReqInf, err error) { - reqInf.CacheHitStatus = CacheHitStatusMiss - var v url.Values - v.Add("name", c) - endpoint := API_v14_CAPABILITIES + "?" + v.Encode() - - var resp *http.Response - resp, reqInf.RemoteAddr, err = to.request(http.MethodDelete, endpoint, nil) - if err != nil { - return - } - defer resp.Body.Close() - - err = json.NewDecoder(resp.Body).Decode(&alerts) - return -} diff --git a/traffic_ops/testing/api/v1/todb_test.go b/traffic_ops/testing/api/v1/todb_test.go index e8e045100e..199091b2ae 100644 --- a/traffic_ops/testing/api/v1/todb_test.go +++ b/traffic_ops/testing/api/v1/todb_test.go @@ -280,6 +280,7 @@ INSERT INTO to_extension (name, version, info_url, isactive, script_file, server func Teardown(db *sql.DB) error { sqlStmt := ` + DELETE FROM capability; DELETE FROM deliveryservices_required_capability; DELETE FROM server_server_capability; DELETE FROM server_server_capability; diff --git a/traffic_ops/testing/api/v14/capabilities_test.go b/traffic_ops/testing/api/v14/capabilities_test.go index 94cb7ee898..daba83b8db 100644 --- a/traffic_ops/testing/api/v14/capabilities_test.go +++ b/traffic_ops/testing/api/v14/capabilities_test.go @@ -42,10 +42,8 @@ var staticCapabilities = []tc.Capability { } func TestCapabilities(t *testing.T) { - WithObjs(t, []TCObj{Capabilities}, func() { - ReplaceTestCapability(t) - GetTestCapabilities(t) - }) + CreateTestCapabilities(t) + GetTestCapabilities(t) } func CreateTestCapabilities(t *testing.T) { @@ -107,50 +105,3 @@ func GetTestCapabilities(t *testing.T) { t.Errorf("Capability '%s' existed in the test data but didn't appear in the response!", c) } } - -func ReplaceTestCapability(t *testing.T) { - if len(testData.Capabilities) < 1 { - t.Fatalf("No test capabilities!") - } - - c := testData.Capabilities[0] - c.Name += "TEST" - c.Description += "REPLACE TEST" - - alerts, _, err := TOSession.ReplaceCapabilityByName(testData.Capabilities[0].Name, c) - log.Debugln("alerts:", alerts) - if err != nil { - t.Fatalf("Failed to replace capability %s: %v", testData.Capabilities[0].Name, err) - } - - // The old one shouldn't exist anymore - resp, _, err := TOSession.GetCapability(testData.Capabilities[0].Name) - if err == nil { - t.Errorf("Expected an error, but got response: %v", resp) - } - - // ... but the new one should - resp, _, err = TOSession.GetCapability(c.Name) - if err != nil { - t.Errorf("Expected to get new capability, error: %v", err) - } - - // Now we need to replace it again, so the DELETE can catch it - alerts, _, err = TOSession.ReplaceCapabilityByName(c.Name, testData.Capabilities[0]) - log.Debugln("alerts:", alerts) - if err != nil { - t.Errorf("Failed to re-replace capability: %s", err) - } - - log.Debugln("response:", resp) -} - -func DeleteTestCapabilities(t *testing.T) { - for _,c := range testData.Capabilities { - resp, _, err := TOSession.DeleteCapability(c.Name) - log.Debugln("Response: ", c.Name, " ", resp) - if err != nil { - t.Errorf("could not delete capability: %v", err) - } - } -} diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 3725da1f84..051cf3a0b8 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -44,19 +44,6 @@ VALUES ($1, $2) RETURNING description, last_updated, name ` -const replaceQuery = ` -UPDATE capability -SET name=$1, description=$2 -WHERE name=$3 -RETURNING description, last_updated, name -` - -const deleteQuery = ` -DELETE FROM capability -WHERE name=$1 -RETURNING description, last_updated, name -` - func Read(w http.ResponseWriter, r *http.Request) { inf, sysErr, userErr, errCode := api.NewInfo(r, nil, nil) tx := inf.Tx.Tx @@ -160,101 +147,6 @@ func Create(w http.ResponseWriter, r *http.Request) { api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Created", cap.Name), inf.User, tx) } -func Replace(w http.ResponseWriter, r *http.Request) { - inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name"}, nil) - tx := inf.Tx.Tx - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - decoder := json.NewDecoder(r.Body) - var cap tc.Capability - if err := decoder.Decode(&cap); err != nil { - userErr = fmt.Errorf("Couldn't parse capacity: %v", err) - errCode = http.StatusBadRequest - api.HandleErr(w, r, tx, errCode, userErr, nil) - return - } - - if cap.Name != inf.Params["name"] { - if ok, err := capabilityNameExists(cap.Name, tx); err != nil { - sysErr = fmt.Errorf("Checking for capability %s's existence: %v", cap.Name, err) - errCode = http.StatusInternalServerError - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } else if ok { - errCode = http.StatusConflict - userErr = fmt.Errorf("A capability named '%s' already exists!", cap.Name) - api.HandleErr(w, r, tx, errCode, userErr, nil) - return - } - } - - row := tx.QueryRow(replaceQuery, cap.Name, cap.Description, inf.Params["name"]) - if err := row.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { - if err == sql.ErrNoRows { - errCode = http.StatusNotFound - userErr = fmt.Errorf("No capability '%s' found!", inf.Params["name"]) - } else { - errCode = http.StatusInternalServerError - sysErr = fmt.Errorf("Replacing capability '%s' with '%s': %v", inf.Params["name"], cap.Name, err) - } - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - - msg := "CAPABILITY: %s, ACTION: Replaced with capability '%s' (%s)'" - msg = fmt.Sprintf(msg, inf.Params["name"], cap.Name, cap.Description) - api.CreateChangeLogRawTx(api.ApiChange, msg, inf.User, tx) - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability was updated.", cap) -} - -func Delete(w http.ResponseWriter, r *http.Request) { - inf, sysErr, userErr, errCode := api.NewInfo(r, []string{"name"}, nil) - tx := inf.Tx.Tx - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - capName := inf.Params["name"] - - row := tx.QueryRow(`SELECT COUNT(*) FROM api_capability WHERE capability=$1`, capName) - var num uint - if err := row.Scan(&num); err != nil && err != sql.ErrNoRows { - sysErr = fmt.Errorf("Checking for API routes linked to capability %s: %v", capName, err) - errCode = http.StatusInternalServerError - api.HandleErr(w, r, tx, errCode, nil, sysErr) - return - } - - if num > 0 { - userErr = fmt.Errorf("Capability '%s' is used by %d API routes; cannot be deleted!", capName, num) - errCode = http.StatusConflict - api.HandleErr(w, r, tx, errCode, userErr, nil) - return - } - - row = tx.QueryRow(deleteQuery, capName) - var cap tc.Capability - if err := row.Scan(&cap.Description, &cap.LastUpdated, &cap.Name); err != nil { - if err == sql.ErrNoRows { - errCode = http.StatusNotFound - userErr = fmt.Errorf("No capability '%s' found!", capName) - } else { - sysErr = fmt.Errorf("Deleting capability %s: %v", capName, err) - errCode = http.StatusInternalServerError - } - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability deleted.", cap) - api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Deleted", capName), inf.User, tx) -} func capabilityNameExists(c string, tx *sql.Tx) (bool, error) { row := tx.QueryRow(`SELECT name FROM capability WHERE name=$1`, c) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 9d86d0b25e..4ef6c79e63 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -170,10 +170,8 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) { {1.1, http.MethodDelete, `cachegroupparameters/{cachegroupID}/{parameterId}$`, api.DeleteHandler(&cachegroupparameter.TOCacheGroupParameter{}), auth.PrivLevelOperations, Authenticated, nil, 912449733, perlBypass}, //Capabilities - {1.1, http.MethodGet, `capabilities(/|\.json)?$`, capabilities.Read, auth.PrivLevelReadOnly, Authenticated, nil}, - {1.1, http.MethodPost, `capabilities(/|\.json)?$`, capabilities.Create, auth.PrivLevelOperations, Authenticated, nil}, - {1.4, http.MethodPut, `capabilities$`, capabilities.Replace, auth.PrivLevelOperations, Authenticated, nil}, - {1.4, http.MethodDelete, `capabilities$`, capabilities.Delete, auth.PrivLevelOperations, Authenticated, nil}, + {1.1, http.MethodGet, `capabilities(/|\.json)?$`, capabilities.Read, auth.PrivLevelReadOnly, Authenticated, nil, 8008135, perlBypass}, + {1.1, http.MethodPost, `capabilities(/|\.json)?$`, capabilities.Create, auth.PrivLevelOperations, Authenticated, nil, -1, perlBypass}, //CDN {1.1, http.MethodGet, `cdns/name/{name}/sslkeys/?(\.json)?$`, cdn.GetSSLKeys, auth.PrivLevelAdmin, Authenticated, nil, 1278581772, noPerlBypass}, From 8a2b23a37a2b2d4077a786e0a680b1bb0879af74 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 6 Jan 2020 12:31:58 -0700 Subject: [PATCH 19/28] Removed Python client methods for removed handlers --- .../clients/python/trafficops/tosession.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py index 12cc2cc313..0b1d22fe95 100644 --- a/traffic_control/clients/python/trafficops/tosession.py +++ b/traffic_control/clients/python/trafficops/tosession.py @@ -565,30 +565,6 @@ def create_capability(self, data=None): :raises: Union[LoginError, OperationError] """ - @api_request(u'put', u'capabilities', ( u'1.4',)) - def update_capability(self, query_params=None, data=None): - """ - Replaces a capability - :ref:`to-api-capabilities` - :param query_params: See API page for more information on accepted parameters - :type query_params: Dict[str, Any] - :param data: See API page for more information on accepted request body data - :type data: Any - :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] - :raises: Union[LoginError, OperationError] - """ - - @api_request(u'delete', u'capabilities', (u'1.4',)) - def delete_capability(self, query_params=None): - """ - Deletes a capability - :ref:`to-api-capabilities` - :param query_params: See API page for more information on accepted parameters - :type query_params: Dict[str, Any] - :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] - :raises: Union[LoginError, OperationError] - """ - # # CDN # From 2df73e5fb5f6e3728354664532ca25f297967f49 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 6 Jan 2020 12:36:50 -0700 Subject: [PATCH 20/28] Fixed missing format arg --- traffic_ops/traffic_ops_golang/capabilities/capabilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 051cf3a0b8..70684f9372 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -103,7 +103,7 @@ func Create(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) var cap tc.Capability if err := decoder.Decode(&cap); err != nil { - sysErr = fmt.Errorf("Decoding request body: %v") + sysErr = fmt.Errorf("Decoding request body: %v", err) errCode = http.StatusInternalServerError api.HandleErr(w, r, tx, errCode, nil, sysErr) return From a57162926bf92ef3cb3ba47ecb2b5c60234da2d4 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 6 Jan 2020 13:12:42 -0700 Subject: [PATCH 21/28] Added deprecation notice --- traffic_ops/traffic_ops_golang/capabilities/capabilities.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go index 70684f9372..d59b1c78ce 100644 --- a/traffic_ops/traffic_ops_golang/capabilities/capabilities.go +++ b/traffic_ops/traffic_ops_golang/capabilities/capabilities.go @@ -143,7 +143,10 @@ func Create(w http.ResponseWriter, r *http.Request) { return } - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Capability created.", cap) + alerts := tc.CreateAlerts(tc.SuccessLevel, "Capability created.") + alerts.AddNewAlert(tc.WarnLevel, "This endpoint is deprecated, and will be removed in the future") + + api.WriteAlertsObj(w, r, http.StatusOK, alerts, cap) api.CreateChangeLogRawTx(api.ApiChange, fmt.Sprintf("CAPABILITY: %s, ACTION: Created", cap.Name), inf.User, tx) } From f6d56ada4af756b01b02683c5b3eaa42dae8d332 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 7 Jan 2020 12:09:16 -0700 Subject: [PATCH 22/28] Updated documentation --- docs/source/api/capabilities.rst | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/source/api/capabilities.rst b/docs/source/api/capabilities.rst index 26eadf0e9a..821ccaf6ea 100644 --- a/docs/source/api/capabilities.rst +++ b/docs/source/api/capabilities.rst @@ -112,12 +112,12 @@ Request Structure User-Agent: curl/7.47.0 Accept: */* Cookie: mojolicious=... - Content-Length: 108 + Content-Length: 73 Content-Type: application/json { - "name": "test", - "description": "This is only a test. If this were a real capability, it might do something" + "name": "testquest", + "description": "A test capability for API examples" } Response Structure @@ -134,22 +134,25 @@ Response Structure Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Set-Cookie, Cookie Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE Access-Control-Allow-Origin: * + Content-Encoding: gzip Content-Type: application/json - Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly - Set-Cookie: mojolicious=...; Path=/; HttpOnly - Whole-Content-Sha512: A1rjpDy+O+oooYeer2j09pCEDpPEFk/nt8/AaJye2sLkfy93MtquCsB/Rlgz7sCYputd/EPOPDyi2WkN8UB1Rw== + Set-Cookie: mojolicious=...; Path=/; Expires=Tue, 07 Jan 2020 20:06:18 GMT; Max-Age=3600; HttpOnly X-Server-Name: traffic_ops_golang/ - Date: Thu, 15 Aug 2019 17:18:03 GMT - Content-Length: 219 + Date: Tue, 07 Jan 2020 19:06:18 GMT + Content-Length: 225 { "alerts": [ { "text": "Capability created.", "level": "success" + }, + { + "text": "This endpoint is deprecated, and will be removed in the future", + "level": "warning" } ], "response": { - "description": "This is only a test. If this were a real capability, it might do something", - "lastUpdated": "2019-08-15 17:18:03+00", - "name": "test" + "description": "A test capability for API examples", + "lastUpdated": "2020-01-07 19:06:18+00", + "name": "testquest" }} From 166848c6bd9551a1ada37e5e467f60128fdd037c Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 8 Jan 2020 08:22:45 -0700 Subject: [PATCH 23/28] Properly removed capabilities from withobjs --- traffic_ops/testing/api/v1/withobjs_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/traffic_ops/testing/api/v1/withobjs_test.go b/traffic_ops/testing/api/v1/withobjs_test.go index 496c6fd5c2..be722e1045 100644 --- a/traffic_ops/testing/api/v1/withobjs_test.go +++ b/traffic_ops/testing/api/v1/withobjs_test.go @@ -39,7 +39,6 @@ const ( CacheGroups TCObj = iota CacheGroupsDeliveryServices CacheGroupParameters - Capabilities CDNs CDNFederations Coordinates @@ -80,7 +79,6 @@ var withFuncs = map[TCObj]TCObjFuncs{ CacheGroups: {CreateTestCacheGroups, DeleteTestCacheGroups}, CacheGroupsDeliveryServices: {CreateTestCachegroupsDeliveryServices, DeleteTestCachegroupsDeliveryServices}, CacheGroupParameters: {CreateTestCacheGroupParameters, DeleteTestCacheGroupParameters}, - Capabilities: {CreateTestCapabilities, DeleteTestCapabilities}, CDNs: {CreateTestCDNs, DeleteTestCDNs}, CDNFederations: {CreateTestCDNFederations, DeleteTestCDNFederations}, Coordinates: {CreateTestCoordinates, DeleteTestCoordinates}, From 0e4b3c4dd9d4194b61aa45afb8bf03a0c4145c5f Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 8 Jan 2020 08:29:15 -0700 Subject: [PATCH 24/28] fixed teardown order --- traffic_ops/testing/api/v1/todb_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/traffic_ops/testing/api/v1/todb_test.go b/traffic_ops/testing/api/v1/todb_test.go index 199091b2ae..6d5764e198 100644 --- a/traffic_ops/testing/api/v1/todb_test.go +++ b/traffic_ops/testing/api/v1/todb_test.go @@ -280,7 +280,7 @@ INSERT INTO to_extension (name, version, info_url, isactive, script_file, server func Teardown(db *sql.DB) error { sqlStmt := ` - DELETE FROM capability; + DELETE FROM api_capability; DELETE FROM deliveryservices_required_capability; DELETE FROM server_server_capability; DELETE FROM server_server_capability; @@ -295,6 +295,7 @@ func Teardown(db *sql.DB) error { DELETE FROM deliveryservice_tmuser; DELETE FROM tm_user; DELETE FROM role; + DELETE FROM capability; ALTER SEQUENCE role_id_seq RESTART WITH 1; DELETE FROM deliveryservice_regex; DELETE FROM regex; From 78e826e894fb4b4e8451699260376f650923ac65 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 8 Jan 2020 08:37:41 -0700 Subject: [PATCH 25/28] setting up API capabilities --- traffic_ops/testing/api/v1/todb_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/traffic_ops/testing/api/v1/todb_test.go b/traffic_ops/testing/api/v1/todb_test.go index 6d5764e198..8151020863 100644 --- a/traffic_ops/testing/api/v1/todb_test.go +++ b/traffic_ops/testing/api/v1/todb_test.go @@ -68,6 +68,12 @@ func SetupTestData(*sql.DB) error { os.Exit(1) } + err = SetupAPICapabilities(db) + if err != nil { + fmt.Printf("\nError setting up APICapabilities %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err) + os.Exit(1) + } + err = SetupTenants(db) if err != nil { fmt.Printf("\nError setting up tenant %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, err) @@ -139,6 +145,20 @@ INSERT INTO capability (name, description) VALUES ('cdn-read','View CDN configur return nil } +func SetupAPICapabilities(db *sql.DB) error { + sqlStmt := ` +INSERT INTO api_capability (http_method, route, capability) VALUES ('GET', '/asns', 'asns-read') ON CONFLICT DO NOTHING; +INSERT INTO api_capability (http_method, route, capability) VALUES ('POST', '/asns', 'asns-write') ON CONFLICT DO NOTHING; +INSERT INTO api_capability (http_method, route, capability) VALUES ('GET', '/cachegroups', 'cache-groups-read') ON CONFLICT DO NOTHING; +` + + err := execSQL(db, sqlStmt, "api_capability") + if err != nil { + return fmt.Errorf("exec failed %v", err) + } + return nil +} + func SetupRoleCapabilities(db *sql.DB) error { sqlStmt := ` INSERT INTO role_capability (role_id, cap_name) VALUES (4,'all-write') ON CONFLICT DO NOTHING; From 0c74ecfe62b18b85690ef140829407493625f30c Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 8 Jan 2020 08:41:58 -0700 Subject: [PATCH 26/28] Added missing capabilities --- traffic_ops/testing/api/v1/todb_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/traffic_ops/testing/api/v1/todb_test.go b/traffic_ops/testing/api/v1/todb_test.go index 8151020863..e1b40e5fcc 100644 --- a/traffic_ops/testing/api/v1/todb_test.go +++ b/traffic_ops/testing/api/v1/todb_test.go @@ -137,6 +137,9 @@ func SetupCapabilities(db *sql.DB) error { INSERT INTO capability (name, description) VALUES ('all-read','Full read access') ON CONFLICT DO NOTHING; INSERT INTO capability (name, description) VALUES ('all-write','Full write access') ON CONFLICT DO NOTHING; INSERT INTO capability (name, description) VALUES ('cdn-read','View CDN configuration') ON CONFLICT DO NOTHING; +INSERT INTO capability (name, description) VALUES ('asns-read', 'Read ASNs') ON CONFLICT DO NOTHING; +INSERT INTO capability (name, description) VALUES ('asns-write', 'Write ASNs') ON CONFLICT DO NOTHING; +INSERT INTO capability (name, description) VALUES ('cache-groups-read', 'Read CGs') ON CONFLICT DO NOTHING; ` err := execSQL(db, sqlStmt, "capability") if err != nil { From 124543fd219f2a6af2d81f223121a39454ccf3f9 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 8 Jan 2020 08:44:44 -0700 Subject: [PATCH 27/28] Added new static capabilities --- traffic_ops/testing/api/v14/capabilities_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/traffic_ops/testing/api/v14/capabilities_test.go b/traffic_ops/testing/api/v14/capabilities_test.go index daba83b8db..4f2008f113 100644 --- a/traffic_ops/testing/api/v14/capabilities_test.go +++ b/traffic_ops/testing/api/v14/capabilities_test.go @@ -39,6 +39,18 @@ var staticCapabilities = []tc.Capability { Name: "cdn-read", Description: "View CDN configuration", }, + tc.Capability { + Name: "asns-read", + Description: "Read ASNs", + }, + tc.Capability { + Name: "asns-write", + Description: "Write ASNs", + }, + tc.Capability { + Name: "cache-groups-read", + Description: "Read CGs", + }, } func TestCapabilities(t *testing.T) { From 8eba7e7ba3e3dec6354233cbfb1d0cbe2e68447e Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Thu, 9 Jan 2020 10:46:04 -0700 Subject: [PATCH 28/28] Removed premature deprecation notice in the docs --- docs/source/api/capabilities_name.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/source/api/capabilities_name.rst b/docs/source/api/capabilities_name.rst index 6afa1a3e92..f82c186810 100644 --- a/docs/source/api/capabilities_name.rst +++ b/docs/source/api/capabilities_name.rst @@ -19,9 +19,6 @@ ``capabilities/{{name}}`` ************************* -.. deprecated:: 1.4 - All of the functionality provided by this endpoint can be accomplished using :ref:`to-api-capabilities` instead. This endpoint will be removed in a future version of :abbr:`ATC (Apache Traffic Control)`. - ``GET`` ======= Get a capability by name.