Is there any Microsoft API to get the group Id from channel id? - microsoft-graph-teams

I have list of channel id of particular group, I want the group Id and name from channel Id.
Is there any such api in Microsoft graph api?

This is not possible with the Graph API. This has been confirmed by Microsoft.
However, when you are working with a channel object and have its id, you usually also have an id of the team it belongs to - for example in the bot framework, it's part of the activity's channelData.
Be prepared not to always have this information, since not all channels of conversations might be Teams channels! But let's assume you're working with Teams channels exclusively.
The id of the team is also the id of its implicitly-generated General channel. You can distinguish that one by the id format 19:…#thread.tacv2 - a normal channel has a hex string whereas the team itself has a longer base62 string and includes a -. Without this id, you're basically lost.
Unfortunately, this team id is still mostly useless. It is not the UUID that you use in the Graph API for the Team resources (at /teams/{team-id}), Channel resources (at /teams/{team-id}/channels/{channel-id}) and Group resources ("Every team is associated with a Microsoft 365 group. The group has the same ID as the team - for example, /groups/{id}/team is the same as /teams/{id}").
Rather, it is the value of the internalId property of the team. It is also the main part of its webUrl. So how to get the groupId/teamId UUID of the team from its internalId string?
The best you can do with the Graph API is to get a list of all teams (either via /groups or via /teams, or at least the teams that you are part of or are otherwise associated with), and then for each team fetch the whole object by id and compare its internalId with the channel id / internal team id that you are looking for.
Unfortunately, /teams?$filter=internalId eq '19%3A…%40thread.tacv2' only gives the response Filter on property 'internalId' is not supported. :-/
However, looking outside of the Graph API, there is the Bot Framework REST API (part of Azure Bot Services), which does provide an undocumented endpoint. I discovered it via this StackOverflow answer, which shows that the Bot Framework SDK for .NET does have a method to fetch this information: FetchTeamDetailsWithHttpMessagesAsync. It takes an internal teamId (which is not distinguished in the documentation) and returns a TeamDetails object which contains an AadGroupId property: "Azure Active Directory (AAD) Group Id for the team." That's the one we want!
And since the code is open-sourced on Github, we can find its implementation:
// Construct URL
var baseUrl = Client.BaseUri.AbsoluteUri;
var url = new System.Uri(new System.Uri(baseUrl + (baseUrl.EndsWith("/", System.StringComparison.InvariantCulture) ? string.Empty : "/")), "v3/teams/{teamId}").ToString();
url = url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));
This works similar in JavaScript/TypeScript, except the SDK doesn't expose the method itself. Turns out I need to look better, it's available as TeamsInfo.getTeamDetails (working from the current TurnContext) or new Teams(client).fetchTeamDetails. Looking at its implementation, it uses
const fetchTeamDetailsOperationSpec: msRest.OperationSpec = {
httpMethod: 'GET',
path: 'v3/teams/{teamId}',
…
};
I'll leave my custom implementation up for posterity still, it has slightly more accurate types:
const SERVICE_URL = 'https://smba.trafficmanager.net/…/';
async function fetchTeamDetails(teamInternalId: string): Promise<TeamDetails> {
const adapter = new BotFrameworkAdapter({
…
});
const client = adapter.createConnectorClient(SERVICE_URL);
const response = await client.sendRequest({
method: 'GET',
url: `${SERVICE_URL}/v3/teams/${encodeURIComponent(teamInternalId)}`,
});
return response.parsedBody as TeamDetails;
}
/** Details related to a team. Modelled after https://learn.microsoft.com/en-us/dotnet/api/microsoft.bot.schema.teams.teamdetails (which is lacking `tenantId` though) */
interface TeamDetails {
/** Unique identifier representing a team. */
id: string;
/** Tenant Id for the team. */
tenantId: string; // UUID
/** Azure Active Directory (AAD) Group Id for the team. */
aadGroupId: string; // UUID
/** type of the team */
type: 'standard' | 'sharedChannel' | 'privateChannel';
/** Name of team. */
name: string;
/** Number of channels in the team. */
channelCount: number;
/** Number of members in the team. */
memberCount: number;
}

Reverse is possible.
If you have Group/team id, can get channels. But no API to get Group id based on Channel id.

Related

In CyberSource, how do you use a credit card flexible token with payments API?

Trying to get my head around how CyberSource works..
In CyberSource, the Secure Acceptance Flexible Token API can be used to tokenize a credit card on client-side for processing for future processing.
That part is easy enough. There's plenty of documentation and a few packages that do it.
How do I then use that token to create a charge with the CyberSource Payments API ?
All examples i've found show how to tokenize the card on client, and how to charge an untokenized card (i.e. card data is sent to server) but I can't find an example that show how to use the Flexible token to create a charge (or pre-auth).
In most other gateways like Stripe, it's pretty clear in documentations but CyberSource doesn't seem to provide that.
Am I missing something?
I'm using Node but happy with solutions in other languages.
https://developer.cybersource.com/api-reference-assets/index.html#flex
https://developer.visa.com/capabilities/cybersource/reference
Ok, thanks to #rhldr for pointing out to this particular documentation.
The answer is in this page https://developer.cybersource.com/api/developer-guides/dita-flex/SAFlexibleToken/FlexMicroform/GetStarted.html under "Using the Token".
Note: Some of the other documentations (not sure why they have some many variations of the docs), like this one don't mention this at all.
See the RESTPaymentAPI section. It needs to be provided as a customerId field.
"paymentInformation": {
"customer": {
"customerId": "7500BB199B4270EFE05340588D0AFCAD"
}
}
Here's a minimal example of how it can be implemented on the API side
var cybersourceRestApi = require('cybersource-rest-client');
var configuration = require('./cybersource/config.js');
var configObject = new configuration();
var instance = new cybersourceRestApi.PaymentsApi(configObject);
var clientReferenceInformation = new cybersourceRestApi.Ptsv2paymentsClientReferenceInformation();
clientReferenceInformation.code = 'test_payment';
var processingInformation = new cybersourceRestApi.Ptsv2paymentsProcessingInformation();
processingInformation.commerceIndicator = 'internet';
var amountDetails = new cybersourceRestApi.Ptsv2paymentsOrderInformationAmountDetails();
amountDetails.totalAmount = "100.00";
amountDetails.currency = 'USD';
var orderInformation = new cybersourceRestApi.Ptsv2paymentsOrderInformation();
orderInformation.amountDetails = amountDetails;
var paymentInformation = new cybersourceRestApi.Ptsv2paymentsPaymentInformation();
// THIS IS THE IMPORTANT BIT
var customer = new cybersourceRestApi.Ptsv2paymentsPaymentInformationCustomer()
customer.customerId = token
paymentInformation.customer = customer
var request = new cybersourceRestApi.CreatePaymentRequest();
request.clientReferenceInformation = clientReferenceInformation;
request.processingInformation = processingInformation;
request.orderInformation = orderInformation;
request.paymentInformation = paymentInformation;
if (!authoriseOnly) {
request.processingInformation.capture = true;
}
Code based on the CyberSource nodejs REST samples: https://github.com/CyberSource/cybersource-rest-samples-node
More info. Once you know where to look, it's actually explained in a few places.
Example, go to https://developer.cybersource.com/cybs-dev-api-ref/index.html#payments-process-a-payment, expand the "REQUEST FIELD DESCRIPTION" underneath and go to
customer
.. customerId
Unique identifier for the customer's card
and billing information.
When you use Payment Tokenization or Recurring Billing and you include
this value in your request, many of the fields that are normally
required for an authorization or credit become optional.
NOTE When you use Payment Tokenization or Recurring Billing, the value
for the Customer ID is actually the Cybersource payment token for a
customer. This token stores information such as the consumer’s card
number so it can be applied towards bill payments, recurring payments,
or one-time payments. By using this token in a payment API request,
the merchant doesn't need to pass in data such as the card number or
expiration date in the request itself.
See "Payment Tokenization," page 222, and "Recurring Billing," page
225.

How to get Google UserId from active user session in App Maker?

Is there a way to get "User Google Id" from the session in App Maker. In the documentation its only mentioned how to retrieve the email of the logged in user Session.getActiveUser().getEmail() but no where it says how to get the id. I need this because the user email might sometimes changes. So I need the user id to keep track of users and related permission tasks. Or is there something I'm missing out here in how this should be implemented.
Yet an easier way to find Google Id simply using the Directory model. Although its mentioned in documentation that there is a way to get current signed in user id ( which is Google Id), its not clearly stated how - maybe documentation could be improved here. Another problem is that in many occasions the email of current active user is referred to as the id for example in deprecated method Session.getActiveUser().getUserLoginId(). Anyways this is a proper way to get the id.
var query = app.models.Directory.newQuery();
query.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
var result = query.run();
var GoogleId = result[0]._key;
So with this GoogleId you can safely relate different models with each other and not worry that database integrity might break if an already referenced user email is changed.
Relating the different models could be done simply by creating a model that acts as a wrapper model around the Directory model and storing GoogleId in it. Then linking that model to other models where you want to track user related data because unfortunately we can not directly link The Directory Model to other models.
A team member has figured it out. This should be done using Apps Script - which works within App Maker environment using server side script.
var GoogleUser = (function (){
/**
*
* #param {string} email
*/
function getUserObjByEmail(email){
// Same as using AdminDirectory class.
var apiUrl = "https://www.googleapis.com/admin/directory/v1/users/"+email+"?fields=id";
var token = ScriptApp.getOAuthToken();
var header = {"Authorization":"Bearer " + token};
var options = {
"method": "GET",
"headers": header
};
var response = JSON.parse(UrlFetchApp.fetch(apiUrl, options));
return response;
}
/**
*
* #param {string} email - User email.
*/
function getIdByEmail(email){
return getUserObjByEmail(email)['id'];
}
var publicApi = {
getIdByEmail: getIdByEmail
};
return publicApi;
})();
Note that using var apiUrl = "https://www.googleapis.com/admin/directory/v1/users/"+email+"?fields=id"; is not going to be asynchronously called because its already happening in the server.
Is this a dup of this question?
I think this will solve your problem, even though it's a bit of a hack.

Adaptive User Management

I have built a review app based on Google's "people viewer" template that allows managers to create and edit reviews for their direct reports.
The app contains the directory model as well as three roles: Admins, HR, EndUsers.
The app contains a user settings model that allows to create and store user settings similar to the "people skills" template.
The app contains a review model that will contain the reviews for every employee. As one employee can have several reviews, this will be a one-to-many relation, either linked to directory model or user settings model.
The reviews should be readable by managers chain of manager. For this I have created a server script, assuming that the EmployeeEmail will be additionally stored in the review. But maybe there is a better alternative?
function getDirectReportsChainForUser_(query) {
var userQuery = app.models.Directory.newQuery();
userQuery.filters.PrimaryEmail._equals = query.parameters.PrimaryEmail;
userQuery.prefetch.DirectReports._add();
userQuery.prefetch.DirectReports.DirectReports._add();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var user = users[0];
var directs = user.DirectReports;
var records = [];
for (var i = 0; i <= directs.length; i++) {
records.push(directs[i].PrimaryEmail);
}
// The following lines are based on the asumption that the EmployeeEmail
// will be stored in the review in case that there is no better alternative.
//The question that then remains is how to recursively add the DirectReports
//of the DirectReports to the array???
var reviewQuery = app.models.Reviews.newQuery();
reviewQuery.filters.EmployeeEmail._in = records;
return reviewQuery.run();
}
The manager should be able to define whether one or more of his deputies can read the reviews for his unit, too. My idea was to solve this issue through a many-to-many relation between the directory and review model, but I am not sure how to implement it?
Furthermore, once a manager or his deputy departures, it should be possible for the Admin to dissolve the connection and to reconnect the reviews to a successor. Therefore I was thinking about integrating a multiselect in the admin page. Would this be feasible?
Here I see at least two distinct questions:
is there better way to associate directory model's record and ordinary data model than just adding primary email field to the data model
Nope, at this time it is not possible to establish relations between data (SQL/Drive Tables) and directory models.
how to recursively get all direct reports for a user
App Maker's Directory Model is a wrapper on top of G Suit Admin SDK's Directory API that exposes just a small subset of its powerful features. When you add Directory Model App Maker automatically plugs in correspondent Apps Script advance service:
Since we already have configured Directory API we can unleash its full power and easily fetch all manger's subordinates with a single call (or multiple if you have a need to support paging). In order to do that we will use Users.List API method with managerId query parameter (the only one that allows us to query all subordinates down the tree). Here are reference for the minimal set of search query parameters quoted from the full search documentation (without those parameters query would not work or wouldn't work in a way we need):
managerId: The ID of a user's manager either directly or up the management chain.
domain: The domain name. Use this field to get fields from only one domain. To return all domains for a customer account, use the customer query parameter instead. Either the customer or the domain parameter must be provided.
viewType: Whether to fetch the administrator-only or domain-wide public view of the user. For more information, see Retrieve a user as a non-administrator (admin_view is default value so we need to override it with domain_view).
query: Query string for searching user fields. For more information on constructing user queries, see Search for Users.
/**
* Fetches all reviews associated with all subordinate employees (both direct
* and indirect reports).
*/
function getAllReportsEmails(managerId) {
var emails = [];
var result = AdminDirectory.Users.list({
domain: 'ENTER HERE YOUR DOMAIN (exapmle.com)',
query: 'managerId=' + managerId,
viewType: 'domain_public',
maxResults: 100
});
if (result.users) {
emails = result.users.map(function (user) {
return user.primaryEmail;
});
}
return emails;
}
/**
* Fetches all reviews associated with all subordinate employees (both direct
* and indirect reports).
*/
function getAllReportsReviewsForManager_(query) {
var userQuery = app.models.Directory.newQuery();
// For better security I would recommend to use
// Session.getActiveUser().getEmail() instead of parameter
// passed from the client.
userQuery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var manager = users[0];
var managerId = manager._key;
var allReportsEmails = getAllReportsEmails(managerId);
var reviewQuery = app.models.Reviews.newQuery();
reviewQuery.filters.EmployeeEmail._in = allReportsEmails;
return reviewQuery.run();
}
Pavel, I tried to integrate the ideas you gave me into one server script that returns an array of the manager and his whole subordinate chains (direct reports + indirect reports), so that I can use it whenever needed. I turned into a recursive function to get the direct reports and indirect reports on the next lower level. Is there a way to get the whole chain?
function getSubordinatesChainForUser(query) {
var userQuery = app.models.Directory.newQuery();
userQuery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
userQuery.prefetch.DirectReports._add();
userQuery.prefetch.DirectReports.DirectReports._add();
var users = userQuery.run();
if (users.length === 0) {
return [];
}
var userEmails = users.map(function(manager){
var employeeEmails = manager.DirectReports.map(function(employee){
return employee.PrimaryEmail;
});
return manager.PrimaryEmail + ',' + employeeEmails;
});
return userEmails;
}

database schema for frequent relations of relations queries

My app is in a way a social sharing platform which heavily relies on searching through friend's friend / 2nd degree connections / relations-of-relations.
Thus I am seeking help on the best schema for this app.
App Setup Each user has a share-able possession called a "Thing" and is living at one "Location". Each user has his/her friends & these friends have their own friends (with some overlap between friends).
Use case
When a user needs a "Thing" (either because he runs out of it or is at different location), he simply performs a location based search in the app. The search returns list of friends in that location who have the "Thing" and also returns list of friend's friends who also have that "Thing". The user then messages his direct friends through the app's Chat functionality. But more importantly, user asks his friends to forward his "message-request" to their friends who match the criteria (location & Thing).
Search Query for Database - expected results This relations-of-relations search should return a two dimensional array: [a friend] [list of this friend's friends who match the search criteria]
Data Model User is modeled in as Person class. It is pretty simple and bare bone.
Additional expectations from Database schema:
Store chats and store profile photos
Note: Im developing in Angular4.
Your help is greatly appreciated!
// List of things of type string (in total 12)
import { Thing } from './thing.enum';
// Gender enum = MALE, FEMALE, UNKNOWN
import { Gender } from './gender.enum';
export class Person {
public firstName: string;
public lastName: string;
public age: number;
public gender: Gender;
public phone: number;
// Users share Things
public thing: Thing; // User can only have 1 of 12 possible "Things"
public city: string;
public country: string;
public countryISO: string;
public postalCode: string;
public placeID: string; // Google Places API identifier
}

Firebase Chat - Get messages just for 2 users [duplicate]

In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.
I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.
But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.
Please, if you want to help me, give me an example in javascript using a firebase url/array.
Thank you!
A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.
You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:
var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
user1 = "Eusthace";
user2 = "Frank";
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:
"userChatrooms" : {
"Frank" : {
"Eusthace_Frank": true
},
"Eusthace" : {
"Eusthace_Frank": true
}
}
If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.
This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:
{
"rules": {
"chatrooms": {
"$chatroomid": {
".read": "
root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
"
}
}
}
}
In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.
Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).
We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.
Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.
Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.
import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)

Resources