Users delta collection returns "empty" records - microsoft-graph-api

We are trying to call this MS Graph endpoint:
https://graph.microsoft.com/v1.0/users/delta?$deltatoken=<DELTA_TOKEN>&$select=id,displayName,givenName,surname,employeeId,accountEnabled,assignedLicenses,assignedPlans,licenseAssignmentStates,usageLocation,userPrincipalName,userType,businessPhones,mail,mailNickname,mobilePhone,proxyAddresses,streetAddress,city,country,companyName,department,jobTitle,officeLocation,postalCode,state,isResourceAccount,createdDateTime,deletedDateTime,lastPasswordChangeDateTime,onPremisesLastSyncDateTime,onPremisesDistinguishedName,onPremisesDomainName,onPremisesImmutableId,onPremisesSamAccountName,onPremisesSecurityIdentifier,onPremisesSyncEnabled,onPremisesUserPrincipalName
This is what we get:
"value": [
{
"displayName": "Jozef Surname",
"givenName": "Jozef",
"surname": "Surname",
"userPrincipalName": "jozef.surname#company.com",
"mail": "jozef.surname#company.com",
"id": "048...3d-eaf2-4199-8e88-9cd6...2091b"
},
{
"displayName": null,
"givenName": null,
"surname": null,
"userPrincipalName": null,
"mail": null,
"id": "ae...ecf-8ae8-4666-b158-93...c799c00"
},
{
"displayName": null,
"givenName": null,
"surname": null,
"userPrincipalName": null,
"mail": null,
"id": "33...d09-81a9-4304-833d-71...92f4b6a"
},
The question is, why are we getting records with null fields? We would expect records like the first one in above example.
When we query for some empty record (record with nulls) (https://graph.microsoft.com/v1.0/users/ae...ecf-8ae8-4666-b158-93...c799c00), we get this:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Zuzana Surname4",
"givenName": "Zuzana",
"jobTitle": null,
"mail": "zuzana.surname4#company.com",
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": "Surname4",
"userPrincipalName": "zuzana.surname4#company.com",
"id": "ae...ecf-8ae8-4666-b158-93...c799c00"
}
It’s an existing user. We just don’t know why it’s returned with null fields when we call /users/delta with deltaToken?
We would expect a response like this one (no “empty” records):
"value": [
{
"displayName": "Jozef Surname",
"givenName": "Jozef",
"surname": "Surname",
"userPrincipalName": "jozef.surname#company.com",
"mail": "jozef.surname#company.com",
"id": "04...33d-eaf2-4199-8e88-9c...b82091b"
},
{
"displayName": "Nick Surname2",
"givenName": "Nick",
"surname": "Surname2",
"userPrincipalName": "nick.surname2#company.com",
"mail": "nick.surname2#company.com",
"id": "4f...faa-f1eb-462b-be31-e5...5860a25"
},
{
"displayName": "Jan Surname3",
"givenName": "Jan",
"surname": "Surname3",
"userPrincipalName": "jan.surname3#company.com",
"mail": "jan.surname3#company.com",
"id": "25...f04-dbec-4580-b35b-3a...262b38c"
},
Is this a bug in /users/delta with deltaToken or is it expected behavior?
Thanks for any help.

With the above behavior, it is not possible to differentiate between a property that has not changed and one that has changed to a null value.
By default, requests using a delta link or next link return the same properties as selected in the initial delta query in the following ways:
If the property has changed, return the property in the JSON
response.
If the property has been set to an empty value, return the property value as null.
If the property has not changed, return the value as null.

yes exactly it should work how the documentation says and it is working this way with other tenants. However we are getting the properties which have the value (like UPN) with null values in the reponse. According to the documentation it should come with the current value of the property or if value was never set the property shouldn't be part of the response at all. It's really weird behavior..

The other part of MS docs says:
By default, requests using a deltaLink or nextLink return the same properties as selected in the initial delta query in the following ways:
If the property has changed, the new value is included in the response. This includes properties being set to null value.
If the property has not changed, the old value is included in the response.
If the property has never been set before it will not be included in the response at all.
What is pretty inconsistent with the MS docs from the answer above.
The question is what will "$select" does if it is used for the property which was not supposed to be part of the response, based on the condition above.

Related

MS Teams Graph API: Get all teams user(s) are apart of

I want to use the graph api to get the teams a list of users is a member/owner of. I am using this endpoint to pass the list of users, then select the properties of the group that I am interested in:
/beta/users/$filter=id+in+('user-1','user-2','user-15')$expand=transitiveMemberOf($select=id,resourceProvisioningOptions,displayName,mailNickname)&$select=id,resourceProvisioningOptions,displayName
This will return a list of groups the member is apart of, including the Teams the member is apart of (up to 15 users). It will filter the groups to only return id,resourceProvisioningOption,displayName and mailNickname.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,resourceProvisioningOptions,displayName,transitiveMemberOf(id,resourceProvisioningOptions,displayName,mailNickname))",
"value": [
{
"id": "user-1",
"displayName": "Jeff Smith",
"transitiveMemberOf": [
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-1",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-2",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.group",
"id": "team-id-1",
"resourceProvisioningOptions": [
"Team"
],
"displayName": "My Team 1",
"mailNickname": "MyTeam1"
},
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "directoryRole-3",
"displayName": null
},
{
"#odata.type": "#microsoft.graph.group",
"id": "team-id-2",
"resourceProvisioningOptions": [
"Team"
],
"displayName": "My Team 2",
"mailNickname": "MyTeam2"
}
]
}
]
}
Since the user is apart of Azure directory groups, those are returned, but the resourceProvisioningOptions identifies if the user is on a team. In this case - 2 teams.
Problem
When looking in the Teams App, we see that the user is actually apart of the 10 different teams.
When calling this endpoint: https://graph.microsoft.com/v1.0/users/user-1/joinedTeams we see that the user is apart of 10 teams
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams",
"#odata.count": 10,
"value": [
{
"id": "team-id-1",
"createdDateTime": null,
"displayName": "Team 1",
"description": "Team1",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
},
{
"id": "team-id-2",
"createdDateTime": null,
"displayName": "Team 2",
"description": "Team2",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
},
...
{
"id": "team-id-10",
"createdDateTime": null,
"displayName": "Team 3",
"description": "Team3",
"internalId": null,
"classification": null,
"specialization": null,
"visibility": null,
"webUrl": null,
"isArchived": false,
"isMembershipLimitedToOwners": null,
"memberSettings": null,
"guestSettings": null,
"messagingSettings": null,
"funSettings": null,
"discoverySettings": null,
"summary": null
}
]
}
Questions:
Is there another endpoint that I can use to pass a list of users and get the teams they are owner/member of?
Why is the data different between endpoints
I am trying to avoid calling the /joinedTeams endpoint due to number of users and rate limiting.
You could use the /joinedTeams endpoint, but also put those calls in a batch to MS Graph.
See: https://learn.microsoft.com/en-us/graph/json-batching
This would allow you to send a batch of 20 (i think) requests in one call to the graph making it much faster and less likely to be throttled.

$expand=manager does not expand manager

When testing https://graph.microsoft.com/v1.0/users/<my-email>/manager, I receive the following response (I replaced values with "HIDDEN"):
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects/$entity",
"#odata.type": "#microsoft.graph.user",
"id": HIDDEN,
"businessPhones": [
HIDDEN
],
"displayName": HIDDEN,
"givenName": HIDDEN,
"jobTitle": "Director, Information Technology",
"mail": HIDDEN,
"mobilePhone": HIDDEN,
"officeLocation": HIDDEN,
"preferredLanguage": null,
"surname": HIDDEN,
"userPrincipalName": HIDDEN
}
Clearly it works. But when I try the same URI using $expand instead, it doesn't show the manager:
URI: https://graph.microsoft.com/v1.0/users/<my-email>?$expand=manager
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [
HIDDEN
],
"displayName": "Yousef Shanawany",
"givenName": "Yousef",
"jobTitle": "Developer, Application",
"mail": HIDDEN,
"mobilePhone": null,
"officeLocation": "US California San Jose America Center",
"preferredLanguage": null,
"surname": "Shanawany",
"userPrincipalName": HIDDEN,
"id": HIDDEN
}
How can I get my manager using the $expand parameter in MS Graph?
I believe this works in beta today. Try GET https://graph.microsoft.com/beta/me?$expand=manager

Not possible to create class assignment when educationClass created via Graph

I created 2 educationClass:
educationClass with description Test100 created via Microsoft Graph
educationClass with the description Test created by the Teams app.
The educationclass Test100 is created using a POST to https://graph.microsoft.com/V1.0/education/classes with the following JSON object
{
"displayName": "Test100",
"description": "Test100",
"externalId": "3099",
"mailNickName": "Test100-3099"
}
Teachers are added using a POST to
https://graph.microsoft.com/V1.0/education/classes/ID-Education-Class/teachers/$ref with the following JSON object
{
"#odata.id": "https://graph.microsoft.com/V1.0/education/users/teacher-ID"
}
Members are added using a POST to
https://graph.microsoft.com/V1.0/education/classes/ID-Education-Class/members/$ref with the following JSON object
{
"#odata.id": "https://graph.microsoft.com/V1.0/education/users/ID-Member"
}
When I want to assign an assignment in the Teams app, it works fine with the educationClass created via Teams but not for the class created via Graph.
To me, it seems there is something missing in the Group properties for the educationClass created by Graph.
Response from GET GROUP for the class created by the API:
{
"#odata.context": "https://graph.microsoft.com/V1.0/$metadata#groups/$entity",
"id": "cd5a64f4-8784-4356-8c04-31deb1aa6849",
"deletedDateTime": null,
"classification": null,
"createdDateTime": "2018-08-06T08:36:28Z",
"creationOptions": [],
"description": "Test100",
"displayName": "Test100",
"groupTypes": ["Unified"],
"mail": "Test100-3099#wisad.be",
"mailEnabled": true,
"mailNickname": "Test100-3099",
"onPremisesLastSyncDateTime": null,
"onPremisesProvisioningErrors": [],
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"preferredDataLocation": null,
"proxyAddresses": ["smtp:Test100-3099#mydomein", "SMTP:Test100-3099#mydomein"],
"renewedDateTime": "2018-08-06T08:36:28Z",
"resourceBehaviorOptions": [],
"resourceProvisioningOptions": ["Team"],
"securityEnabled": false,
"visibility": "HiddenMembership",
"extension_fe2174665583431c953114ff7268b7b3_Education_SyncSource_SectionId": "3099",
"extension_fe2174665583431c953114ff7268b7b3_Education_CreatedByUserId": "myuserid",
"extension_fe2174665583431c953114ff7268b7b3_Education_CreatedByAppId": "MyAppID",
"extension_fe2174665583431c953114ff7268b7b3_Education_ObjectType": "Section"
}
Response from GET GROUP for the class created in Teams:
{
"#odata.context": "https://graph.microsoft.com/V1.0/$metadata#groups/$entity",
"id": "e971cfd4-dc4c-4321-ae51-4d216557bec8",
"deletedDateTime": null,
"classification": null,
"createdDateTime": "2018-08-06T11:56:52Z",
"creationOptions": ["classAssignments", "ExchangeProvisioningFlags:2509"],
"description": "Test",
"displayName": "Test",
"groupTypes": ["Unified"],
"mail": "Test92#mydomain",
"mailEnabled": true,
"mailNickname": "Test92",
"onPremisesLastSyncDateTime": null,
"onPremisesProvisioningErrors": [],
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"preferredDataLocation": null,
"proxyAddresses": ["smtp:Test92#mydomein.be", "SMTP:Test92#mydomain.be"],
"renewedDateTime": "2018-08-06T11:56:52Z",
"resourceBehaviorOptions": [
"HideGroupInOutlook",
"WelcomeEmailDisabled",
"ConnectorsDisabled",
"SubscribeNewGroupMembers"
],
"resourceProvisioningOptions": [],
"securityEnabled": false,
"visibility": "HiddenMembership",
"extension_fe2174665583431c953114ff7268b7b3_Education_ObjectType": "Section"
}
Notice that the creationOptions and resourceBehaviorOptions of the two Groups are different.
When I try to add a new assignment to education classes created by the GRAPH API, i get the following message saying: "Education class is being setup"
It is necessary to the send the Term startdate and enddate.
"term":{
"displayName":"Term2",
"externalID":"2",
"startDate":"2017-09-01",
"endDate":"2019-08-31"
}
Your observations were spot on. The assignments Api (currently in Graph Beta) were not reading all the possible properties that could denote a class type. (creationOptions and another extension property which can be stamped to signal the group is a class type. Exactly which property get stamped is dependent on when the group was created and how).
We are working on a fix and getting it patched this week.
Thanks

Error when adding a member to a group: "Unable to update the specified properties for objects that have originated within an external service."

Using https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/group_post_members to add a member to a group
POST /groups/{id}/members/$ref
{
"#odata.id": "https://graph.microsoft.com/v1.0/users/xxx#yyy.com"
}
returns error
{
"error": {
"code": "Request_BadRequest",
"message": "Unable to update the specified properties for objects that have originated within an external service."
}
}
The group that I'm trying to add to has the following properties:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
"id": "xxx",
"deletedDateTime": null,
"classification": null,
"createdDateTime": "2017-09-11T21:04:49Z",
"description": null,
"displayName": "xxx",
"groupTypes": [],
"mail": "xxx#yyy.com",
"mailEnabled": true,
"mailNickname": "xxx",
"onPremisesLastSyncDateTime": null,
"onPremisesProvisioningErrors": [],
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"preferredDataLocation": null,
"proxyAddresses": [
"smtp:xxx#yyy.com",
"SMTP:xxx#yyy.com"
],
"renewedDateTime": "2017-09-11T21:04:49Z",
"securityEnabled": false,
"visibility": null
}
Found out that the reason for the rejection was because Microsoft only allows adding members to Office 365 Groups (groupTypes = ["Unified"])
In my case, the group properties above shows (groupTypes= [])
My test result for your reference.
List user ID so get user ID.
https://graph.microsoft.com/v1.0/users

O365 Graph API, GET /groups return null entities

i'm trying develop an app that integrate with O365 Groups, for that i need all the info i can get. using the Graph API Explorer when i call "https://graph.microsoft.com/v1.0/groups/"
i get answer like this:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
"id": "8143b56b-a4bb-43ef-8b17-a747b1cacec1",
"createdDateTime": "2016-02-23T11:03:33Z",
"description": "asd asd asd ",
"displayName": "Test Group",
"groupTypes": [
"Unified"
],
"mail": "testgroup#fastgroup.onmicrosoft.com",
"mailEnabled": true,
"mailNickname": "testgroup",
"onPremisesLastSyncDateTime": null,
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"proxyAddresses": [
"SMTP:testgroup#fastgroup.onmicrosoft.com"
],
"renewedDateTime": "2016-02-23T11:03:33Z",
"securityEnabled": false,
"visibility": "Public"
}
but once i'm calling the same rest function from my app i get most of the entities as null, like this:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
"id": "8143b56b-a4bb-43ef-8b17-a747b1cacec1",
"createdDateTime": null,
"description": null,
"displayName": "Test Group",
"groupTypes": [],
"mail": null,
"mailEnabled": null,
"mailNickname": null,
"onPremisesLastSyncDateTime": null,
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"proxyAddresses": [],
"renewedDateTime": null,
"securityEnabled": null,
"visibility": null
}
can someone please tell me what i'm missing?
Access token scopes in my app: Group.Read.All User.Read.
Thanks!
Currently we only return the basic properties of the group with the group.read.all permission. We will be changing that in the future to return all properties (as long as the signed-in user has permissions to read the group property information).
For now you will need to use group.readwrite.all to get the full properties of the group.
I'll update this answer once we make the update.
Hope this helps,

Resources