I'm listening to groups endpoint via webhook.
I perform the follow actions via Outlook Online +Group
Create Group
Set Owner
Add one member
I got a webhook event for all three at once, this is fine, I assume things are batched. But the eventTime is identical – which makes it difficult to understand the order these events happened. So I was asking for clarification whether the resourceData.eventTime should be the same, or should they have been different.
{
"value": [
{
"changeType": "updated",
"clientState": "its_me_flow",
"resource": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Group",
"#odata.id": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"id": "7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"organizationId": "26e65220-5561-46ef-9783-ce5f20489241",
"eventTime": "2018-02-13T00:20:03.9107966Z"
},
"subscriptionExpirationDateTime": "2018-02-13T04:07:19.7318815+00:00",
"subscriptionId": "c8c521bf-2bcb-4b93-821f-40a1fb72c0f8"
},
{
"changeType": "updated",
"clientState": "its_me_flow",
"resource": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Group",
"#odata.id": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"id": "7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"organizationId": "26e65220-5561-46ef-9783-ce5f20489241",
"eventTime": "2018-02-13T00:20:03.9107966Z",
"owner#delta": [
{
"id": "36e3b856-3c2e-4605-ac5f-99064d229c3a"
}
]
},
"subscriptionExpirationDateTime": "2018-02-13T04:07:19.7318815+00:00",
"subscriptionId": "c8c521bf-2bcb-4b93-821f-40a1fb72c0f8"
},
{
"changeType": "updated",
"clientState": "its_me_flow",
"resource": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Group",
"#odata.id": "Groups/7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"id": "7763cbdb-7f79-4b95-bb3b-d8e83413f7ad",
"organizationId": "26e65220-5561-46ef-9783-ce5f20489241",
"eventTime": "2018-02-13T00:20:03.9107966Z",
"members#delta": [
{
"id": "36e3b856-3c2e-4605-ac5f-99064d229c3a"
},
{
"id": "9b47aca9-efc6-488c-8afd-c323eccad4b4"
}
]
},
"subscriptionExpirationDateTime": "2018-02-13T04:07:19.7318815+00:00",
"subscriptionId": "c8c521bf-2bcb-4b93-821f-40a1fb72c0f8"
}
]
}
see the eventTime is identical. From the looks of the owner#delta and members#delta it looks like the order is correct, but is can I trust the array order? Much prefer if the eventTime was sequential.
This is similar to receiving a page of changes via Delta Query, where many objects can be included a single page and order of changed object in a page follows some logic order. (eg: in your case owner add followed by member add).
Since ordering of events is not guaranteed with webhooks, eventTime is meant to help order pages of events received at different points of time. ex: Microsoft graph is attempting to send pages of changes P1 and P2 at time T1 and T2. However the target webhook endpoint was down at time T1, so Microsoft graph will retry with exponential delay upto 4 hours. While webhook endpoint might be available at time T2. So page P2 is received first, followed by P1 in this case. To help clients distinguish and order events on their end, eventTime field can be used.
Related
With Microsoft Graph API you can search your calendar events by issuing a request like it's documented here or like this:
POST /v1.0/search/query HTTP/1.1
Host: graph.microsoft.com
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: 174
{
"requests": [
{
"entityTypes": [
"event"
],
"query": {
"queryString": "test"
},
"from": 0,
"size": 50
}
]
}
So, in my case, for the search query test, I have more than 200 events (recurring and single ones), and in the request payload we've sent to Microsoft Graph we have the size parameter which basically sets the limit of records to be returned by this call.
So, when I issue the request from above, I get something like this, but never more than 25 results. No matter how I modify the size parameter, it always returns 25 events, although there are hundreds.
HTTP/1.1 200 OK
Content-type: application/json
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#search",
"value": [
{
"#odata.type": "#microsoft.graph.searchResponse",
"searchTerms": [
"contoso"
],
"hitsContainers": [
{
"#odata.type": "#microsoft.graph.searchHitsContainer",
"hits": [
{
"#odata.type": "#microsoft.graph.searchHit",
"hitId": "AAMkADEwODY2NzllLTQ3MmEtNGRlMC05ZTUyLTE4ZDRhYmU1ZGM3NABGAAAAAAA3+iYQBnJnQabRVDelNhnzBwAejhWkAOAxQ6M4c1c9NwfrAAAAAAENAAAejhWkAOAxQ6M4c1c9NwfrAABbUZLJAAA=",
"rank": 1,
"summary": "this is a testing nothing more",
"resource": {
"#odata.type": "#microsoft.graph.event",
"end": {
"dateTime": "2020-06-16T04:15:00Z",
"timeZone": "UTC"
},
"hasAttachments": false,
"iCalUId": "040000008200E00074C5B7101A82E008000000007093FDD79B3AD60100000000000000001000000036DAA2262EB4E04DA27DA77985FB8251",
"isAllDay": false,
"sensitivity": "Normal",
"start": {
"dateTime": "2020-06-16T03:30:00Z",
"timeZone": "UTC"
},
"subject": "testing testing 123",
"type": "Single"
}
}
],
"total": 25,
"moreResultsAvailable": true
}
]
}
]
}
Furthermore, in the response you get the flag moreResultsAvailable which is in my case true, but other than that, I can't find a way to traverse between the results page, or do pagination of some sort.
What am I missing here, or in the docs?
So, when I issue the request from above, I get something like this,
but never more than 25 results. No matter how I modify the size
parameter, it always returns 25 events, although there are hundreds.
As per https://learn.microsoft.com/en-us/graph/api/resources/search-api-overview?view=graph-rest-1.0#page-search-results
The maximum results per page (size) is 25 for message and event.
For paging you just need to use the from property to specify the start of the page eg for the next page it would be
{
"requests": [
{
"entityTypes": [
"event"
],
"query": {
"queryString": "test"
},
"from": 25,
"size": 25
}
]
}
When you create a Shift via Microsoft Teams, there is a field for an Unpaid Break in minutes, as shown here:
Shifts UI
When this shift is retrieved via Microsoft Graph API, the Unpaid Break field is nowhere to be found.
{
"#odata.etag": "\"350013f7-0000-0400-0000-60d4e66a0000\"",
"id": "SHFT_273d6cb6-8a97-4e56-9012-41902a8904d6",
"createdDateTime": "2021-06-24T20:09:14.58Z",
"lastModifiedDateTime": "2021-06-24T20:09:14.58Z",
"schedulingGroupId": "TAG_28cc22dd-65df-4f3f-86aa-f5fcff11da80",
"userId": "fd4be5ff-ceeb-4f6c-8331-4324573d37f8",
"draftShift": null,
"lastModifiedBy": {
"application": null,
"device": null,
"user": {
"id": "fd4be5ff-ceeb-4f6c-8331-4324573d37f8",
"displayName": "",
"userIdentityType": "aadUser"
}
},
"sharedShift": {
"displayName": "Sleep Shift",
"notes": null,
"startDateTime": "2021-06-15T12:00:00Z",
"endDateTime": "2021-06-15T21:00:00Z",
"theme": "blue",
"activities": []
}
}
I've tried the /teams/{teamId}/schedule/shifts and /teams/{teamId}/schedule/timesOff endpoints.
How do you retrieve the Unpaid Break value via Microsoft Graph? Is there a different endpoint I should be using?
Currently, there is no way to get 'Unpaid Break field' in response using Graph API. This will only be available in UI.
I'm trying to fetch all users from a specific group via an HTTP connector, a registered app, and Microsoft Graph.
The registered app has Directory.Read.All permissions.
My idea is that I'm calling the nextLink as long as it's there while appending all of the fetched users' userPrincipalName to an array eventually filling the array with all users of the group.
My Logic App looks like this:
Unfortunately, I'm just 1 reputation short of posting images, please forgive. The 3 links should provide an overview of the structure of my app.
First, nextLink is initialized to the first Graph API endpoint. This variable is set to the current nextLink through each iteration of the until loop.
Second, For the purpose of this exercise, I only get the top 5. I know there are only 9 users:
Lastly, I call the union method on the "users" array that I initialized earlier and the "value" array from the HTTP get method, to get one single array consisting of all users:
The issue is that the HTTP action always returns the same top 5 users. I've checked that the nextLink provided in the first HTTP GET call to Graph, is correct by copying it from the Runs history and pasting it into Microsoft Graph Explorer and there the next 4 users are correctly returned.
I also made sure that, for each iteration in the until loop, I call the Graph API with the nextLink from the previous iteration as expected.
The nextLink returned inside of the Logic App is exactly the same when I test it in Graph Explorer, but the same nextLink returns 2 different results when called from Graph Explorer and inside my Logic App.
Why is the result always the same top 5 users and not the next 4 users as expected?
If not sure about the reason why you will get this issue, but based on your requirement, I did a sample below:
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Initialize_variable": {
"inputs": {
"variables": [
{
"name": "GetGroupUrl",
"type": "string",
"value": "https://graph.microsoft.com/v1.0/groups/<your group id>/members?$select=userPrincipalName&$top=5"
}
]
},
"runAfter": {},
"type": "InitializeVariable"
},
"Initialize_variable_2": {
"inputs": {
"variables": [
{
"name": "users",
"type": "array"
}
]
},
"runAfter": {
"Initialize_variable": [
"Succeeded"
]
},
"type": "InitializeVariable"
},
"Until": {
"actions": {
"Compose": {
"inputs": "#union(variables('users'),body('HTTP')['value'])",
"runAfter": {
"HTTP": [
"Succeeded"
]
},
"type": "Compose"
},
"HTTP": {
"inputs": {
"authentication": {
"audience": "https://graph.microsoft.com",
"clientId": "<app id>",
"secret": "<app secret>",
"tenant": "<your secret>",
"type": "ActiveDirectoryOAuth"
},
"method": "GET",
"uri": "#variables('GetGroupUrl')"
},
"runAfter": {},
"type": "Http"
},
"Set_variable": {
"inputs": {
"name": "GetGroupUrl",
"value": "#{if(equals(body('HTTP')?['#odata.nextLink'], null),null,body('HTTP')['#odata.nextLink'])}"
},
"runAfter": {
"Compose": [
"Succeeded"
]
},
"type": "SetVariable"
}
},
"expression": "#equals(variables('GetGroupUrl'), '')",
"limit": {
"count": 60,
"timeout": "PT1H"
},
"runAfter": {
"Initialize_variable_2": [
"Succeeded"
]
},
"type": "Until"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"manual": {
"inputs": {
"method": "GET",
"schema": {
"properties": {
"text": {
"type": "string"
}
},
"type": "object"
}
},
"kind": "Http",
"type": "Request"
}
}
},
"parameters": {}
}
You can just replace the params with your own and paste it into your logic app code view and test it .
It works for me, as you can see , each request results are different :
Hope it helps .
This issue solved by OP self, this issue is due to queries in request URL , copy OP's comment as an answer :
After fiddling a bit more around with what each of you providing I
found a solution. It seems that when the query arguments are passed to
the HTTP GET outside of the endpoint itself (meaning in the "queries"
field inside of the block) it seems to keep overriding the nextLink.
When writing the endpoint URL out entirely with the odata parameters,
it works as intended.
I have been trying to grant a single user (possibly outside of the organization) access to a folder used by a shared group in Share Point The Microsoft Graph Docs state that the response should have an Id in it.
If I use a personal oneDrive account, and make the following request:
POST v1.0
https://graph.microsoft.com/v1.0/groups/{groupId}/drive/items/{folderId}/invite
body: {
"requireSignIn": true,
"sendInvitation": true,
"roles": [ "read"],
"recipients": [
{ "email": "{myPersonalEmail}" },
],
"message": "testing giving permission to self"
}
I get the following response:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
"value": [
{
"#odata.type": "#microsoft.graph.permission",
"id": "{permissionID}",
"roles": [
"read"
],
"grantedTo": {
"user": {
"email": "{myPersonalEmail}",
"id": "{responseID}",
"displayName": "Sarah"
}
}
}
]
}
However, we setup a different SharePoint account that when I make the same request https://graph.microsoft.com/v1.0/groups/{groupId}/drive/items/{folderId}/invite
I get a completely different response:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
"value": [
{
"#odata.type": "#microsoft.graph.permission",
"roles": [
"read"
],
"invitation": {
"signInRequired": true
},
"link": {
"type": "view",
"webUrl": "{shareURL}"
}
}
]
}
This is so bizarre to me, because we are hitting the same endpoint. It makes me think that maybe there are some site configurations that are making these two responses differ.
Does anyone know why we would be getting a different results from the same endpoint?
For better efficiency we upgraded our system from polling over to using the webhooks subscriptions and delta queries.
However, for the webhooks, I am finding we are getting a lot of unnecessary/spurious notifications.
For example if a user cancels an event what we see are several update notifications followed by a single deleted notification, in a matter of milliseconds.
This is quite annoying as it creates a situation where instead of being able to react to the notifications immediately (if the work queue is empty that is), we have had to add a throttle/debounce, so that we can get a more accurate reading from the webhooks.
An example of a delete also incurring an update is as follows:
[2017-06-28T23:17:40.657Z][meetingroom1.outlookDidNotify] Received notification {
"subscriptionId": "54c27b11-c2e2-4962-89dd-cf316bf3eecb",
"subscriptionExpirationDateTime": "2017-06-28T23:59:59.999+00:00",
"changeType": "updated",
"resource": "Users/f4a2ac63-ddf7-40dc-80da-4e05acf6488a/Events/AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA=",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Event",
"#odata.id": "Users/f4a2ac63-ddf7-40dc-80da-4e05acf6488a/Events/AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA=",
"#odata.etag": "W/\"DwAAABYAAAAPLFcVBl+pRZA4tM1jHR47AAD+zvOL\"",
"id": "AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA="
},
"clientState": "…"
}
[2017-06-28T23:17:40.658Z][meetingroom1.outlookDidNotify] Received notification {
"subscriptionId": "54c27b11-c2e2-4962-89dd-cf316bf3eecb",
"subscriptionExpirationDateTime": "2017-06-28T23:59:59.999+00:00",
"changeType": "deleted",
"resource": "Users/f4a2ac63-ddf7-40dc-80da-4e05acf6488a/Events/AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA=",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Event",
"#odata.id": "Users/f4a2ac63-ddf7-40dc-80da-4e05acf6488a/Events/AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA=",
"#odata.etag": "W/\"CQAAAA==\"",
"id": "AAMkADc1MWNjZWM3LTNhZDYtNGYwYi04OWYxLTU0N2E0MmI5YTE4MwBGAAAAAAAKhIOGniPIR6bsTEhp5zDzBwAPLFcVBl_pRZA4tM1jHR47AAAAAAENAAAPLFcVBl_pRZA4tM1jHR47AAD5gM31AAA="
},
"clientState": "…"
}
Likewise sending multiple update notifications for the same event in a fraction of a second.
Events that are created can be followed by several update events within a second:
[2017-06-28T15:57:45.044Z][meetingroom2.outlookDidNotify] Received notification {
"subscriptionId": "de0147cc-cdef-4141-b601-4eb32cec1377",
"subscriptionExpirationDateTime": "2017-06-28T23:59:59.999+00:00",
"changeType": "created",
"resource": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Event",
"#odata.id": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"#odata.etag": "W/\"DwAAABYAAADlDV58cGeaS7ZJcZ6ff+AvAAD9kqa/\"",
"id": "AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA="
},
"clientState": "…"
}
[2017-06-28T15:57:45.954Z][meetingroom2.outlookDidNotify] Received notification {
"subscriptionId": "de0147cc-cdef-4141-b601-4eb32cec1377",
"subscriptionExpirationDateTime": "2017-06-28T23:59:59.999+00:00",
"changeType": "updated",
"resource": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Event",
"#odata.id": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"#odata.etag": "W/\"DwAAABYAAADlDV58cGeaS7ZJcZ6ff+AvAAD9kqa/\"",
"id": "AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA="
},
"clientState": "…"
}
[2017-06-28T15:57:47.409Z][meetingroom2.outlookDidNotify] Received notification {
"subscriptionId": "de0147cc-cdef-4141-b601-4eb32cec1377",
"subscriptionExpirationDateTime": "2017-06-28T23:59:59.999+00:00",
"changeType": "updated",
"resource": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"resourceData": {
"#odata.type": "#Microsoft.Graph.Event",
"#odata.id": "Users/97e22591-9c20-4030-a11e-102ba89741fc/Events/AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA=",
"#odata.etag": "W/\"DwAAABYAAADlDV58cGeaS7ZJcZ6ff+AvAAD9kqbB\"",
"id": "AAMkADFlY2YyODZiLTc1OGItNGE1Yy1iNDI1LWNjMGQ5MDRjNDU2ZABGAAAAAACfo61cAY_iTZYbfhL-JgzZBwDlDV58cGeaS7ZJcZ6ff_AvAAAAAAENAADlDV58cGeaS7ZJcZ6ff_AvAAD9hgs0AAA="
},
"clientState": "…"
}
The notifications are a result of the behavior of Outlook's calendar. You may consider subscribing to only certain change types or using a $filter for the subscription.
POST https://graph.microsoft.com/V1.0/subscriptions
Header:
Content-type = application/json
Body:
{
"resource": "me/mailFolders('Inbox')/messages?$filter=IsRead%20eq%20false",
"notificationUrl": "https://mywebhook.azurewebsites.net/api/send/NotifyClient",
"changeType": "Created,Deleted ",
"expirationDateTime": "2017-06-30T18:23:45.935Z",
"clientState": "Message unread"
}