Item id's and attachment id's that change - exchangewebservices

I've been struggling for months now with a random error in the ms graph api. Finally I found some sence in the whole mess, but I think there's an error in the graph api.
I've developed a multitenant outlook-addin (JS), that my clients use to store their emails and/or the attachments into a storage. I use the Office.context.mailbox.item object to fetch both the ItemId of the message and the Id's of the attachments. I send those id's off to a sevrer to do the heavy liftning of retriving the email and/or attachments, using EWS. Because the client has a choice of selecting which attachments to save, I need to send the selected attachment Id's to the server, where I retrieve the email and the attachments. However when I try to match the attachment Id's with the selected from the Outlook app they're different, and I can't find the correct attachments. This happens with random messages, with random tenants.
From what I can see from the id's for the email and the attachments, it looks like the attachment id is prefixed with the email id. Sometimes they don't match. Example:
The user select an email in the Outlook desktop client (Mac or Windows) and get this id for the email:
AAMkADYxNWNjMTRiLWFjYTYtNDM1OS04MTNjLThiNzM1ZWM0ZDFmZgBGAAAAAADbXpqFYshcSYLgLaL8DcdZBwDVFKe56AEMQo4qySw0u6tyAAAfj8mWAADVFKe56AEMQo4qySw0u6tyAABBmpnFAAA=
The attachment has this id (which DOESN'T match the email id):
AAMkADYxNWNjMTRiLWFjYTYtNDM1OS04MTNjLThiNzM1ZWM0ZDFmZgBGAAAAAADbXpqFYshcSYLgLaL8DcdZBwDVFKe56AEMQo4qySw0u6tyAAAAAAEJAADVFKe56AEMQo4qySw0u6tyAABBmmK4AAABEgAQAOBOPI4JZ71CuMzlk7nqfZw=
but when I query the EWS using the email id (I'm aware of the REST/EWS id differences), I get this id for the attachment (which DO match the email id):
AAMkADYxNWNjMTRiLWFjYTYtNDM1OS04MTNjLThiNzM1ZWM0ZDFmZgBGAAAAAADbXpqFYshcSYLgLaL8DcdZBwDVFKe56AEMQo4qySw0u6tyAAAfj8mWAADVFKe56AEMQo4qySw0u6tyAABBmpnFAAABEgAQAOBOPI4JZ71CuMzlk7nqfZw=
The funny thing is, that it works from OWA - this attahcment id is correct. This specific email has been moved, and I know that when moving files, the id changes, but shouldn't the attachment id then follow along? In EWS the id seems to have changed but not in Outlook

I used my Id-Whispering skills to look at your ids.
The first one is the id of the item itself, not an attachment on the item. The actual PR_ENTRYID for this id is AAAAANtemoViyFxJguAtovwNx1kHANUUp7noAQxCjirJLDS7q3IAAB+PyZYAANUUp7noAQxCjirJLDS7q3IAAEGamcUAAA==
The second one is an attachment, but the parent item is different. The PR_ENTRYID for this id is
AAAAANtemoViyFxJguAtovwNx1kHANUUp7noAQxCjirJLDS7q3IAAAAAAQkAANUUp7noAQxCjirJLDS7q3IAAEGaYrgAAA==
Attachment id part: EADgTjyOCWe9QrjM5ZO56n2c
The third one is also an attachment, same "attachment id" part of the id, but in this case, it refers to the item in the original location. As you can see, the PR_ENTRYID matches:
AAAAANtemoViyFxJguAtovwNx1kHANUUp7noAQxCjirJLDS7q3IAAB+PyZYAANUUp7noAQxCjirJLDS7q3IAAEGamcUAAA==
Attachment id part: EADgTjyOCWe9QrjM5ZO56n2c
So what happened here? What it looks like is that in the second case, the item was in a different folder. PR_ENTRYID made up of the FID (folder id) and the MID (message id). As a result, if an item moves from one folder to another, then the id changes. You move it back, it reverts back to its old id. Weird, I know.
That's a good reason for Microsoft to come up with an immutable id :)

Related

ContactsApp.getContact() for contact with multiple emails returns a different contact for each email address

My company allows employees to have multiple email addresses in their account.
I am attempting to write an input form where someone can enter any of the emails for a contact and we'll resolve it to the same person.
I've tried doing this via ContactsApp.getContact(email).getPrimaryEmail() to resolve all different inputs to the same primary email, but it doesn't work as expected. Each email I search for returns a different Contact object with only a single email (the one I used to search).
Even if I use ContactsApp.getContact(email).getEmails() to list all emails to the employee, I can see it only returns one at a time.
When going to contacts.google.com, I see the information I expected. Searching for any of the emails will return the same contact, with the same primary email and all other emails listed.
Is there something I'm doing wrong? Or is this how ContactsApp works. If so, is there a workaround?
Thanks!
getPrimaryEmail() will only return an email address if the contact in question has had a default email set and this can only be done via the Google Contacts App, not on the web (go figure).
To solve your problem I would suggest loading all the contacts into an array variable first and then searching this for matching email addresses. I would do the data retrieval on page/view load (so it can be reused without multiple server calls) but have included it all together here for conciseness.
What I found strange about the ContactsApp (and presumable People API too) is that the contacts returned are just empty objects (when logged) with just a bunch of methods on them. However, once you have an array of objects you can write your own properties to those objects for easier access of data.
Therefore I would first of all retrieve all the users contacts, then add a property of .emails to each contact object in the array and then use .some, perhaps, to check if a match appears in .emails (which will also be an array).
Something like:
let strSearch = 'someone#somewhere.net' // EMAIL address to search based on user input
let arrContacts = ContactsApp.getContacts();
let contacts = arrContacts.map(contact => {
let emails = contact.getEmails();
contact.emails = emails.map(email => email.getAddress());
return contact;
});
let foundContact = contacts.filter(contact => contact.emails.some(email => email === strSearch));
Remember .getEmails() returns another array of objects that have the method .getAddress() on them in order to retrieve the actual address, that's why I'm getting an array of email objects with let emails = contact.getEmails(); then using emails.map here to put the actual email addresses into an array stored in the contact.emails property (that doesn't exist so we're actually creating it here).
After that we're filtering the contacts array down to a contact where the searched email address is matched to one of the email addresses in the contact.emails array. I haven't tested it so the last line might need playing around with slightly but I've used something very similar so would expect it to work.
You can then use foundContact[0] to reference the contact found and use the further methods of .getFullname() .getId() etc. to retrieve their data as required. If you need the contacts phone numbers or geographical address that's a whole other process of returning an array of objects using .getPhones() or getAddresses(), but I think that's beyond the scope of this question.

Mass Upload Files To Specific Contacts Salesforce

I need to upload some 2000 documents to specific users in salesforce. I have a csv file that has the Salesforce-assigned ContactID, as well as a direct path to the files on my desktop. Each contact's specific file url has been included in the csv. How can I upload them all at one and, especially, to the correct contact?
You indicated in the comments / chat that you want it as "Files".
The "Files" object is bit more complex than Attachments, you'll need to do it in 2-3 steps. What you see as a File (you might see it referred to in documentation as Chatter Files or Salesforce Content) is actually several tables. There's
ContentDocument which can be kind of a file header (title, description, language, tags, linkage to many other areas in SF - because it can be standalone, it can be uploaded to certain SF Content Library, it can be linked to Accounts, Contacts, $_GOD knows what else)
ContentVersion which is well, actual payload. Only most recent version is displayed out of the box but if you really want you can go back in time
and more
The crap part is that you can't insert ContentDocument directly (there's no create() call in the list of operations) .
Theory
So you'll need:
Insert ContentVersion (v1 will automatically create for you parent ContentDocuments... it does sound bit ass-backwards but it works). After this is done you'll have a bunch of standalone documents loaded but not linked to any Contacts
Learn the Ids of their parent ContentDocuments
Insert ContentDocumentLink records that will connect Contacts and their PDFs
Practice
This is my C:\stacktest folder. It contains some SF cheat sheet PDFs.
Here's my file for part 1 of the load
Title PathOnClient VersionData
"Lightning Components CheatSheet" "C:\stacktest\SF_LightningComponents_cheatsheet_web.pdf" "C:\stacktest\SF_LightningComponents_cheatsheet_web.pdf"
"Process Automation CheatSheet" "C:\stacktest\SF_Process_Automation_cheatsheet_web.pdf" "C:\stacktest\SF_Process_Automation_cheatsheet_web.pdf"
"Admin CheatSheet" "C:\stacktest\SF_S1-Admin_cheatsheet_web.pdf" "C:\stacktest\SF_S1-Admin_cheatsheet_web.pdf"
"S1 CheatSheet" "C:\stacktest\SF_S1-Developer_cheatsheet_web.pdf" "C:\stacktest\SF_S1-Developer_cheatsheet_web.pdf"
Fire Data Loader, select Insert, select showing all Salesforce objects. Find ContentVersion. Load should be straightforward (if you're hitting memory issues set batch size to something low, even 1 record at a time if really needed).
You'll get back a "success file", it's useless. We don't need the Ids of generated content versions, we need their parents... Fire "Export" in Data Loader, pick all objects again, pick ContentDocument. Use query similar to this:
Select Id, Title, FileType, FileExtension
FROM ContentDocument
WHERE CreatedDate = TODAY AND CreatedBy.FirstName = 'Ethan'
You should see something like this:
"ID","TITLE","FILETYPE","FILEEXTENSION"
"0690g0000048G2MAAU","Lightning Components CheatSheet","PDF","pdf"
"0690g0000048G2NAAU","Process Automation CheatSheet","PDF","pdf"
"0690g0000048G2OAAU","Admin CheatSheet","PDF","pdf"
"0690g0000048G2PAAU","S1 CheatSheet","PDF","pdf"
Use Excel and magic of VLOOKUP or other things like that to link them back by title to Contacts. You wrote you already have a file with Contact Ids and titles so there's hope... Create a file like that:
ContentDocumentId LinkedEntityId ShareType Visibility
0690g0000048G2MAAU 0037000000TWREI V InternalUsers
0690g0000048G2NAAU 0030g000027rQ3z V InternalUsers
0690g0000048G2OAAU 0030g000027rQ3a V InternalUsers
0690g0000048G2PAAU 0030g000027rPz4 V InternalUsers
1st column is the file Id, then contact Id, then some black magic you can read about & change if needed in ContentDocumentLink docs.
Load it as insert to (again, show all objects) ContentDocumentLink.
Woohoo! Beer time.
Your CSV should contain following fields :
- ParentID = Id of object you want to link the attachment to (the ID of the contact)
- Name = name of the file
- ContentType = extension(.xls or .pdf or ...)
- OwnerId = if empty I believe it takes your user as owner
- body = the location on your machine of the file (for instance: C:\SFDC\Files\test.pdf
Use this csv to insert the records (via data loader) into the Attachment object.
You will then see for each contact, that records have been added to the 'Notes & Attachments' related list.

How to store hidden fields inside message in web application?

I have web app (laravel 5.3, mysql) where users can comment any entity in project (almost every page contains something like chat)
I want to add possibility to mention other users inside message via '#' symbol ("Hello, #John, see here", for example).
When this message is posted to chat user named John must get notification about new message (via email, if he is offline).
Every message is connected to some page (/object/45, for example), so when email is sent user will know the page where he was mentioned.
The question is how to store this inside database?
message field has type text
In this example row would contain this data "Hello, #John, see here" (without quotes).
Problem is that there can be many users with name "John" so I can not not do simple:
select email from users where username = 'John' -- email is used as login
Also username can be something like #John Malkovich, so I have to parse string to find out, if "John" or "John Malkovich" was mentioned.
What is unique - user id.
So how to store this inside database?
Possible solution:
Hello, [user=34], see here - field in database
Parse string before displaying to web browser and replace this string with
Hello, #John, see here
but, obviously, no one can paste literal text '[user=123]' inside message, because it would be interpreted as userid.
P.S. Inside one message many users can be mentioned.
Maybe you could create something like <span value="user34">John Malkovich</span> and parse the value?
Or <span data-user-id="user34">John Malkovich</span> is probably better semantically.
Have absolutely no experience in this kind of stuff though, so don't take me too seriously ;)

Office 365 Rest API - Changing message Id

Is there any scenario in which the Id field on a message could change?
We received a message at today (Thu, 19 Feb 2015 10:29:48 GMT +00:00) with an ID ending in 'MT80AAA='. However when trying to retrieve message details at a later time I get a 404 error with the message:
{ "error" : { "code": "ErrorItemNotFound", "message" : "The specified object was not found in the store." } }
When I do another request to get all messages from that inbox the same message appears however it has another ID ending in 'TwDlAAA='.
Both the message ID and the Attachment ID have been changed at some point from the first time receiving the message and now.
Is this a common occurrence? What could cause this? Or is it a bug?
It's not a bug, it's definitely a common occurrence with Exchange. Entry ID's aren't static, and can change in a number of scenarios. Common ones are moving the item (which is essentially a copy and delete operation, copy gets a new ID since it's a new item), or mailbox move (which may be applicable to your case).
Typically I would advise a strategy of optimistically storing the ID and using it first, since it will usually work. Then I'd recommend storing the record key and search key as a backup, so you could try searching for the item in the event the Id no longer works. However, the REST API doesn't expose these fields! So you'd have to make do with searching for the item based on whatever fields are important to you, which isn't ideal. I'll provide this feedback to our developers.

Send email to dynamic recipient SSIS Send Mail Task

I have an SSIS package which is going to be deployed on test, staging, and finally production.
It has a couple of "Send Mail Tasks",say 10.
As developer, I put my email address as the recipient of the email.
Currently, for the test person, I need to change all the "To"s in all the script task to e.g. "TestPerson#test.com". If following the paradigm of hard-coding the emails this way,I need to change the recipient email 30 times!!! (10 for each stage stated above)
Just wondering if there is any way to inject To field(recipient) dynamically. e.g. from a variable. like I have done for the "MessageSource"
You can set the ToLine of the Send Mail task to be the value of a variable, using an Expression from the Properties window of the task.
We use a SQL table containing a list of email recipients for various conditions (table columns of kemail, emailaddress, success, error) and set flags in the table to 0=no, 1=yes for that particular user to receive emails on particular conditions.
Then create a variable that contains a delimited list of your recipients then use an expression to set "ToLine" for the send mail task.
The only thing to watch here is that you don't end up with a no records returned from the SQL table. We always have our "support" email address always having all the bits set, to avoid this.
So the package wont need to be modified when a new user needs to receive email updates.