OfficeDev / office-js

A repo and NPM package for Office.js, corresponding to a copy of what gets published to the official "evergreen" Office.js CDN, at https://appsforoffice.microsoft.com/lib/1/hosted/office.js.
https://learn.microsoft.com/javascript/api/overview
Other
670 stars 96 forks source link

Outlook desktop: loadCustomPropertiesAsync and save erases MAPI property set #1848

Closed Hourglasser23 closed 3 years ago

Hourglasser23 commented 3 years ago

I am tracking Outlook items (Message and Event) to our CRM system. When I want to track message receive I send a request to our backend from office-js addin, it creates an activity object in our CRM system and then sets an icon for Outlook item via Graph API

Message updateMessage = new Message();

SingleValueLegacyExtendedProperty iconExtendedProperty = new SingleValueLegacyExtendedProperty();
iconExtendedProperty.id = "Integer 0x1080";
iconExtendedProperty.value = icon.value;

SingleValueLegacyExtendedPropertyCollectionRequestBuilder extendedPropertyBuilder = graphServiceClient.me()
        .messages(itemId).singleValueExtendedProperties();
updateMessage.singleValueExtendedProperties = new SingleValueLegacyExtendedPropertyCollectionPage(
        Collections.singletonList(iconExtendedProperty), extendedPropertyBuilder
);

graphServiceClient.me().messages(itemId).buildRequest().patch(updateMessage);

After that, I send back the ID of the CRM activity and add it to custom properties in addin and save it.

Office.context.mailbox.item.loadCustomPropertiesAsync(
      (asyncResult: Office.AsyncResult<Office.CustomProperties>) => {
        asyncResult.value.set(IS_TRACKED_KEY, globalStore.isTracked().toString());
        if (activityId && !asyncResult.value.get(ACTIVITY_ID_KEY)) {
          asyncResult.value.set(ACTIVITY_ID_KEY, activityId.toString());
        }

        asyncResult.value.saveAsync((asyncResult: Office.AsyncResult<void>) => {
          if (asyncResult.error) {
            globalStore.setError("Could not mark mail as Added to CRM");
            reject();
          }
          resolve();
        });
      }
    );

Expected Behavior

The icon is successfully changed (via Graph API call and backend) and persists (Office-js saveAsync won't change it)

Current Behavior

Graph API request on our backend changes icon, however, saveAsync on asyncResult of custom properties resets it to the icon that was set before (mostly none) -> I suppose, that Graph API changes it on exchange server but Outlook desktop won't fetch change instantly and Office-js has probably under the hood whole Message/Appointment item cached and save old icon value (MAPI) of cached item which overwrites my change in Graph API

Steps to Reproduce, or Live Example

Code above

Context & Your Environment

Windows 10 Pro 20H2 (build 19042.964), Outlook desktop 2104 (build 13929.20296), Office-js (don't know where to find version,not in package.json, @types/office-js 1.0.169, used newest yo generator) Custom backend JAVA, crm (Graph API SDK 3.0.0)

Useful logs

None

exextoc commented 3 years ago

Generally, making server-side and client-side changes to an item simultaneously can result in conflicts, and that is what is happening here. Outlook has logic for conflict resolution. I'm sure that if there's sufficient time allowed between your server-side change and the client-side change, you won't experience this issue because Outlook will synchronize the updated server item before applying the client/local change. Are there any special reasons why you are using Extended Properties for the icon, or are there any limitations with Custom Properties that make it impossible to use for the icon?

ghost commented 3 years ago

This issue has been automatically marked as stale because it is marked as needing author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. Thank you for your interest in Office Add-ins!

Hourglasser23 commented 3 years ago

I could not find how to set an icon with anything else than Extended Properties. I went through the docs and asked on StackOverflow and did not notice any possible solution of changing an icon via Custom Properties. Is it possible? It would solve my problem absolutely :)

exextoc commented 3 years ago

What we would recommend for you to get unblocked is that if you're using Graph to set the ExtendedProperty, you should also use Graph to retrieve it. Else, you could use setCustomProperties API and then the loadCustomProperties will work as expected.

In addition: Client side JS API for setting extended properties is not currently a part of the product. We track Outlook add-in feature requests on our Tech Community Page. Please submit your request here and choose the appropriate label. Feature requests on Tech Community are considered, when we go through our planning process.

Github Label: “Type: product feature request” https://techcommunity.microsoft.com/t5/microsoft-365-developer-platform/idb-p/Microsoft365DeveloperPlatform

Hourglasser23 commented 3 years ago

What we would recommend for you to get unblocked is that if you're using Graph to set the ExtendedProperty, you should also use Graph to retrieve it - yes, that is exactly what I am doing. I set an icon on Extended properties via Graph API, not even retrieving it I just tried to retrieve to confirm it is saved.

Else, you could use setCustomProperties API and then the loadCustomProperties will work as expected - this works as expected too and I am using it this way.

The problem is when I set icon via Graph API on the backend (it changes on the exchange server) and then in Office-js addin I set my another property via Custom properties (not the icon, it is not even possible. I just save some ID of the internal object) and save it, outlook saves cached item (not refreshed from exchange server) which !somehow! overrides Exchange properties I was saving on the backend a few seconds before.

exextoc commented 3 years ago

Just to clarify, you are setting property A through exchange and a separate property B through client side with setCustomProperties? And when you say that the original extended property "A" is getting overridden, is property A lost completely? or is it getting set to a different value? After you set property A with Graph and B with setCustomProperties can you try to get property A again through Graph and share what you get back?

Hourglasser23 commented 3 years ago

Just to clarify, you are setting property A through exchange and a separate property B through client side with setCustomProperties Yes

And when you say that the original extended property "A" is getting overridden, is property A lost completely nah, property A is icon ('Integer 0x1080') as you can see in script I attached above - it cannot get lost from Message item. setting custom properties just put there its previous value cached in Outlook.

After you set property A with Graph and B with setCustomProperties can you try to get property A again through Graph and share what you get back - if I am quick enough and setCustomProperties in addin won't save it to exchange server, I will get my property set in A, but if addin saves changes from setCustomProperties to the exchange server, then there is previous value.

Setting custom properties in addin overrides extended properties (where custom properties are saved) cached ones in Outlook.

exextoc commented 3 years ago

The issue you are observing may be related to inconsistent item state. An item can get in an inconsistent state if it is updated using office.js API and Graph API before Outlook client gets a chance to sync its state. In your case, since you were only able to set the icon via Graph, it is recommended to also set property B via Graph. Mixing the two can result in conflicts.

Hourglasser23 commented 3 years ago

Okay, it makes sense now. If I update it via Graph then it should not be the problem. Thank you