com.moesif.api.controllers.APIController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of moesifapi Show documentation
Show all versions of moesifapi Show documentation
Java API Library for Moesif
The newest version!
/*
* MoesifAPILib
*
*
*/
package com.moesif.api.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mashape.unirest.http.Unirest;
import com.moesif.api.APIHelper;
import com.moesif.api.Configuration;
import com.moesif.api.IAPIController;
import com.moesif.api.controllers.syncwrapper.APICallBackCatcher;
import com.moesif.api.exceptions.APIException;
import com.moesif.api.http.client.APICallBack;
import com.moesif.api.http.client.HttpContext;
import com.moesif.api.http.request.HttpRequest;
import com.moesif.api.http.response.HttpResponse;
import com.moesif.api.models.*;
import org.apache.http.client.HttpRequestRetryHandler;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class APIController extends BaseController implements IAPIController {
//private static variables for the singleton pattern
private static final Logger logger = Logger.getLogger(APIController.class.toString());
private static final String APP_CONFIG_ETAG_HEADER = "x-moesif-config-etag";
private static final String RULES_ETAG_HEADER = "x-moesif-rules-tag";
// wait 5 minutes before grabbing the new config (different servers might have different states)
private static final int APP_CONFIG_DEBOUNCE = 1000 * 60 * 5; // 5 minutes
private volatile long lastAppConfigFetch;
private boolean shouldSyncAppConfig = true;
private volatile AppConfigModel appConfigModel;
private volatile String appConfigEtag;
private volatile List governanceRules;
private volatile String rulesEtag;
private Configuration config;
private ObjectMapper objectMapper = new ObjectMapper();
public APIController(Configuration config) {
this.config = config;
getInitAppConfigModel();
getGovernanceRulesModel();
}
public void setHttpRequestRetryHandler(HttpRequestRetryHandler httpRequestRetryHandler) {
Unirest.setHttpRequestRetryHandler(httpRequestRetryHandler);
}
/**
* Add Single API Event Call
* @param body The event to create
* @throws Throwable on error creating event
*/
public Map createEvent(
final EventModel body
) throws Throwable {
QueryInfo qInfo = getQueryInfo("/v1/events");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
//invoke the callback before request if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnBeforeRequest(_request);
}
// make the API call
return executeRequest(_request);
}
/**
* Add Single API Event Call
* @param body The event to create
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error creating event
*/
public void createEventAsync(
final EventModel body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/events");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Add multiple API Events in a single batch
* @param body The events to create
* @throws Throwable on error creating event
*/
public Map createEventsBatch(
final List body
) throws Throwable {
QueryInfo qInfo = getQueryInfo("/v1/events/batch");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
// make the API call
return executeRequest(_request);
}
public void createEventsBatchAsync(
final List body,
final APICallBack callBack
) throws JsonProcessingException {
createEventsBatchAsync(body, callBack,false);
}
/**
* Add multiple API Events in a single batch
* @param body The events to create
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error creating event
*/
public void createEventsBatchAsync(
final List body,
final APICallBack callBack,
boolean useGzip
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/events/batch");
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack, useGzip);
}
/**
* Send a single custom action to the API.
*
* @param body The ActionModel object to send.
* @throws Throwable if an error occurs during the API call.
*/
public void createAction(final ActionModel body) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
createActionAsync(body, callback);
if (!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Send a single custom action to the API asynchronously.
*
* @param body The ActionModel object to send.
* @param callBack Called after the HTTP response is received.
* @throws JsonProcessingException if an error occurs during serialization.
*/
public void createActionAsync(
final ActionModel body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/actions");
// Prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(
qInfo._queryUrl,
qInfo._headers,
APIHelper.serialize(body)
);
executeRequestAsync(_request, callBack);
}
/**
* Send multiple custom actions to the API in a single batch.
*
* @param body The list of ActionModel objects to send.
* @throws Throwable if an error occurs during the API call.
*/
public void createActionsBatch(final List body) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
createActionsBatchAsync(body, callback);
if (!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Send multiple custom actions to the API in a single batch asynchronously.
*
* @param body The list of ActionModel objects to send.
* @param callBack Called after the HTTP response is received.
* @throws JsonProcessingException if an error occurs during serialization.
*/
public void createActionsBatchAsync(
final List body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/actions/batch");
// Prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(
qInfo._queryUrl,
qInfo._headers,
APIHelper.serialize(body)
);
executeRequestAsync(_request, callBack);
}
/**
* Update a Single User
* @param body The user to update
* @throws Throwable on error updating user
*/
public void updateUser(
final UserModel body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateUserAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update a Single User async
* @param body The user to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error updating user
*/
public void updateUserAsync(
final UserModel body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/users");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Update multiple Users in a single batch
* @param body The list of users to update
* @throws Throwable on error updating users
*/
public void updateUsersBatch(
final List body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateUsersBatchAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update multiple Users in a single batch async
* @param body The list of users to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error updating users
*/
public void updateUsersBatchAsync(
final List body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/users/batch");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Update a Single Company
* @param body The company to update
* @throws Throwable on error updating a company
*/
public void updateCompany(
final CompanyModel body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateCompanyAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update a Single Company async
* @param body The company to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error updating a company
*/
public void updateCompanyAsync(
final CompanyModel body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/companies");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Update multiple Companies in a single batch
* @param body The list of companies to update
* @throws Throwable on error updating companies
*/
public void updateCompaniesBatch(
final List body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateCompaniesBatchAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update multiple Companies in a single batch async
* @param body The list of companies to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error updating companies
*/
public void updateCompaniesBatchAsync(
final List body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/companies/batch");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Update a Single Subscription
* @param body The subscriptuon to update
* @throws Throwable on error creating event
*/
public void updateSubscription(
final SubscriptionModel body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateSubscriptionAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update a Single Subscription async
* @param body The subscription to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error creating event
*/
public void updateSubscriptionAsync(
final SubscriptionModel body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/subscriptions");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Update multiple Subscriptions in a single batch
* @param body The list of subscriptions to update
* @throws Throwable on error creating event
*/
public void updateSubscriptionsBatch(
final List body
) throws Throwable {
APICallBackCatcher callback = new APICallBackCatcher();
updateSubscriptionsBatchAsync(body, callback);
if(!callback.isSuccess())
throw callback.getError();
callback.getResult();
}
/**
* Update multiple Subscriptions in a single batch async
* @param body The list of subscriptions to update
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error creating event
*/
public void updateSubscriptionsBatchAsync(
final List body,
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/subscriptions/batch");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().postBody(qInfo._queryUrl, qInfo._headers, APIHelper.serialize(body));
executeRequestAsync(_request, callBack);
}
/**
* Get the Application config
* @throws Throwable on error getting app config
*/
public HttpResponse getAppConfig() throws Throwable {
QueryInfo qInfo = getQueryInfo("/v1/config");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().get(qInfo._queryUrl, qInfo._headers, null);
//invoke the callback before request if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnBeforeRequest(_request);
}
// make the API call
HttpResponse _response = getClientInstance().executeAsString(_request);
// Wrap the request and the response in an HttpContext object
HttpContext _context = new HttpContext(_request, _response);
//invoke the callback after response if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnAfterResponse(_context);
}
//handle errors defined at the API level
validateResponse(_response, _context);
// Return headers to the client
return _response;
}
private void getInitAppConfigModel() {
try {
HttpResponse response = getAppConfig();
String newAppConfigEtag = response.getHeaders().get(APP_CONFIG_ETAG_HEADER);
InputStream respBodyIs = response.getRawBody();
appConfigModel = parseAppConfigModel(respBodyIs);
respBodyIs.close();
if (config.debug) {
logger.info("App Config Model returned is " + appConfigModel);
}
appConfigEtag = newAppConfigEtag;
lastAppConfigFetch = new Date().getTime();
} catch( Throwable e){
e.printStackTrace();
logger.warning("Error getting AppConfig: " + e.getMessage());
appConfigModel = getDefaultAppConfig();
lastAppConfigFetch = new Date().getTime();
}
}
private void getGovernanceRulesModel() {
try {
HttpResponse response = getGovernanceRules();
this.rulesEtag = response.getHeaders().get(RULES_ETAG_HEADER);
InputStream respBodyIs = response.getRawBody();
this.governanceRules = parseGovernanceRulesModel(respBodyIs);
respBodyIs.close();
if (config.debug) {
logger.info("Governance model returned is " + this.governanceRules);
}
} catch( Throwable e){
logger.warning("Error getting GovernanceRules: " + e.getMessage());
this.governanceRules = new ArrayList<>();
}
}
/**
* Get the Application config async
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error getting app config
*/
public void getAppConfigAsync(
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/config");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().get(qInfo._queryUrl, qInfo._headers, null);
executeRequestAsync(_request, callBack);
}
/**
* Get the Governance rules
* @throws Throwable on error getting app config
*/
public HttpResponse getGovernanceRules() throws Throwable {
QueryInfo qInfo = getQueryInfo("/v1/rules");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().get(qInfo._queryUrl, qInfo._headers, null);
//invoke the callback before request if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnBeforeRequest(_request);
}
// make the API call
HttpResponse _response = getClientInstance().executeAsString(_request);
// Wrap the request and the response in an HttpContext object
HttpContext _context = new HttpContext(_request, _response);
//invoke the callback after response if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnAfterResponse(_context);
}
//handle errors defined at the API level
validateResponse(_response, _context);
// Return headers to the client
return _response;
}
/**
* Get the Governance rules
* @param callBack Called after the HTTP response is received
* @throws JsonProcessingException on error getting app config
*/
public void getGovernanceRulesAsync(
final APICallBack callBack
) throws JsonProcessingException {
QueryInfo qInfo = getQueryInfo("/v1/rules");
//prepare and invoke the API call request to fetch the response
final HttpRequest _request = getClientInstance().get(qInfo._queryUrl, qInfo._headers, null);
executeRequestAsync(_request, callBack);
}
private static class QueryInfo {
String _queryUrl;
Map _headers;
public QueryInfo(String url, Map headers) {
_queryUrl = url; _headers = headers;
}
}
private QueryInfo getQueryInfo(String url) {
if (config.applicationId == null || config.applicationId.equals("")) {
throw new IllegalArgumentException("A Moesif Application Id is required. Please obtain it through your settings at www.moesif.com");
}
if (config.baseUri == null || config.baseUri.equals("")) {
throw new IllegalArgumentException("The API BaseUri is required.");
}
String _baseUri = config.baseUri;
//prepare query string for API call
StringBuilder _queryBuilder = new StringBuilder(_baseUri);
_queryBuilder.append(url);
//validate and preprocess url
String _queryUrl = APIHelper.cleanUrl(_queryBuilder);
//load all headers for the outgoing API request
Map _headers = new HashMap() {
{
put( "X-Moesif-Application-Id", config.applicationId);
}
};
return new QueryInfo(_queryUrl, _headers);
}
private Map executeRequest(final HttpRequest _request) throws Throwable {
HttpResponse _response = getClientInstance().executeAsString(_request);
Map headers = _response.getHeaders();
// Wrap the request and the response in an HttpContext object
HttpContext _context = new HttpContext(_request, _response);
//invoke the callback after response if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnAfterResponse(_context);
}
//handle errors defined at the API level
validateResponse(_response, _context);
checkAppConfigEtag(headers.get(APP_CONFIG_ETAG_HEADER));
checkRulesEtag(_response.getHeaders().get(RULES_ETAG_HEADER));
// Return headers to the client
return headers;
}
private void executeRequestAsync(final HttpRequest _request, final APICallBack callBack) {
executeRequestAsync(_request, callBack, false);
}
// Add the isBinary option to support binary async for gzip request
private void executeRequestAsync(final HttpRequest _request, final APICallBack callBack, boolean isBinary) {
//invoke the callback before request if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnBeforeRequest(_request);
}
//invoke request and get response
Runnable _responseTask = new Runnable() {
public void run() {
//make the API call
if(isBinary){
try {
getClientInstance().executeAsBinaryAsync(_request, createHttpResponseCallback(callBack), config.debug);
} catch (JsonProcessingException e) {
if(config.debug){
logger.warning( "[DEBUG] Error when Json parse objects: " + e.getMessage() + " | executeRequestAsync");
}
}
}
else {
getClientInstance().executeAsStringAsync(_request, createHttpResponseCallback(callBack));
}
}
};
//execute async using thread pool
APIHelper.getScheduler().execute(_responseTask);
}
private AppConfigModel getCachedAppConfig() {
trySyncAppConfig(true);
return appConfigModel;
}
private void checkAppConfigEtag(String newAppConfigEtag) {
if (newAppConfigEtag != null && !newAppConfigEtag.equals(appConfigEtag)) {
// only update the etag once we've gotten the new config
trySyncAppConfig(false);
}
}
private void checkRulesEtag(String newRulesEtag) {
if(newRulesEtag != null && !newRulesEtag.equals(rulesEtag)) {
getGovernanceRulesModel();
}
}
private void trySyncAppConfig(boolean timeCheck) {
long now = new Date().getTime();
if (timeCheck) {
boolean willFetch = lastAppConfigFetch + APP_CONFIG_DEBOUNCE < now;
if (willFetch) {
syncAppConfig(timeCheck);
}
}
else
{
syncAppConfig(timeCheck);
}
}
public List parseGovernanceRulesModel(InputStream jsonTxt) throws IOException {
return objectMapper.readValue(jsonTxt, new TypeReference>(){});
}
public AppConfigModel parseAppConfigModel(InputStream jsonTxt) throws IOException {
return objectMapper.readValue(jsonTxt, AppConfigModel.class);
}
private void syncAppConfig(Boolean etagCheck) {
if (shouldSyncAppConfig) {
APICallBack callback = new APICallBack() {
public void onSuccess(HttpContext context, HttpResponse response) {
if(etagCheck) {
String newAppConfigEtag = response.getHeaders().get(APP_CONFIG_ETAG_HEADER);
if (newAppConfigEtag != null && !newAppConfigEtag.equals(appConfigEtag)) {
// Read the response body
try {
InputStream respBodyIs = response.getRawBody();
appConfigModel = parseAppConfigModel(respBodyIs);
respBodyIs.close();
} catch (Exception e) {
logger.warning("Invalid AppConfig JSON: " + e.getMessage());
}
if (config.debug) {
logger.info("App Config Model returned is " + appConfigModel);
}
appConfigEtag = newAppConfigEtag;
lastAppConfigFetch = new Date().getTime();
}
}
else {
try {
InputStream respBodyIs = response.getRawBody();
appConfigModel = parseAppConfigModel(respBodyIs);
respBodyIs.close();
} catch (Exception e) {
logger.warning("Invalid AppConfig JSON: " + e.getMessage());
}
if (config.debug) {
logger.info("App Config Model returned is " + appConfigModel);
}
appConfigEtag = response.getHeaders().get(APP_CONFIG_ETAG_HEADER);
lastAppConfigFetch = new Date().getTime();
}
}
public void onFailure(HttpContext context, Throwable error) {
// fail silently
// try again later
}
};
try {
getAppConfigAsync(callback);
} catch (Exception e) {
logger.warning("Error performing async operation");
}
}
}
public AppConfigModel getDefaultAppConfig() {
return new AppConfigBuilder()
.sampleRate(100)
.build();
}
public int calculateWeight(int sampleRate) {
return (int)(sampleRate == 0 ? 1 : Math.floor(100.00 / sampleRate));
}
public EventModel buildEventModel(EventRequestModel eventRequestModel,
EventResponseModel eventResponseModel,
String userId,
String companyId,
String sessionToken,
String apiVersion,
Object metadata,
String direction) {
EventBuilder eb = new EventBuilder();
eb.request(eventRequestModel);
eb.response(eventResponseModel);
if (userId != null) {
eb.userId(userId);
}
if (companyId != null) {
eb.companyId(companyId);
}
if (sessionToken != null) {
eb.sessionToken(sessionToken);
}
if (apiVersion != null) {
eb.tags(apiVersion);
}
if (metadata != null) {
eb.metadata(metadata);
}
eb.direction(direction);
return eb.build();
}
@Deprecated
public boolean isBlockedByGovernanceRules(EventModel eventModel) {
return isBlockedByGovernanceRules(eventModel, this.governanceRules, getCachedAppConfig());
}
public BlockedByGovernanceRulesResponse getBlockedByGovernanceRulesResponse(EventModel eventModel) {
return getBlockedByGovernanceRulesResponse(eventModel, this.governanceRules, getCachedAppConfig());
}
public BlockedByGovernanceRulesResponse getBlockedByGovernanceRulesResponse(EventModel eventModel,List rules, AppConfigModel appConfig) {
if(rules.isEmpty()) {
return BlockedByGovernanceRulesResponse.NO_BLOCK;
}
Map regexMap = eventModel.getRegexMap();
List> users = getMatchingEntityRules(eventModel.getUserId(), "user", appConfig, rules);
List> rulesList = new ArrayList<>(users.stream().filter(tuple -> isRegexMatch(tuple.a, regexMap)).collect(Collectors.toList()));
List> companies = getMatchingEntityRules(eventModel.getCompanyId(), "company", appConfig, rules);
rulesList.addAll(companies.stream().filter(tuple -> isRegexMatch(tuple.a, regexMap)).collect(Collectors.toList()));
List> regexRules = rules.stream().filter(r -> r.getType().equals("regex")).map(gm -> new Tuple2(gm , null)).collect(Collectors.toList());
rulesList.addAll(regexRules.stream().filter(r -> isRegexMatch(r.a, regexMap)).collect(Collectors.toList()));
return getBlockedByGovernanceRulesResponse(rulesList);
}
@Deprecated
public boolean isBlockedByGovernanceRules(EventModel eventModel, List rules, AppConfigModel appConfig) {
BlockedByGovernanceRulesResponse response = getBlockedByGovernanceRulesResponse(eventModel, rules, appConfig);
if(response.isBlocked) {
eventModel.setBlockedBy(response.blockedBy);
eventModel.setResponse(response.response);
return true;
} else {
return false;
}
}
private List> getMatchingEntityRules(String entityId, String type, AppConfigModel appConfig, List rules) {
List> matching = new ArrayList<>();
Map> entityRules = type.equals("user") ? appConfig.getUserRules() : appConfig.getCompanyRules();
if(entityId == null) {
for (GovernanceRulesModel m : rules) {
if(m.getBlock() && m.isAppliedToUnidentified() && m.getType().equals(type)) {
matching.add(new Tuple2<>(m, null));
}
}
return matching;
}
if(entityRules.containsKey(entityId)) {
for (EntityRuleModel e : entityRules.get(entityId)) {
for (GovernanceRulesModel m : rules) {
if(m.getBlock() && m.getId().equals(e.getRules()) && m.getType().equals(type) && m.getAppliedTo().equals("matching")) {
matching.add(new Tuple2<>(m, e));
}
}
}
return matching;
} else {
for (GovernanceRulesModel m : rules) {
if( m.getBlock() && m.getAppliedTo().equals("not_matching") && m.getType().equals(type)) {
matching.add(new Tuple2<>(m, null));
}
}
return matching;
}
}
private boolean isRegexMatch(GovernanceRulesModel m, Map regexMap) {
List regexModels = m.getRegexConfig();
if(regexModels == null || regexModels.isEmpty())
return true;
for(GovernanceRuleRegexRuleModel r : regexModels) {
boolean match = false;
for(GovernanceRuleRegexConditionModel con: r.getConditions()) {
if(con.getPath().startsWith("request.body.") && regexMap.containsKey("request.body")) {
Map requestBody = (Map) regexMap.get("request.body");
String field = con.getPath().split("\\.")[2];
Object value = requestBody.get(field);
if(value != null) {
Pattern pattern = Pattern.compile(con.getValue(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(value.toString());
if (matcher.find()) {
match = true;
} else {
match = false;
break;
}
}
else {
match = false;
break;
}
}
else if (regexMap.containsKey(con.getPath())) {
Pattern pattern = Pattern.compile(con.getValue(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(regexMap.get(con.getPath()).toString());
if (matcher.find()) {
match = true;
} else {
match = false;
break;
}
}
else {
match = false;
break;
}
}
if (match) {
return true;
}
}
return false;
}
private BlockedByGovernanceRulesResponse getBlockedByGovernanceRulesResponse(List> rules) {
if(rules.isEmpty()) {
return BlockedByGovernanceRulesResponse.NO_BLOCK;
}
BlockedByGovernanceRulesResponse response = new BlockedByGovernanceRulesResponse();
Tuple2 rule = rules.get(0);
response.isBlocked = true;
response.blockedBy = rule.a.getId();
GovernanceRulesResponseModel r = rule.a.getResponse();
String body = updateMergeTagBody(rule);
Map headers = new HashMap<>();
Collections.reverse(rules);
for(Tuple2 r2 : rules) {
headers.putAll(updateMergeTagHeaders(r2));
}
EventResponseModel eventResponseModel = new EventResponseModel();
eventResponseModel.setTime(new Date());
eventResponseModel.setStatus(r.getStatus());
eventResponseModel.setHeaders(headers);
eventResponseModel.setBody(body);
eventResponseModel.setTransferEncoding("json");
response.response = eventResponseModel;
return response;
}
private String updateMergeTagBody(Tuple2 rule) {
try {
String body = APIHelper.serialize(rule.a.getResponse().getBody());
Map variables = rule.b == null ? new HashMap<>() : rule.b.getValues().entrySet().stream().collect(Collectors.toMap(
entry -> String.format("{{%s}}", entry.getKey()),
entry -> entry.getValue())
);
for (String key : variables.keySet()) {
body = body.replace(key, variables.get(key));
}
return body;
}catch (Exception e) {
return "";
}
}
private Map updateMergeTagHeaders(Tuple2 rule) {
Map headers = rule.a.getResponse().getHeaders();
if(headers == null) {
headers = new HashMap<>();
}
Map variables = rule.b == null? new HashMap<>() : rule.b.getValues().entrySet().stream().collect(Collectors.toMap(
entry -> String.format("{{%s}}", entry.getKey()),
entry -> entry.getValue())
);
for (String key : variables.keySet()) {
headers = headers.entrySet().stream().collect(Collectors.toMap(
entry -> entry.getKey(),
entry -> entry.getValue().replace(key, variables.get(key)))
);
}
return headers;
}
public boolean shouldSendSampledEvent(EventModel eventModel) {
int sampleRate = getSampleRateToUse(eventModel);
double randomPercentage = Math.random() * 100;
return sampleRate >= randomPercentage;
}
public int getSampleRateToUse(EventModel eventModel) {
return getSampleRateToUse(eventModel, getCachedAppConfig());
}
public int getSampleRateToUse(EventModel eventModel, AppConfigModel appConfigModel) {
List sampleRates = new ArrayList<>();
if (eventModel.getUserId() != null && appConfigModel.getUserSampleRate().containsKey(eventModel.getUserId())) {
sampleRates.add(appConfigModel.getUserSampleRate().get(eventModel.getUserId()));
} else if (eventModel.getCompanyId() != null && appConfigModel.getCompanySampleRate().containsKey(eventModel.getCompanyId())) {
sampleRates.add(appConfigModel.getCompanySampleRate().get(eventModel.getCompanyId()));
}
if(!appConfigModel.getRegex_config().isEmpty()) {
Map regexMap = eventModel.getRegexMap();
for (RegexConfigModel regexConfigModel : appConfigModel.getRegex_config()) {
boolean match = false;
for(GovernanceRuleRegexConditionModel con: regexConfigModel.conditions) {
if (regexMap.containsKey(con.getPath())) {
Pattern pattern = Pattern.compile(con.getValue(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(regexMap.get(con.getPath()).toString());
if (matcher.find()) {
match = true;
} else {
match = false;
}
}
}
if (match) {
sampleRates.add(regexConfigModel.sampeleRate);
break;
}
}
}
return sampleRates.isEmpty()? appConfigModel.getSampleRate() : Collections.min(sampleRates);
}
private APICallBack createHttpResponseCallback(final APICallBack callBack) {
return new APICallBack() {
public void onSuccess(HttpContext _context, HttpResponse _response) {
try {
//invoke the callback after response if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnAfterResponse(_context);
}
//handle errors defined at the API level
validateResponse(_response, _context);
checkAppConfigEtag(_response.getHeaders().get(APP_CONFIG_ETAG_HEADER));
checkRulesEtag(_response.getHeaders().get(RULES_ETAG_HEADER));
//let the caller know of the success
callBack.onSuccess(_context, _response);
} catch (APIException error) {
//let the caller know of the error
callBack.onFailure(_context, error);
} catch (Exception exception) {
//let the caller know of the caught Exception
callBack.onFailure(_context, exception);
}
}
public void onFailure(HttpContext _context, Throwable _error) {
//invoke the callback after response if its not null
if (getHttpCallBack() != null)
{
getHttpCallBack().OnAfterResponse(_context);
}
//let the caller know of the failure
callBack.onFailure(_context, _error);
}
};
}
static final class Tuple2 {
final A a;
final B b;
public Tuple2(A a, B b) {
this.a = a;
this.b = b;
}
}
}