ErrorItemNotFound when trying to retrieve room calendar via MS Graph API - microsoft-graph-api

Next to retrieving calendar views of a user's calendar (on behalf of the user), we are trying hard to also get the calendar view of rooms via the Graph API using
https://graph.microsoft.com/beta/users/room1#ourdomain.com/calendarView. It's a painful process since we've been running into many problems and are currently stuck with the following 404 response:
https://graph.microsoft.com:443/v1.0/users/room1#ourdomain.com/calendarView?startDateTime=2018-12-04T23:00:00.000Z&endDateTime=2019-02-10T22:59:59.999Z
{
"error": {
"code": "ErrorItemNotFound",
"message": "The specified object was not found in the store.",
"innerError": {
"request-id": "358a003a-57a4-4f0e-91da-edc17c1fa2d8",
"date": "2018-12-12T07:38:33"
}
}
}
The email address of the room has been double checked and the resource exists, since we can create appointments with it and it is even being returned in the response when we retrieve the calendar of the user who has an appointment in that location.
App permissions and OAuth2 scopes are set to: openid email profile offline_access https://graph.microsoft.com/Calendars.Read https://graph.microsoft.com/Calendars.Read.Shared https://graph.microsoft.com/User.Read
https://graph.microsoft.com/User.ReadBasic.All https://graph.microsoft.com/User.Read.All, so that should not be an issue, judging by the documentation.
Does anyone know how to solve this?

I've tried all possible ways, but there is no way to get access.
This is what I've tried out the following in the Graph explorer:
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/events -> DelegatedCalendarAccessDenied
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/calendarView?startDateTime=2019-01-14&endDateTime=2019-01-18 -> ErrorItemNotFound
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/calendar/calendarView?startDateTime=2019-01-14&endDateTime=2019-01-18 -> ErrorItemNotFound
All three on both the v1.0 and the beta.
It isn't an issue with rights, because for my testing I granted the Graph Explorer the Directory.ReadWrite.All scope. Resulting in the following scp claim.
The first requests seems the most promising (because of the different error), I also made myself a delegate with full control of the rooms-mailbox. That still didn't help.
A request to https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com gives a result, as in a result describing the meetingroom.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Meeting room 1",
"givenName": null,
"jobTitle": null,
"mail": "meetingroom1#domain.com",
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": null,
"userPrincipalName": "meetingroom1#domain.com",
"id": "3e0a7b7e-xxxx-xxxx-xxxx-xxxxcxxxx120"
}
After doing all these tests, I can only conclude that you cannot access the events in a rooms mailbox. This is either intended (as in only use the scheduling assistant) or a bug.
Maybe some of the Microsoft guys around here could clarify this?

FINALLY! After going through this with countless Microsoft support people, each of whom said this was not their territory and did not know where to forward the question, I got in touch with somebody from the Exchange team. He suggested the one thing that worked for us: the user on behalf of which you are retrieving the room resource calendar needs to be a delegate of that room resource!
In addition, to retrieve the list of room resources which the user can select from, we needed to use the findRooms endpoint but this only works on the beta API. The only drawback of this is that you cannot seem to filter for rooms of which the user is a delegate. So the user will get a list of rooms for which he might or might not be able to retrieve the calendar.
A final drawback of the room resource calendarView response is that the response does not contain the names of the meetings planned in the rooms. The description of each event only contains the name of the meeting organizer.

Related

List private team channel members with Microsoft Graph API in Microsoft Teams

My application for Microsoft Teams works as a tab and needs to get the list of the chat members or channel members, depending on where it is installed.
/chats/{chat-id}/members
/teams/{team-id}/channels/{channel-id}/members
To use the APIs, I get the Context object from the client SDK library to get the identifiers required for the APIs. It works fine for chats–both group and one on one (using Context.chatId), and public team channels (using Context.groupId and Context.channelId).
However, nothing I try seems to work for private team channels.
The context object returned for private team channels contains teamId and channelId, but they are equal, and using one value for both ids naturally doesn't work. Here is an example of what is returned for a private team channel by the SDK library 1.11.0 (the latest):
{
"locale": "en-us",
"theme": "default",
"subEntityId": "",
"isFullScreen": false,
"sessionId": "5194fd2b-5c9a-16a7-7411-94ddabffffff",
"chatId": "",
"meetingId": "",
"parentMessageId": "",
"hostClientType": "desktop",
"tenantSKU": "unknown",
"jsonTabUrl": "microsoft-teams-json-tab.azurewebsites.net",
"userLicenseType": "Unknown",
"appSessionId": "7503c11c-d524-409c-b58b-004810ffffff",
"appLaunchId": "c736c663-cc0b-47c3-8824-ba56b7ffffff",
"isMultiWindow": false,
"appIconPosition": 79,
"userClickTime": 1637007245298,
"sourceOrigin": null,
"userFileOpenPreference": "inline",
"osLocaleInfo": {
"platform": "macos",
"regionalFormat": "en-gb",
"longDate": "d MMMM y",
"shortDate": "dd/MM/y",
"longTime": "HH:mm:ss z",
"shortTime": "HH:mm"
},
"frameContext": "settings",
"isTeamArchived": false,
"teamType": 0,
"userTeamRole": 0,
"channelRelativeUrl": "/sites/ffffff/Shared Documents/Devel",
"channelId": "19:0bc109b412d9448bb6b1b3d4d485700b#thread.tacv2",
"channelName": "Devel",
"channelType": "Private",
"defaultOneNoteSectionId": "",
"teamId": "19:0bc109b412d9448bb6b1b3d4d485700b#thread.tacv2",
"teamName": "Devel",
"teamSiteUrl": "https://ffffff.sharepoint.com/sites/worldrtech-Devel",
"teamSiteDomain": "ffffff.sharepoint.com",
"teamSitePath": "/sites/ffffff",
"teamTemplateId": "",
"teamSiteId": "",
"ringId": "general",
"tid": "d158bb9f-f90c-422d-9d0d-0040efffffff",
"loginHint": "ffffff#ffffff.uk",
"upn": "nox#worldr.co.uk",
"userPrincipalName": "ffffff#ffffff.uk",
"userObjectId": "fc5a4a6d-60e2-4370-83bd-aab1baffffff"
}
You can see above that the two are equal:
"channelId": "19:0bc109b412d9448bb6b1b3d4d485700b#thread.tacv2"
"teamId": "19:0bc109b412d9448bb6b1b3d4d485700b#thread.tacv2"
I wonder, whether this is an expected behaviour, or something is broken there... 🤔 As per comment by #Prasad-MSFT, this is normal behaviour for private channels.
There is an answer suggesting that one should first list all the teams the user joined. However, I don't see how I would connect this information to the context data shown above.
Is there a way to list the members of a private team channel? What am I missing?
UPDATE1 16.11:
I did an experiment, but the results got me puzzled. I followed the idea of getting all the teams of the user first. This got me ids of all the teams the user is a member of. I then requested members of for the current private channel for every team: I expected to get errors for all teams but one–that team the channel really belongs to. However, I got members for every request! That's very confusing.
/me/joinedTeams
/teams/{id}/channels/{channel_id}/members for each team received in 1. and channelId received from context.
Each call returned some members, which I did not expect...
UPDATE2 16.11:
This long-winded way gets me channel members in the end:
/me/joinedTeams
/teams/{id}/channels for each team received in 1.
Find the channel with id matching the channelId from my context among those received in 2.
/teams/{id}/channels/{channel_id}/members for the channel found in 3.
Looks like a lot of effort for such a simple thing. 🙄
Update: see the new answer from Nov 1 2022.
Update 2: it's still not fully functional, use both. 🤷‍♂️
This long-winded way gets me channel members list in the end. Only applies to private team channels because we don't have group id/team id in the Context there. As per #Prasad-MSFT's comment to the original post, there is no other way at the time of writing.
Get /me/joinedTeams
Get /teams/{id}/channels for each team received in #1.
Find the channel with id matching the channelId from the Context among those received in #2.
Get /teams/{id}/channels/{channel_id}/members for the channel found in #3.
Here is a caveat: if you create a private team channel and immediately add your tab to it, there is a chance that your new channel will not be returned by the API. If #3 in the above list of procedures fails, I ask the user to retry in a couple of minutes.
A year later there seem to be some improvements! (Yeah, but not really, see below.)
There is a new property called hostTeamGroupId which appears to contain the correct team id for private channels. The documentation, however, doesn't mention private channels specifically and only tells us about shared channels. Nevertheless, it seems to be working for the purposes of this question.
In #microsoft/teams-js v2 we can use Context.channel.ownerGroupId.
hostTeamGroupId
The property is newly added and describes the host
team’s Azure AD group ID, useful for making Microsoft Graph API calls
to retrieve shared channel membership.
However
This still doesn't work on mobile platforms at the time of writing 08.11.2022. The fields mentioned above are not populated.
So, for mobile platforms, we still need my other answer.

Using RSC To Access Chat Messages with Microsoft Graph

I am building a Teams chat-bot that looks at the history of messages in the current chat/channel whilst in conversation with the user.
My bot has been granted all the RSC (Resource-Specific Content) Permissions it needs (see image below)
Here is the relevant parts of the manifest:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json",
"version": "1.0.0",
"manifestVersion": "1.11",
"id": "bd33f8b1-b593-433c-926e-44a27c1bd94a",
...
"permissions": [
"identity",
"messageTeamMembers"
],
...
"bots": [
{
"botId": "e6d93739-a8ab-412d-a4f6-b6f514a3451a",
"scopes": [
"team",
"personal",
"groupchat"
],
"isNotificationOnly": false,
"supportsFiles": true
}
],
"validDomains": [],
"webApplicationInfo": {
"id": "e6d93739-a8ab-412d-a4f6-b6f514a3451a",
"resource": "https://RscBasedStoreApp",
"applicationPermissions": [
"TeamSettings.Read.Group",
"ChannelMessage.Read.Group",
"TeamSettings.Edit.Group",
"ChannelSettings.ReadWrite.Group",
"Channel.Create.Group",
"Channel.Delete.Group",
"TeamsApp.Read.Group",
"TeamsTab.Read.Group",
"TeamsTab.Create.Group",
"TeamsTab.ReadWrite.Group",
"TeamsTab.Delete.Group",
"Member.Read.Group",
"Owner.Read.Group",
"ChatSettings.Read.Chat",
"ChatSettings.ReadWrite.Chat",
"ChatMessage.Read.Chat",
"ChatMember.Read.Chat",
"Chat.Manage.Chat",
"TeamsTab.Read.Chat",
"TeamsTab.Create.Chat",
"TeamsTab.Delete.Chat",
"TeamsTab.ReadWrite.Chat",
"TeamsAppInstallation.Read.Chat",
"OnlineMeeting.ReadBasic.Chat",
"Calls.AccessMedia.Chat",
"Calls.JoinGroupCalls.Chat",
"TeamsActivity.Send.Chat"
]
}
}
Note: the bot has permission to read messages in chats and channels. Specifically, my problem affects chats and not channels (which I can get messages from fine).
In order to do this, I get a JWT token for the bot account, accessing the Graph API like so:
GraphServiceClient<?> gsc = GraphServiceClient.builder()
.authenticationProvider(u -> mac.getToken())
.buildClient();
Next, I am using the Graph API to pull back these messages. For messages in channels I can do:
gsc.teams("some group id")
.channels("team id")
.messages()
.buildRequest(Collections.emptyList()).get()));
This works fine.
For chats, I am doing something like:
gsc.chats("29:13qY8hmfkJinH9-v7rYKjCNFHYFJXKbjqR-NyzyKzL694npelHJoq5HrVtqJLRYo79OYeHGQq-bhtJM5N-yKXyQ")
.messages()
.buildRequest().get()));
However, this time I get an error from the Graph API:
[Some information was truncated for brevity, enable debug logging for
more details] com.microsoft.graph.http.GraphServiceException: Error
code: Forbidden Error message: Invoked API requires Protected API
access in application-only context when not using Resource Specific
Consent. Visit
https://learn.microsoft.com/en-us/graph/teams-protected-apis for more
details.
GET
https://graph.microsoft.com/v1.0/chats/29:13qY8hmfkJinH9-v7rYKjCNFHYFJXKbjqR-NyzyKzL694npelHJoq5HrVtqJLRYo79OYeHGQq-bhtJM5N-yKXyQ/messages
SdkVersion : graph-java/v5.6.0
I am at a loss to explain why querying channels works fine but querying chats fails.
Any help gratefully appreciated!
This is a protected API and in order to use it you will first need to make a formal request to Microsoft Graph, asking for permissions to use the API without any user interaction
Here is the list of protected APIs. You need to fill this form to get the required permissions.
To request access to these protected APIs, complete the following
request form. We review access requests every Wednesday and deploy
approvals every Friday, except during major holiday weeks in the U.S.
Submissions during those weeks will be processed the following
non-holiday week.
The other option would be to use delegated flow.

The Microsoft graph endpoints failed with 5xx error randomly. why?

Request URL:https://graph.microsoft.com/v1.0/me/messages
Some times when I try to get messages with this query (top > 10) /v1.0/me/messages?$top=20, it fails and responds 503-UnknownError. But with this one $top=10, it always works.
Another confusing part is that this problem only occurred with a specific personal-outlook(not organizational) email address!!!
There is another similar problem too:
Sometimes, When I try to initiate the graph-client I get this error:
AADSTS70012: A transient error has occurred. Please try again
The other problem:
It's possible to create a new outlook account with valid Gmail-address, then you can log in to your outlook account with a username equal to a valid Gmail-address.
This is what I got from M-Graph-Console:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"displayName": "FirstName LastName",
"surname": "LastName",
"givenName": "FirstName",
"id": "286526a26a78f1a8",
"userPrincipalName": "any-valid-gmail-username#gmail.com",
"businessPhones": [],
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null
}
Check the mail property: it's null !!
You can not fetch randomly-genrated-username#outlook.com from graph-APIs!!
As I know the only way to find the real outlook email address is to fetch sent messages and parse both of from and sender properties.
Now you can send an email to any other valid Gmail address from your outlook dashboard, but it's not possible to reply it or send email to your new outlook account address (any-valid-gmail-username#gmail.com).
I'm not sure this is a bug or feature but it does not seem to be a good idea!
503 is a server side issue. Possibly a bug on the Graph API side. If you are able to reproduce it you should open a support ticket.
You should not be able to add a user (in Office365 Business) for an unverified domain. Obviously you won't be able to verify gmail.com as your domain. Therefore, if you have managed to find a scenario where you are able to create an #gmail.com account in Office365, I suggest you report it to Microsoft as a bug (possibly "security" severity).

Change Plan's Owner/Group - Required Permissions missing

When attempting to update a Planner Plan's group using PATCH, updates to the owner field fail with 403. The attempt is being made using the Graph Explorer (and also fails using PostMan).
An attempt to change the title field succeeds (no issue with permissions, using eTag, etc.) for the call.
The documentation indicate Groups.ReadWrite.All are required, and that the owner can only be changed by the Group Owner, but the account being used is the owner of the unified group.
Are there any additional permissions required?
Example info:
If-Match: W/"JzEtUGxhbiAgQEBAQEBAQEBAQEBAQEBARCc="
PATCH https://graph.microsoft.com/beta/planner/plans/Vk_27olfLESU6vWJNgzVT2UABrBj
BODY:
{
"title": "Updated plan with new group owner",
"owner": "0ab4b389-7c7c-4757-ac75-2ba5de8c8197"
}
RESPONSE:
{
"error": {
"code": "",
"message": "You do not have the required permissions to access this item, or the item may not exist.",
"innerError": {
"request-id": "7696b78c-474f-4fc0-bba9-4554bfe303c4",
"date": "2018-11-14T16:50:59"
}
}
}
The documentation in this case is misleading/incorrect (it is trying to say the field owner can only be updated by the principal identified by the value of the owner field, which is a group that cannot actually perform operations, not the owner of that group). We'll update the documentation and clarify.
We currently do not allow Plans to be moved between groups, as some of the related items mostly live within the group itself (e.g. membership, comments, document links). We'd like to understand your scenario better to see if we could support it, so please provide feedback on https://planner.uservoice.com.

Simplest way to display upcoming events from a public Google calendar?

I have a public calendar on the site. I need to display upcoming events on the front page.
After spending the whole day on this I am about to give up. Google is disabling older API there is no documentation or anything. I am parsing the RSS feed:
http://www.google.com/calendar/feeds/.../public/basic?futureevents=true
And grabbing feed/entry/update and feed/entry/title from XML. I read somewhere that futureevents will display upcoming events. However I get the list of older events.
Okay, then I tried to use API V3 https://developers.google.com/google-apps/calendar/v3/reference/events/list
Here is the responce I get:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
I jumped through more hoops and installed their ruby code sample as I need this for Rails site.
It sort of worked but it requires user to confirm access to their own calendar. I don't need that. Plus it seems that authentication only valid for a day.
The said calendar is public. It is being displayed in iframe on the site with xml and ical feed. All I need is to parse upcoming events to show them on front page.
What is the way to do it without authorization and other ridiculous stuff?
//This works for me! js:
var request = gapi.client.calendar.events.list({
'calendarId': 'xxx',
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 1,
'orderBy': 'startTime'
});
request.execute(function(resp) {}
Use this wizard to create a new API project: https://console.developers.google.com/start/api?id=calendar Then click on "create new key" (choose server key). Then do a request to https://www.googleapis.com/calendar/v3/calendars//events?key={YOUR_API_KEY} replacing the email and api key with your values.

Resources