Planner Plan URL or creating a Planner Tab in Teams - microsoft-graph-api

I've been trying to create a plan using the Graph REST API for .Net and Microsoft Planner. Following the provided documentation, I was able to create a plan. However, I've seen that after creating it, no URL in order to access that plan is provided.
I was wondering if there was a way of getting or constructing this URL having the planId.
Following this, I also want to link the created Plan to a Tab in Microsoft Plan, but could not find anything useful in the documentation. Is there even a way to create a Planner Tab in Ms Teams using the Graph API?

These are the documentation pages for adding tabs:
https://learn.microsoft.com/en-us/graph/api/channel-post-tabs?view=graph-rest-1.0
https://learn.microsoft.com/en-us/graph/teams-configuring-builtin-tabs
The URL for the Planner Tab page is as follows. You'll need to put your plan id there, but other variables in curly braces are part of the URL as variables, and get filled in by Teams when someone is viewing the tab. https://tasks.teams.microsoft.com/teamsui/{tid}/Home/PlannerFrame?page=7&auth_pvr=OrgId&auth_upn={userPrincipalName}&groupId={groupId}&planId=<YourPlanId>&channelId={channelId}&entityId={entityId}&tid={tid}&userObjectId={userObjectId}&subEntityId={subEntityId}&sessionId={sessionId}&theme={theme}&mkt={locale}&ringId={ringId}&PlannerRouteHint={tid}&tabVersion=20200228.1_s
The full request looks like:
{
"displayName": "<Name of the tab>",
"teamsApp#odata.bind" : "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.planner",
"configuration":{
"entityId": "<combined channel and plan id>",
"contentUrl": "https://tasks.teams.microsoft.com/teamsui/{tid}/Home/PlannerFrame?page=7&auth_pvr=OrgId&auth_upn={userPrincipalName}&groupId={groupId}&planId=<Your plan Id>&channelId={channelId}&entityId={entityId}&tid={tid}&userObjectId={userObjectId}&subEntityId={subEntityId}&sessionId={sessionId}&theme={theme}&mkt={locale}&ringId={ringId}&PlannerRouteHint={tid}&tabVersion=20200228.1_s"
}
}
3 values need to be replaced there, the display name, the entity id and the plan id in the URL.
The entity id value looks like tt.c_<channel id>_p_<plan id> For example for Channel ID = ABC, and Plan ID = 123, you'd get literal string tt.c_ABC_p_123

The plans can be accessed to through this API:https://developer.microsoft.com/graph/graph-explorer?request=groups/%7Bid%7D/planner/plans&version=v1.0
Please refer this document:Top Planner API tasks

You should only need the Planner Id and Channel Id to create a Planner tab in Teams.
Here is my complete request body:
{
"displayName": "<Name of the tab>",
"teamsApp#odata.bind" : "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.planner",
"configuration":{
"entityId": "tt.c_<Channel Id>_p_<Plan Id>",
"contentUrl": "https://tasks.teams.microsoft.com/teamsui/{tid}/Home/PlannerFrame?page=7&auth_pvr=OrgId&auth_upn={userPrincipalName}&groupId={groupId}&planId=<Plan Id>&channelId={channelId}&entityId={entityId}&tid={tid}&userObjectId={userObjectId}&subEntityId={subEntityId}&sessionId={sessionId}&theme={theme}&mkt={locale}&ringId={ringId}&PlannerRouteHint={tid}&tabVersion=20200228.1_s",
"removeUrl": "https://tasks.teams.microsoft.com/teamsui/{tid}/Home/PlannerFrame?page=13&auth_pvr=OrgId&auth_upn={userPrincipalName}&groupId={groupId}&planId=<Plan Id>&channelId={channelId}&entityId={entityId}&tid={tid}&userObjectId={userObjectId}&subEntityId={subEntityId}&sessionId={sessionId}&theme={theme}&mkt={locale}&ringId={ringId}&PlannerRouteHint={tid}&tabVersion=20200228.1_s",
"websiteUrl": "https://tasks.office.com/d3ee719b-9e5c-478b-87c9-c4ffbfd27c96/Home/PlanViews/<Plan Id>?Type=PlanLink&Channel=TeamsTab"
}
}
The following attributes need values replaced:
displayName: Tab Title
entityId: Channel Id & Plan Id
contentUrl: Plan Id
removeUrl: Plan Id
websiteUrl: Plan Id

Related

Query string with OData linked service in Azure Data Factory

Setting up a OData linked service on public OData like at:
https://www.odata.org/odata-services/
works fine in the Azure Data Factory. Hard coding a url in the OData linked service to:
https://services.odata.org/TripPinRESTierService
from an associated data set shows the entities: People, Airlines, Airports. Previewing is helpful when setting up linked services and data sources. Would be great if that was doable with Azure Search OData endpoints.
The Azure Search requires that the api-version exist in the query string. A query string can not be added until either a Copy or LookUp activity (see documentation). Previewing in the LookUp seems not to take with it the query and the preview fails with:
Invalid or missing api-version query string parameter
Debug doesn't show the url or header with the api-key from linked service nore the path from data source. But the output does show the query (with the api-version):
{
"source": {
"type": "ODataSource",
"query": "api-version=2021-04-30-Preview",
"httpRequestTimeout": "00:05:00"
},
"dataset": {
"referenceName": "AzureODataSearch",
"type": "DatasetReference",
"parameters": {}
}
}
Anybody?

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.

Microsoft Graph translateExchangeIds not returning the same id as EWS

I am working with both EWS and the Graph API.
I would like to create events (online meetings with skype/teams) in an calendar that is already available via EWS.
To match the calendar to the one available via Graph API i try to use https://learn.microsoft.com/en-us/graph/api/user-translateexchangeids
The calendar i created has this id when returned by the FindFolder call:
<t:FolderId Id="AAMkAGNiY2YxMjY3LTUxYjgtNGI1Yy1hOTM2LTU4MTM5OTZiNjdjYgAuAAAAAABW2gY0kRG1SqggDTNZN6i8AQAPJkKZ1XJkQ6huFmcVa6XaAAGixNZ3AAA=" ChangeKey="..."/>
<t:DisplayName>Test</t:DisplayName>
I create a request to the graph api:
{
"inputIds": [
"AAMkAGNiY2YxMjY3LTUxYjgtNGI1Yy1hOTM2LTU4MTM5OTZiNjdjYgAuAAAAAABW2gY0kRG1SqggDTNZN6i8AQAPJkKZ1XJkQ6huFmcVa6XaAAGixNZ3AAA="
],
"sourceIdType": "ewsId",
"targetIdType": "restId"}
and get the result
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.convertIdResult)",
"value": [
{
"sourceId": "AAMkAGNiY2YxMjY3LTUxYjgtNGI1Yy1hOTM2LTU4MTM5OTZiNjdjYgAuAAAAAABW2gY0kRG1SqggDTNZN6i8AQAPJkKZ1XJkQ6huFmcVa6XaAAGixNZ3AAA=",
"targetId": "AAMkAGNiY2YxMjY3LTUxYjgtNGI1Yy1hOTM2LTU4MTM5OTZiNjdjYgAuAAAAAABW2gY0kRG1SqggDTNZN6i8AQAPJkKZ1XJkQ6huFmcVa6XaAAGixNZ3AAA="
}
]
}
However, if i call https://graph.microsoft.com/v1.0/me/calendars i get a different id
"id": "AAMkAGNiY2YxMjY3LTUxYjgtNGI1Yy1hOTM2LTU4MTM5OTZiNjdjYgBGAAAAAABW2gY0kRG1SqggDTNZN6i8BwBIq5JjIBY-RqWQllrF0GSkAAAAB353AAAPJkKZ1XJkQ6huFmcVa6XaAAGixNowAAA=",
"name": "Test",
Is there a way to match the (ews) calendar i already have to the one returned by the Graph API?
This is a shot in the dark, because I've never dug this deeply into the weeds on the Graph Ids, but you might try calling Graph with the header that selects "immutable ids." I tried to find some details on what this actually means without much luck.
The header is:
request.Header("Prefer", "IdType=\"ImmutableId\"");
HTH, and if not, sorry for guessing.

How do I get a usable email message reference id from headers to use in a Microsoft Graph query?

I am trying to use the references message ID in email headers of a forwarded email in Office 365 to then find the original email that has that message ID.
The references ID in the forwarded email's headers would show something like what's shown below. Note I'm obtaining these headers from Microsoft Graph by adding &$select=internetMessageHeaders to my query using sender/subject to find the forwarded email.
{
"name": "References",
"value": "<CAOLK2SAEzvs=-6Rs5cTbgORNivQVK2AvMJDJT8o+ghx-XhCn7w#mail.gmail.com>"
}
However, if I take this and use it in a query like https://graph.microsoft.com/v1.0/me/messages?$filter=internetMessageId eq '<CAOLK2SAEzvs=-6Rs5cTbgORNivQVK2AvMJDJT8o+ghx-XhCn7w#mail.gmail.com>'
I do not receive any results.
If I hunt down the original email in Microsoft Graph using sender and subject, I see the following for the internetMessageId field for the original email:
{
"name": "Message-ID",
"value": "<CAOLK2SC6s9Me05kYRqeuEHKK8DrQbXx34ScT0AzGOFFLMdRapg#mail.gmail.com>"
}
Does anyone have any ideas on what Office 365 or Microsoft Graph is doing to change CAOLK2SAEzvs=-6Rs5cTbgORNivQVK2AvMJDJT8o+ghx-XhCn7w into CAOLK2SC6s9Me05kYRqeuEHKK8DrQbXx34ScT0AzGOFFLMdRapg? It looks like some sort of encoding, but I haven't been able to reproduce it.
Ultimately I would like to be able to transform what I'm getting in the references ID header such that I can turn around and use that ID to find the original email using Microsoft Graph.
The id is a hashed value that, among other things, includes the path/folder that the message resides in. So if you, for example, move a message (say from Inbox to Archive) then you should expect the id to change.
For tracking a message, regardless of its location, you should use the internetMessageId ($select=internetMessageId) property instead.
I discovered the issue. In my test message-ID I have the characters = and +
These characters must be URL encoded to %3D and %2B before being used in the $filter query. When they are URL encoded, I am able to find the original email using MS Graph.
In Microsoft Graph v1.0 and beta, you can obtain "immutable ids" by sending an additional HTTP header in your API requests:
Prefer: IdType="ImmutableId"
I've put immutable in italics as this is the term Microsoft uses. Be aware of the following caveats, the ID will change if:
The user moves the item to an archive mailbox;
The user exports the item (to a PST, as an MSG file, etc.) and re-imports it into their mailbox;
The user creates a draft which they later send;
You can also convert existing ids to immutable ones by the following request:
POST https://graph.microsoft.com/beta/me/translateExchangeIds
{
"inputIds" :
[
"AQMkAGM2…"
],
"targetIdType" : "restImmutableEntryId",
"sourceIdType" : "restId"
}
Which will give you the response:
HTTP 200 OK
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#Collection(microsoft.graph.convertIdResult)",
"value": [
{
"targetId": "AAkALgAA...",
"sourceId": "AQMkAGM2..."
}
]
}

Getting Issue Type Scheme and Workflow Scheme for JIRA project using REST API

I'm doing an integration for JIRA using REST API 6.2.6. One thing that I need to do is to get Issue Type Scheme and Workflow Scheme for a project.
What I tried:
Issue Type Scheme
The only thing that I can get right now is a list of issue types using /rest/api/2/project/{projectIdOrKey}. I can't see any way of getting an ID of Issue Type Scheme. Looking at API there is no any endpoints for issue type schemes, so I guess it's not possible.
Workflow Scheme
/rest/api/2/project/{projectIdOrKey} doesn't return any information about Workflow Scheme. But there is an endpoint /rest/api/2/workflowscheme/{id}, so that means that it's possible to get ID somehow... At the end I want to get a list of workflows for a project to check transitions for an issue type.
Question
Is there any way to get the data I want? Maybe there is some hidden not documented API?
Note: I'm using only JIRA REST API.
This is what you want.
/rest/projectconfig/1/workflowscheme/{projectIdOrKey}
Latest Jira documentation provides information about the APIs which can be used to fetch details for issuetype scheme and workflow scheme. Below are the APIs which can be used for the same,
Fetching Issue type Scheme for a project Issuetype Scheme API
Rest URL:GET https://your-domain.atlassian.com/rest/api/2/issuetypescheme/project?projectId={projectId}'
Sample Response:
{
"maxResults": 100,
"startAt": 0,
"total": 3,
"isLast": true,
"values": [
{
"issueTypeScheme": {
"id": "10000",
"name": "Default Issue Type Scheme",
"description": "Default issue type scheme is the list of global issue types. All newly created issue types will automatically be added to this scheme.",
"defaultIssueTypeId": "10003",
"isDefault": true
},
"projectIds": [
"10000",
"10001"
]
}
]
}
Fetching workflow scheme configured for a project Workflow Scheme API
REST URl: GET https://your-domain.atlassian.com/rest/api/2/workflowscheme/{id}
Sample Response:
{
"id": 101010,
"name": "Example workflow scheme",
"description": "The description of the example workflow scheme.",
"defaultWorkflow": "jira",
"issueTypeMappings": {
"10000": "scrum workflow",
"10001": "builds workflow"
},
"draft": false,
"self": "https://your-domain.atlassian.net/rest/api/2/workflowscheme/101010"
}
As far as I am aware you can get the correct XML or JSON response from the
REST API:
/rest/api/2/project/{projectIdOrKey}.
Then if you want to find out the information about the workflowscheme you can do this programmatically by using the following information.
If you have an issue that you want to use then you can use it to get the workflowscheme id by doing this this:
ComponentAccessor.getWorkflowSchemeManager().getWorkflowScheme(issue.getProject()).get("id");
Then once you get the id of the workflow scheme e.g. 10, you then can get the scheme generic value as follows:
GernericValue scheme = ComponentAccessor.getWorkflowSchemeManager().getScheme(10);
Now that you have the scheme you then can get all of the workflows that are referenced in the scheme by doing this:
Collection<JiraWorkflow> workflows = ComponentAccessor.getWorkflowManager().getWorkflowsFromScheme(scheme);
Then if you want to get one workflow you will have to use:
workflows.iterator().next():
Also note, that workflows are identified by their name in JIRA as there are no id's in JiraWorkflow.
So that would be the approach I would use if I wanted to find out the workflowscheme information so I could use the id to then use the REST API:
But the main reason that you might not be able to find a workflowscheme is because it is not present in the JIRA's Issue.
Using this HTTP and inputting it into "Postman" as a get request will return all the JSON information.
Using this https://jira.atlassian.com/browse/JRA-25121/project/23 will return all the information for that project. Then using this REST API:
/rest/api/2/workflowscheme/{id}
Using this HTTP get request https://jira.atlassian.com/browse/JRA-25121/workflow/45 will get you the returned XML or JSON workflow information too.

Resources