Gmail REST API Thread Search not Giving Expected Results - google-app-engine

We have built an Email Audit Application for one of our customers. The app utilizes the Gmail REST API and provides a front-end interface that allows users with permission to run audit queries based on a selected date-ranges. The date-range that is provided by the user is utilized in the Email Thread search query.
We have noticed, however, that the API is showing a discrepancy between the threads that are returned and the actual items that are in the inbox of an audited user. For example, in order to collect everything within 1 day, say 4/28, we need to expand the audit range from 4/27-4/29.
The documentation for the Gmail REST API provides no explanation nor highlighting of this behavior. Is this an issue with the API or are there additional parameters that perhaps can specify the time-zone for which we can search for these email threads?
Below you will find a snippet of code that is utilized to grab such email threads:
def GrabAllThreadIDs(user_email, after_date, before_date):
query = "in:inbox " + "after:" + after_date + " " + "before:" + before_date
# Create the Gmail Service
gmail_service = create_gmail_service(user_email)
raw_thread_response = ListThreadsMatchingQuery(gmail_service, 'me', query)
for item in raw_thread_response:
all_ids.append(item['id'])
return all_ids
======================================================
def ListThreadsMatchingQuery(service, user_id, query=''):
"""List all Threads of the user's mailbox matching the query.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
query: String used to filter messages returned.
Eg.- 'label:UNREAD' for unread messages only.
Returns:
List of threads that match the criteria of the query. Note that the returned
list contains Thread IDs, you must use get with the appropriate
ID to get the details for a Thread.
"""
try:
response = service.users().threads().list(userId=user_id, q=query).execute()
threads = []
if 'threads' in response:
threads.extend(response['threads'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().threads().list(userId=user_id, q=query,
pageToken=page_token).execute()
threads.extend(response['threads'])
return threads
except errors.HttpError, error:
print 'An error occurred: %s' % error
======================================================

That is how the Advanced search is designed. after gives messages sent after 12:00 AM (or 00:00), and before gives messages before the given date. Asking for after:2015/04/28 and before:2015/04/28 would result in a non-existent timespan.
I like to use the alternate form after:<TIME_IN_SECONDS_SINCE_THE_EPOCH>. If you would like to get all the messages received on 2015/04/28 you would write after:1430172000 before:1430258399 (2015/04/28 00:00 to 2015/04/28 23:59:59)

Related

How to query more than 200 field; Salesforce SOQL Fields

I am facing an issue using Salesforce API. While querying I am getting the following exception: "The SOQL FIELDS function must have a LIMIT of at most 200". Now, I understand SF expects a max of 200 only. So, I wanted to ask how can I query when the results are more than 200?
I can only use REST API to query, but if there is another option, then please let me know and I will try to add it in my code.
Thanks in Advance
You could chunk it, SELECT FIELDS(ALL) FROM Account ORDER BY Id LIMIT 200. Read the id of last record and in next query add WHERE Id> '001...'. but that's not very effective.
Look into "describe" calls, waste 1 call to learn names of all fields you need and explicitly list them in the query instead of relying on FIELDS(ALL). You can compose SOQL up to 20k characters long and with "bulk API" queries you could fetch up to 10k records in each API call so "investing" 1 call for describes would quickly pay off.
You could even cache the describe's result in your application and fetch fresh only if something interesting changed, there's rest API header for that: https://developer.salesforce.com/docs/atlas.en-us.232.0.api_rest.meta/api_rest/sobject_describe_with_ifmodified_header.htm
Try this it is Helpful:
// Get the Map of Schema of Account SObject
Map<String, Schema.SObjectField> fieldMap = Account.sObjectType.getDescribe().fields.getMap();
// Get all of the fields on the object
Set<String> setFieldNames = fieldMap.keySet();
list<String> lstFieldNames = new List<String>(setFieldNames);
// Dynamic Query String.
List<Account> lstAccounts = Database.query('SELECT ' + String.join(lstFieldNames, ',') + ' FROM Account');
system.debug('lstAccounts'+lstAccounts);

Is there a workaround for retrieving data for more than 1000 messages using SendGrid Filter All Messages?

I have a requirement of retrieving the activity for certain category of emails sent through our SendGrid account with events occurring before specific time.
For this, I first invoke the filter all messages endpoint of SendGrid to retrieve message IDs of the specific category of messages I am interested in bounding above using last_event_time parameter of the endpoint followed by retrieving activity for individual messages using the Filter message by message ID endpoint with the retrieved message ids.
The problem that I am faced with is that the filter all messages has a limit parameter with maximum value of 1000 while the number of messages for which the last_event_time is equal to a specific timestamp say '2021-10-10T10:10:10Z' can be more than 1000.
In this case, the use of timestamp to iteratively filter messages doesn't work as the response from filter all messages contains data for the same set of 1000 messages. I though of using the message ID's from the data retrieved through filter all messages to exclude data for those in subsequent calls and even tried that out but it errored out because the request uri was too long.
Not sure if I am missing something here.
You can paginate through the responses by adding limit and offset parameters to your request URL.
For example:
https://api.sendgrid.com/v3/resource?limit=1000&offset=1000

Azure AD SCIM: SystemForCrossDomainIdentityManagementMultipleEntriesInResponse

We're using Azure AD as the Identity Provider for User Provisioning into our system.
We have started getting this error of late.
EntrySynchronizationError
Result Failure
Description Failed to match an entry in the source and target systems User 'XXX#XXX.com'
ErrorCode SystemForCrossDomainIdentityManagementMultipleEntriesInResponse
There has been no change in our scim server code. The error message is obviously stating it's fetching more than 1 entry when it should return 1 but in reality, there is no user with the said username & Azure AD should be sending a request to create a new one.
This is happening under the action "Other", I'm guessing it's a GET.
Any idea on what's going wrong here?
A GET operation with a filter (ie: GET /Users?filter=userName eq "Test_User_dfeef4c5-5681-4387-b016-bdf221e82081") is expecting either 0 or 1 result to be returned, but is receiving more than one result. Either your configuration in provisioning is matching on an attribute that is not uniqueness constrained (ie: department eq "Sales") or there's a problem with your logic for returning filtered results.

How to make sure that email is not sent twice with task queue usage?

I would like to change my GAE app logic and to start emails sending with task queue usage.
Currently I have a cron job, which runs each 15 minutes and read messages to be sent from the datastore:
class SendMessagesHandler(webapp2.RequestHandler):
def get(self):
emails_quota_exceeded = models.get_system_value('emails_quota_exceeded')
if emails_quota_exceeded == 0 or emails_quota_exceeded == None:
messages = models.get_emails_queue()
for message in messages:
try:
...
email.send()
models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent
except apiproxy_errors.OverQuotaError, error_message:
models.set_system_value(what='emails_quota_exceeded', val=1)
logging.warning('E-mails quota exceeded for today: %s' % error_message)
break
else:
logging.info('Free quota to send e-mails is exceeded')
If I use task queues, then I'll get something like:
for message in messages:
taskqueue.add(url='/sendmsg', payload=message)
In this scenario it is possible that the same message will be sent twice (or even more times) - for ex., if it wasn't sent yet, but cron job was executed second time.
If I update email status immediately after adding the message to the queue:
for message in messages:
taskqueue.add(url='/sendmsg', payload=message)
models.update_email_status(message.key.id()) # update email status indicating that the mail has been sent
then it is possible that the message will never be sent. For ex., if exception happened during e-mail sending. Understand that the task will be retried, but in case quota is exceeded for today, then retries will not help.
I think I can also re-read the status of each message at task queue before trying to sent it, but it will cost me additional read operations.
What's the best way to handle it?
Giving your task a name including the key.id() will prevent it from being sent twice:
task_name = ''.join(['myemail-', str(mykey)])
try:
taskqueue.Task(
url="/someURL/send-single-email",
name=task_name,
method="POST",
params={
"subject": subject,
"body": body,
"to": to,
"from": from }
).add(queue_name="mail-queue")
except:
pass #throws TombstonedTaskError(InvalidTaskError) if tombstoned name used.
There may be times when you want to send follow-up emails for messages with the same key. Therefore, I would recommend adding a date or datetime stamp to the task name. This will allow you to send other messages of the same key at a later time:
task_name = ''.join(['myemail-', str(mykey), str(datetime.utcnow()-timedelta(hours=8))]).translate(string.maketrans('.:_ ', '----'))

Is it possible to check if a User is Locked Out?

Using the Salesforce Web Services API is it possible to check (or query) if a User is Locked Out (if they have attempted to log in unsuccessfully too many times and are therefore blocked from logging in)?
Although there is no specific field on the User object to indicate that they are locked out, you can query the LoginHistory object.
select Id, UserId, LoginTime, Status from LoginHistory where
UserId = 'xxxxxxxxx' order by LoginTime desc limit 20
Then loop through the results, checking the value of the Status field. If the user has been locked out, the most recent login attempts will have a value of "Password Lockout" in this field.
Other possible values of this Status field include:
Success
User is Inactive
Invalid Password
Failed: API security token required
Failed: Computer activation pending
Failed: Computer activation required
Failed: Invalid Timestamp
Failed: Mobile License Required
Nevermind; I found the answer.
It says in the documentation:
The password lockout status and the ability to reset the User locked-out status is not available via the API. You must check and reset the User password lockout status using the user interface.
For admin users - it's now possible to unlock users on iphone / ipad via the SalesforceA mobile app. https://itunes.apple.com/au/app/salesforcea/id731117958?mt=8
In Apex, I can check the IsPasswordLocked field on UserLogin object to check if a User is locked out or not by using the following SOQL -:
[SELECT IsPasswordLocked FROM UserLogin
WHERE UserId = 'ENTER YOUR USER ID HERE'];

Resources