mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
added *http filter type
This commit is contained in:
committed by
Dan Christian Bogos
parent
b76d61281c
commit
584a55ab25
@@ -197,7 +197,7 @@ func sentrypeerGetToken(tokenUrl, clientID, clientSecret, audience, grantType st
|
||||
utils.GrantTypeCfg: grantType,
|
||||
}
|
||||
jsonPayload, _ := json.Marshal(payload)
|
||||
resp, err = getHTTP(http.MethodPost, tokenUrl, bytes.NewBuffer(jsonPayload), map[string][]string{utils.ContentType: {utils.JsonBody}})
|
||||
resp, err = getHTTP(http.MethodPost, tokenUrl, bytes.NewBuffer(jsonPayload), map[string]string{utils.ContentType: utils.JsonBody})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -216,7 +216,7 @@ func sentrypeerGetToken(tokenUrl, clientID, clientSecret, audience, grantType st
|
||||
// sentrypeerHasData return a boolean based on query response on finding ip/number
|
||||
func sentrypeerHasData(itemId, token, url string) (found bool, err error) {
|
||||
var resp *http.Response
|
||||
resp, err = getHTTP(http.MethodGet, url, nil, map[string][]string{utils.AuthorizationHdr: {fmt.Sprintf("%s %s", utils.BearerAuth, token)}})
|
||||
resp, err = getHTTP(http.MethodGet, url, nil, map[string]string{utils.AuthorizationHdr: fmt.Sprintf("%s %s", utils.BearerAuth, token)})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -238,11 +238,56 @@ func sentrypeerHasData(itemId, token, url string) (found bool, err error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
func getHTTP(method, url string, payload io.Reader, headers map[string][]string) (resp *http.Response, err error) {
|
||||
// send an http get request to a server
|
||||
// expects a boolean reply
|
||||
// when element is set to *any the CGREvent is sent as JSON body
|
||||
// when the element is specified as a path e.g ~*req.Account is sent as query string pair ,the path being the key with the value extracted from dataprovider
|
||||
func externalAPI(urlStr string, dDP any, fieldname, value string) (reply bool, err error) {
|
||||
var resp *http.Response
|
||||
parsedURL, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if fieldname != utils.MetaAny {
|
||||
queryParams := parsedURL.Query()
|
||||
queryParams.Set(fieldname, value)
|
||||
parsedURL.RawQuery = queryParams.Encode()
|
||||
resp, err = getHTTP(http.MethodGet, parsedURL.String(), nil, nil)
|
||||
} else {
|
||||
var data []byte
|
||||
data, err = json.Marshal(dDP)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error marshaling data: %w", err)
|
||||
}
|
||||
resp, err = getHTTP(http.MethodGet, parsedURL.String(), bytes.NewReader(data), nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error processing the request: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusMultipleChoices {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
return false, fmt.Errorf("http request returned non-OK status code: %d ,body: %v ,err: %w", resp.StatusCode, string(body), err)
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&reply); err != nil {
|
||||
return false, fmt.Errorf("error decoding response: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// constructs an request via parameters provided,url,header and payload ,uses defaultclient for sending the request
|
||||
func getHTTP(method, url string, payload io.Reader, headers map[string]string) (resp *http.Response, err error) {
|
||||
var req *http.Request
|
||||
if req, err = http.NewRequest(method, url, payload); err != nil {
|
||||
return
|
||||
}
|
||||
req.Header = headers
|
||||
for k, hVal := range headers {
|
||||
req.Header.Add(k, hVal)
|
||||
}
|
||||
return http.DefaultClient.Do(req)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
@@ -226,7 +227,7 @@ func (fltr *Filter) Compile() (err error) {
|
||||
|
||||
var supportedFiltersType utils.StringSet = utils.NewStringSet([]string{
|
||||
utils.MetaString, utils.MetaContains, utils.MetaPrefix, utils.MetaSuffix,
|
||||
utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations,
|
||||
utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations, utils.MetaHTTP,
|
||||
utils.MetaEmpty, utils.MetaExists, utils.MetaLessThan, utils.MetaLessOrEqual,
|
||||
utils.MetaGreaterThan, utils.MetaGreaterOrEqual, utils.MetaEqual,
|
||||
utils.MetaIPNet, utils.MetaAPIBan, utils.MetaSentryPeer, utils.MetaActivationInterval,
|
||||
@@ -252,6 +253,9 @@ func NewFilterRule(rfType, fieldName string, vals []string) (*FilterRule, error)
|
||||
rType = utils.Meta + strings.TrimPrefix(rfType, utils.MetaNot)
|
||||
negative = true
|
||||
}
|
||||
if strings.HasPrefix(rfType, utils.MetaHTTP) {
|
||||
rType = utils.MetaHTTP
|
||||
}
|
||||
if !supportedFiltersType.Has(rType) {
|
||||
return nil, fmt.Errorf("Unsupported filter Type: %s", rfType)
|
||||
}
|
||||
@@ -361,6 +365,10 @@ func (fltr *FilterRule) Pass(dDP utils.DataProvider) (result bool, err error) {
|
||||
case utils.MetaRegex, utils.MetaNotRegex:
|
||||
result, err = fltr.passRegex(dDP)
|
||||
default:
|
||||
if strings.HasPrefix(fltr.Type, utils.MetaHTTP) && strings.Index(fltr.Type, "#") == len(utils.MetaHTTP) {
|
||||
result, err = fltr.passHttp(dDP)
|
||||
break
|
||||
}
|
||||
err = utils.ErrPrefixNotErrNotImplemented(fltr.Type)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -771,3 +779,22 @@ func (fltr *FilterRule) passRegex(dDP utils.DataProvider) (bool, error) {
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (fltr *FilterRule) passHttp(dDP utils.DataProvider) (bool, error) {
|
||||
strVal, err := fltr.rsrElement.ParseDataProvider(dDP)
|
||||
if err != nil {
|
||||
if err == utils.ErrNotFound {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
parts := strings.Split(fltr.Type, utils.HashtagSep)
|
||||
if len(parts) != 2 {
|
||||
return false, errors.New("url is not specified")
|
||||
}
|
||||
//extracting the url from the type
|
||||
url := strings.Trim(parts[1], "[]")
|
||||
return externalAPI(url, dDP, fltr.Element, strVal)
|
||||
|
||||
}
|
||||
|
||||
@@ -2676,3 +2676,85 @@ func TestFilterPassTiming(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHttpInlineFilter(t *testing.T) {
|
||||
|
||||
dP := &utils.MapStorage{
|
||||
utils.MetaReq: map[string]any{
|
||||
"Attribute": "AttributeProfile1",
|
||||
utils.CGRID: "CGRATES_ID1",
|
||||
utils.AccountField: "1002",
|
||||
utils.AnswerTime: time.Date(2013, 12, 30, 14, 59, 31, 0, time.UTC),
|
||||
},
|
||||
}
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if len(r.URL.Query()) != 0 {
|
||||
queryVal := r.URL.Query()
|
||||
reply := queryVal.Has("~*req.Account") && queryVal.Get("~*req.Account") == "1002"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, reply)
|
||||
return
|
||||
}
|
||||
|
||||
var data map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
_, has := data["*req"]
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprint(w, has)
|
||||
|
||||
}))
|
||||
url := "*http#" + "[" + srv.URL + "]"
|
||||
|
||||
exp := &Filter{
|
||||
Tenant: "cgrates.org",
|
||||
ID: url + ":" + "~*req.Account:",
|
||||
Rules: []*FilterRule{
|
||||
{
|
||||
Type: url,
|
||||
Element: "~*req.Account",
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := exp.Compile(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fl, err := NewFilterFromInline("cgrates.org", url+":"+"~*req.Account:"); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(exp, fl) {
|
||||
t.Errorf("Expected: %s , received: %s", utils.ToJSON(exp), utils.ToJSON(fl))
|
||||
}
|
||||
if pass, err := exp.Rules[0].Pass(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if !pass {
|
||||
t.Error("should had passed")
|
||||
}
|
||||
|
||||
exp2 := &Filter{
|
||||
Tenant: "cgrates.org",
|
||||
ID: url + ":" + "*any:",
|
||||
Rules: []*FilterRule{
|
||||
{
|
||||
Type: url,
|
||||
Element: "*any",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if fl2, err := NewFilterFromInline("cgrates.org", url+":"+"*any:"); err != nil {
|
||||
t.Error(err)
|
||||
} else if fl2.Rules[0].Type != exp2.Rules[0].Type {
|
||||
t.Errorf("Expected: %s , received: %s", utils.ToJSON(exp2), utils.ToJSON(fl2))
|
||||
} else if pass, err := fl2.Rules[0].Pass(dP); err != nil {
|
||||
t.Error(err)
|
||||
} else if !pass {
|
||||
t.Error("should had passed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,6 +1179,7 @@ const (
|
||||
MetaActivationInterval = "*ai"
|
||||
MetaRegex = "*regex"
|
||||
MetaContains = "*contains"
|
||||
MetaHTTP = "*http"
|
||||
|
||||
MetaNotString = "*notstring"
|
||||
MetaNotPrefix = "*notprefix"
|
||||
|
||||
Reference in New Issue
Block a user