I recently noticed that I'm getting about 22 times the requests as I should be getting on the Google API console which let to some unexpected charges. Below is the code I am using to get details about hospitals near an area. I need the name and street address.
GeoApiContext context = new GeoApiContext.Builder().apiKey("MYAPIKEY")
.build();
NearbySearchRequest req = PlacesApi.nearbySearchQuery(context,
new com.google.maps.model.LatLng(Double.valueOf(lat), Double.valueOf(lon)));
PlacesSearchResponse resp = req.keyword("hospital").type(PlaceType.HOSPITAL).radius(42000).await();
if (resp.results != null && resp.results.length > 0) {
for (PlacesSearchResult r : resp.results) {
try {
PlaceDetails placeDetails = PlacesApi.placeDetails(context, r.placeId).await();
AddressComponent[] address = placeDetails.addressComponents;
} catch (com.google.maps.errors.NotFoundException nfe) {
}
}
}
What is causing this to register about 22 times more requests?
Related
I am working for a mobile application and using Spring Boot as a backend and building REST APIs. So now, I need to implement like and dislike feature. That is if a user hits like (thumbs up icon) then count should be increased and that count should be displayed there and if hits dislike (thumbs down icon) then like count should be decreased and count should be displayed then also. But what I wrote for like and dislike is not as expected as it should be.
Actual working should be as follows:
1. If user A hits like to post A, then count of post A should be increased to 1.
2. If user B also hits like to post A then count of post A should be increased from 1 to 2. (means total like count of Post A is 2 now)
3. If user A again hits like button then it will be counted as dislike and count should get decreased from 2 to 1. (Now current count should be 1 again for post A).
So the like-dislike feature should work this way. But my code is not working as expected.
Below is my REST controller:
#RequestMapping(value = RestApiUrl.likepost, method = {RequestMethod.POST, RequestMethod.GET }, produces = {MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> likepost(HttpServletRequest request, #RequestParam("userid") String userid, #RequestParam("postid") String smsid) {
CustomResponse = ResponseFactory.getResponse(request);
int lcount = 0, dcount = 0, likcount = 0, postlike = 0;
FavouritePost favPost = null;
Post messageid = null, newpost = null;
try {
Long postid = Long.parseLong(smsid);
Long uid = Long.parseLong(userid);
User userbyid = userDao.findByUserid(uid);
messageid = postDao.findByPostid(postid);
postlike = messageid.getLike_count();
favPost = favouritePostDao.findByPostidAndUserid(messageid, userbyid);
if(favPost == null) {
favPost = new FavouritePost();
likcount = favPost.getLikecount();
lcount = likcount + 1;
favPost.setUserid(userbyid);
favPost.setCreatedby(userbyid);
favPost.setPostid(messageid);
favPost.setLikecount(lcount);
favouritePostDao.save(favPost);
messageid.setModifiedby(userbyid);
messageid.setModifieddate(new Date());
messageid.setLike_count(postlike+1);
newpost = postDao.save(messageid);
CustomResponse.setResponse(newpost);
CustomResponse.setStatus(CustomStatus.OK);
CustomResponse.setStatusCode(CustomStatus.OK_CODE);
CustomResponse.setResponseMessage(CustomStatus.SuccessMsg);
}else {
dcount = favPost.getDislikecount();
favPost.setUserid(userbyid);
favPost.setModifiedby(userbyid);
favPost.setModifieddate(new Date());;
favPost.setPostid(messageid);
favPost.setDislikecount(dcount+1);;
favouritePostDao.save(favPost);
messageid.setModifiedby(userbyid);
messageid.setModifieddate(new Date());
if(postlike > 0) {
int count =postlike-1;
messageid.setLike_count(count);
}else {
int count =postlike+1;
messageid.setLike_count(count);
}
newpost = postDao.save(messageid);
CustomResponse.setResponse(newpost);
CustomResponse.setStatus(CustomStatus.OK);
CustomResponse.setStatusCode(CustomStatus.OK_CODE);
CustomResponse.setResponseMessage(CustomStatus.SuccessMsg);
}
} catch (Exception e) {
e.printStackTrace();
CustomResponse.setResponse("Error occurred! please try again");
CustomResponse.setStatus(CustomStatus.Error);
CustomResponse.setStatusCode(CustomStatus.Error_CODE);
CustomResponse.setResponseMessage(CustomStatus.ErrorMsg);
}
return new ResponseEntity<ResponseDao>(CustomResponse, HttpStatus.OK);
}
Please help me in the implementation of like-dislike feature for a mobile application. A concept/idea would be great of how to do this.
I am hoping there is someone here who is familiar with a Real Estate data standard known as RETS. The National Association of Realtors provides a dll for interfacing with their services called libRETS, but it is not being supported like it once was and recent events have prompted us to create our own as a replacement. For logistics reasons, we can't do this in Core and are using the current C#.Net 4.7.2.
There are 2 or 3 different "security levels" for connecting to a RETS Server, with the method being a per case basis from one MLS to the next. We can successfully connect to those who only require a login and password, but are hitting a wall on those who also require what is called a UserAgent and UserAgentPassword, which must passed somehow using Md5 encryption. The server is returning:
The remote server returned an error: (401) Unauthorized.
private WebResponse GetLoginBasicResponse()//*** THIS ONE WORKS ***
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var request = (HttpWebRequest)WebRequest.Create(new Uri(_cred.loginUri));
request.Method = "GET";
request.Headers.Add("RETS-Version", _retsVersion);
request.Credentials = new NetworkCredential(_login, _password);
return request.GetResponse();
}
catch (Exception ex)
{
string ignore = ex.Message;
return null;
}
}
private WebResponse GetLoginWithUserAgentResponse()//*** THIS ONE DOES NOT WORK ***
{
try
{
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var request = (HttpWebRequest)WebRequest.Create(new Uri(_cred.loginUri));
request.Method = "GET";
request.Headers.Add("RETS-Version", _retsVersion);
if (!string.IsNullOrEmpty(_cred.userAgent))
{
request.UserAgent = Md5(_cred.userAgent + ":" + _cred.userAgentPassword);
//request.Headers.Add("RETS-UA-Authorization", "Digest " + Md5(_cred.userAgent + ":" + _cred.userAgentPassword));
}
request.Credentials = new NetworkCredential(_login, _password);
return request.GetResponse();
}
catch (Exception ex)
{
string ignore = ex.Message;
return null;
}
}
public string Md5(string input) //*** Borrowed this from from .NET Core Project and presume it works
{
// Use input string to calculate MD5 hash
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
byte[] inputBytes = Encoding.ASCII.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
// Convert the byte array to hexadecimal string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
sb.Append(hashBytes[i].ToString("X2"));
}
return sb.ToString();
}
}
Page 20 of this document describes how to build the UA header: https://www.ranww.org/documents/resources/rets_1_8.pdf
There’s a few other fields you need to include.
We were not able to solve the issue in .NET but found a .NET Core project in GitHub that we are using instead. https://github.com/CrestApps/RetsConnector
This case can be closed
Not seeing an option to "Mark as Answer". Have tried both MS Edge and Google Chrome
I've created a method to add members in a Batch Request to a google group using .NET core and google's .NET client library. The code looks like this:
private void InitializeGSuiteDirectoryService()
{
_directoryServiceCredential = GoogleCredential
.FromJson(GlobalSettings.Instance.GSuiteSettings.Credentials)
.CreateScoped(_scopes)
.CreateWithUser(GlobalSettings.Instance.GSuiteSettings.User);
_directoryService = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = _directoryServiceCredential,
ApplicationName = _applicationName
});
}
public OperationResult<int> AddGroupMembers(Group group, IEnumerable<Member> members)
{
var result = new OperationResult<int>();
var memberList = members.ToList();
var batchRequestCount = 0;
if (memberList.Any())
{
var request = new BatchRequest(_directoryService);
foreach (var member in memberList)
{
batchRequestCount++;
request.Queue<Members>(_directoryService.Members.Insert(member, group.Id), (content, error, i, message) =>
{
if (message.IsSuccessStatusCode)
{
//log OK
}
else
{
// Implement Exponential backoff only on the request that failed.
}
});
if (batchRequestCount == 30|| member.Equals(memberList.Last()))
{
request.ExecuteAsync().Wait();
request = new BatchRequest(_directoryService); //Clear queue
}
}
}
return result;
}
The logic works fine if the amount of members is small; however, when the members count is let's say 100( this is the max amount of users in my google's test instance), I get an Error from Google that reads: "quotaExceeded". According to Google's documentation, the limit for a batch request on their Admin SDK is 1000 and I've set my logic to Execute when we reach a limit of 30.
The QUESTION is: How do I implement error handling to retry whenever I get this error? Their documentation suggests implementing 'Exponential Backoff' with a response that contains a 'retry-able error'(I don't see this when I inspect my response).
So here's what I ended up doing to implement Exponential Backoff on my call to add members to a Gsuite group. Since I'm using dotnet core, I was able to use 'Polly', which is a resilience and transient-fault-handling library that offers this functionality out of the box. There may be some need for refactoring, but here's what the code looks like for now:
public OperationResult<int> AddGroupMembers(Group group, IEnumerable<Member> members)
{
var result = new OperationResult<int>();
var memberList = members.ToList();
var batchRequestCount = 0;
if (memberList.Any())
{
var request = new BatchRequest(_directoryService);
foreach (var member in memberList)
{
retryRequest = false; // This variable needs to be declared at the class level to guarantee the value is available to the original thread running the process.
batchRequestCount++;
request.Queue<Members>(_directoryService.Members.Insert(member, group.Id), (content, error, i, message) =>
{
// If error code is 'quotaExceeded' retry the request ( You can add as many error codes as you'd like to retry here)
if (error.Code == 403)
{
retryRequest = true;
}
});
// Execute batch request to add members in batches of 30 member max
if (batchRequestCount == 30|| member.Equals(memberList.Last()))
{
// Below is what the code to retry using polly looks like
var response = Policy
.HandleResult<HttpResponseMessage>(message => message.StatusCode == HttpStatusCode.Conflict)
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(4)
}, (results, timeSpan, retryCount, context) =>
{
// Log Warn saying a retry was required.
})
.Execute(() =>
{
var httpResponseMsg = new HttpResponseMessage();
// Execute batch request Synchronously
request.ExecuteAsync().Wait();
if (retryRequest)
{
httpResponseMsg.StatusCode = HttpStatusCode.Conflict;
retryRequest = false;
}
else
{
httpResponseMsg.StatusCode = HttpStatusCode.OK;
}
return httpResponseMsg;
});
if (response.IsSuccessStatusCode)
{
// Log info
}
else
{
// Log warn
}
requestCount = 0;
request = new BatchRequest(_directoryService);
batchCompletedCount++;
}
}
}
return result;
}
Using the YouTube Data API, I can find a channel with the "search" API:
https://www.googleapis.com/youtube/v3/search?part=snippet&q=Alexander+condrashov&type=channel&key=YOUR_API_KEY
This gives me one object in my items array.
But this is a "search" API, not a "channel" API.
I think that the correct way to find a channel is to use "Channels: list" API.
Trying to find channel by username gives me zero objects in my items array:
https://www.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&forUsername=Alexander+kondrashov&key=YOUR_API_KEY
Has anyone succeeded in using "Channels: list" with "forUsername" to find a channel?
I know this is old and you may have already figured something out, but unfortunately YouTube's Channels API isn't great for finding channels reliably for anything other than the channel's ID. As a workaround, I combine the two approaches to get a realiable result (though it takes two queries and costs more quota which is lame sauce).
For example (in C# using the Google SDKs):
public async Task<Channel> GetChannelByUsername(string username)
{
try
{
var channel = default(Channel);
var searchRequest = this.youtubeService.Search.List("snippet");
searchRequest.Q = username;
searchRequest.Type = "channel";
var searchResults = await searchRequest.ExecuteAsync();
if (searchResults != null && searchResults.Items.Count > 0)
{
var searchResult = searchResults.Items.FirstOrDefault(i => i.Snippet.ChannelTitle.ToLower() == username);
if (searchResult != null && searchResult.Snippet != null)
{
var channelSearchResult = searchResult.Snippet;
var channelListRequest = this.youtubeService.Channels.List("snippet");
channelListRequest.Id = channelSearchResult.ChannelId;
var channelListResult = await channelListRequest.ExecuteAsync();
if(channelListResult != null)
{
channel = channelListResult.Items.FirstOrDefault();
}
}
}
return channel;
}
catch (Exception ex)
{
// Handle Exception
}
}
I am seeing errors while exporting email in office 365 account using ews managed api, "The server cannot service this request right now. Try again later." Why is that error occurring and what can be done about it?
I am using the following code for that work:-
_GetEmail = (EmailMessage)item;
bool isread = _GetEmail.IsRead;
sub = _GetEmail.Subject;
fold = folder.DisplayName;
historicalDate = _GetEmail.DateTimeSent.Subtract(folder.Service.TimeZone.GetUtcOffset(_GetEmail.DateTimeSent));
props = new PropertySet(EmailMessageSchema.MimeContent);
var email = EmailMessage.Bind(_source, item.Id, props);
bytes = new byte[email.MimeContent.Content.Length];
fs = new MemoryStream(bytes, 0, email.MimeContent.Content.Length, true);
fs.Write(email.MimeContent.Content, 0, email.MimeContent.Content.Length);
Demail = new EmailMessage(_destination);
Demail.MimeContent = new MimeContent("UTF-8", bytes);
// 'SetExtendedProperty' used to maintain historical date of items
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(57, MapiPropertyType.SystemTime), historicalDate);
// PR_MESSAGE_DELIVERY_TIME
Demail.SetExtendedProperty(new ExtendedPropertyDefinition(3590, MapiPropertyType.SystemTime), historicalDate);
if (isread == false)
{
Demail.IsRead = isread;
}
if (_source.RequestedServerVersion == flagVersion && _destination.RequestedServerVersion == flagVersion)
{
Demail.Flag = _GetEmail.Flag;
}
_lstdestmail.Add(Demail);
_objtask = new TaskStatu();
_objtask.TaskId = _taskid;
_objtask.SubTaskId = subtaskid;
_objtask.FolderId = Convert.ToInt64(folderId);
_objtask.SourceItemId = Convert.ToString(_GetEmail.InternetMessageId.ToString());
_objtask.DestinationEmail = Convert.ToString(_fromEmail);
_objtask.CreatedOn = DateTime.UtcNow;
_objtask.IsSubFolder = false;
_objtask.FolderName = fold;
_objdbcontext.TaskStatus.Add(_objtask);
try
{
if (counter == countGroup)
{
Demails = new EmailMessage(_destination);
Demails.Service.CreateItems(_lstdestmail, _destinationFolder.Id, MessageDisposition.SaveOnly, SendInvitationsMode.SendToNone);
_objdbcontext.SaveChanges();
counter = 0;
_lstdestmail.Clear();
}
}
catch (Exception ex)
{
ClouldErrorLog.CreateError(_taskid, subtaskid, ex.Message + GetLineNumber(ex, _taskid, subtaskid), CreateInnerException(sub, fold, historicalDate));
counter = 0;
_lstdestmail.Clear();
continue;
}
This error occurs only if try to export in office 365 accounts and works fine in case of outlook 2010, 2013, 2016 etc..
Usually this is the case when exceed the EWS throttling in Exchange. It is explain in here.
Make sure you already knew throttling policies and your code comply with them.
You can find throttling policies using Get-ThrottlingPolicy if you have the server.
One way to solve the throttling issue you are experiencing is to implement paging instead of requesting all items in one go. You can refer to this link.
For instance:
using Microsoft.Exchange.WebServices.Data;
static void PageSearchItems(ExchangeService service, WellKnownFolderName folder)
{
int pageSize = 5;
int offset = 0;
// Request one more item than your actual pageSize.
// This will be used to detect a change to the result
// set while paging.
ItemView view = new ItemView(pageSize + 1, offset);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.Traversal = ItemTraversal.Shallow;
bool moreItems = true;
ItemId anchorId = null;
while (moreItems)
{
try
{
FindItemsResults<Item> results = service.FindItems(folder, view);
moreItems = results.MoreAvailable;
if (moreItems && anchorId != null)
{
// Check the first result to make sure it matches
// the last result (anchor) from the previous page.
// If it doesn't, that means that something was added
// or deleted since you started the search.
if (results.Items.First<Item>().Id != anchorId)
{
Console.WriteLine("The collection has changed while paging. Some results may be missed.");
}
}
if (moreItems)
view.Offset += pageSize;
anchorId = results.Items.Last<Item>().Id;
// Because you’re including an additional item on the end of your results
// as an anchor, you don't want to display it.
// Set the number to loop as the smaller value between
// the number of items in the collection and the page size.
int displayCount = results.Items.Count > pageSize ? pageSize : results.Items.Count;
for (int i = 0; i < displayCount; i++)
{
Item item = results.Items[i];
Console.WriteLine("Subject: {0}", item.Subject);
Console.WriteLine("Id: {0}\n", item.Id.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine("Exception while paging results: {0}", ex.Message);
}
}
}