Get all user properties from Microsoft graph - javascript

We have an application which has used a local AD to fetch user info. Some customers want to move to the cloud and are using Azure AD. We extended the app to sign users in via owin and now we're fetching users via Microsoft Graph.
However from Microsoft Graph we do not get full user profiles. We want to fetch all properties on users, not just the basic ones.
var client = new RestClient(string.Format("https://graph.microsoft.com/v1.0/users/{0}", userEmail));
request = new RestRequest();
request.Method = Method.GET;
request.AddHeader("Authorization", _token.Token);
var reponse = client.Execute(request);
This only gives me some information though, for example I don't get 'Department' from this.
Is it possible to configure in azure what should be returned here, if so then where? Or do I need something other than /users/?
Different customers might have different special properties that need to be fetched. So the best solution would be to have an endpoint to call and get everything, including special properties not standard in azure ad. After that I can parse it on my side. Is this possible?
The app has permission to read both basic and full profiles. Do I need something more?

That's the normal behaviour of Graph API, see documentation here and this extract:
By default, only a limited set of properties are returned (
businessPhones, displayName, givenName, id, jobTitle, mail,
mobilePhone, officeLocation, preferredLanguage, surname,
userPrincipalName).
To return an alternative property set, you must specify the desired
set of user properties using the OData $select query parameter. For
example, to return displayName, givenName, and postalCode, you would
use the add the following to your query
$select=displayName,givenName,postalCode
You have to specify all fields in the select, as $select=* will only output the key fields in Graph API implementation.
So you will not be able to get what you ask (variable custom fields).
More info on the fields of User can be found here

User user = await graphServiceClient
.Users[emailId]
.Request()
.Select(aadUser => new
{
aadUser.Id,
aadUser.UserPrincipalName,
aadUser.DisplayName,
aadUser.GivenName,
aadUser.Surname,
aadUser.City,
aadUser.MailNickname,
aadUser.UserType
})
.GetAsync()
.ConfigureAwait(false);

As already stated by NicolasR, you must list all the fields you want to retrieve by using the "$select" parameter; if you want, instead, to retrieve the custom fields, you can either add them to the previous parameter (if you know their names) or you can use "$expand=extensions"

function getGraphDataAdvanced($authToken, $urlGraph){
$url = $urlGraph + '&$count=true'
$data = (Invoke-RestMethod -Headers #{
Authorization = "Bearer $($authToken)"
ConsistencyLevel = "eventual"
} -Uri $url -Method Get)
$dataList = #()
$dataList += $data.value
$url = $data.'#Odata.NextLink'
while ($null -ne $url){
Write-Warning 'Retreiving Next Page'
$data = (Invoke-RestMethod -Headers #{
Authorization = "Bearer $($authToken)"
ConsistencyLevel = "eventual"
} -Uri $url -Method Get)
$dataList += $data.value
$url = $data.'#Odata.NextLink'
}
return $dataList
}
getGraphDataAdvanced $authToken 'https://graph.microsoft.com/beta/users? $expand=extensions'

Using the Microsoft Graph Explorer, I've been able to find all available properties for a user:
Go to "Groups"
Select "list all groups in my organization"
Change the query to filter by a group you know and expand members: https://graph.microsoft.com/v1.0/groups?$filter=mail eq 'aGroup#company.com'&$expand=members
Now you'll see all the available properties for the users.

I've been trying to find a way to get all Azure AD properties of objects via Powershell MSGraph cmdlets without it truncating at the right edge of the console.
I've discovered that Format-Custom triggers vomiting of (apparently) all properties of an object in a huge, alphabetical, indented, and bracketed list.
Get-MgUser -filter "startswith(userprincipalname, 'username')" | format-custom
The formatted properties of a newly created and unused user account in Azure AD is 13217 lines long.

Related

Need Help to implement Tincan Javascript API

I'm working on tincan JavaScript API. The issue my data format is total change and TinCan have specified a why to pass data along with call. Help me to adjust my data in TinCan Api format. Here is sample data one of my call.
var data = {
"groupId": "groupId",
"groupName": "gNameEncrypt",
"tutorNames": "tutorNames",
"actorNames": "actorNames",
"otherNames": "otherNames"
};
Current what i do i simply decode this data and send it like this.
var actionList = new TinCan(
{
recordStores: [{
endpoint: "http://example.com",
username: username,
password: password,
allowFail: false
}]
});
var action = new TinCan.Agent({
"name": "insert"
});
actionList.getStatements({
'params': {
'agent': action,
'verb': {
'id': $.base64.encode(data)
}
},
'callback': function (err, data) {
console.info(data.more);
var urlref = "http://<?php echo $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . $uriParts[0] . "?" ?>t=" + data.more.TutorToken;
window.location.href = urlref;
}
});
crypt.finish();
});
There are really two parts here:
need to get data into an xAPI (formerly Tin Can) format, and
the code itself.
In depth,
I think you need to take another look at how xAPI is used in general. Data is stored a JSON "Statement" object that has 3 required properties and various other optional ones. These properties often contain complex objects that are very extensible. It is hard to tell from what you've shown what you are really trying to capture and what the best approach would be. I suggest reading some material about the xAPI statement format. http://experienceapi.com/statements-101/ is a good starting point, and to get at least some coverage of all the possibilities continue with http://experienceapi.com/statements/ .
The code you've listed is attempting to get already stored statements based on two parameters rather than trying to store a statement. The two parameters being "agent" and "verb". In this case We can't tell what the verb is supposed to be since we don't know what data contains, I suspect this isn't going to make sense as a verb which is intended to be the action of a statement. Having said that the fact that the "actor" has a value of action is questionable, as that really sounds more like what a "verb" should contain. Getting the statements right as part of #1 should make obvious how you would retrieve those statements. As far as storing those statements, if you're using the TinCan interface object you would need to use the sendStatement method of that object. But this interface is no longer recommended, the recommended practice is to construct a TinCan.LRS object and interact directly with it, in which case you'd be using the saveStatement method.
I would recommend looking at the "Basic Usage" section of the project home page here: http://rusticisoftware.github.io/TinCanJS/ for more specifics look at the API doc: http://rusticisoftware.github.io/TinCanJS/doc/api/latest/

'An undeclared property' when trying to create record via Web API

I am getting an error which I just cannot seem to debug. I am trying to create a custom activity entity via custom HTML/JavaScript web resource.
The user clicks a button and the following params:
var params = {
'rob_faqid#odata.bind': '/rob_faqs(guid-here)',
'rob_source': 180840000,
'subject': 'Signpost',
'actualstart': new Date(),
'actualend': new Date()
};
Are passed to this URL:
https://dynamicsorg/api/data/v8.2/rob_quickactions/
With the following headers:
xhr.setRequestHeader('OData-MaxVersion', '4.0');
xhr.setRequestHeader('OData-Version', '4.0');
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xhr.setRequestHeader('Prefer', 'return=representation');
This gives me a HTTP code of 400 (bad request) and this error message:
An undeclared property 'rob_faqid' which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.
Interestingly, I get this error whether I use an actual GUID or if I put some gibberish in there (suggesting it is not to do with the value being passed in).
I can create the records manually via the standard form.
I am using the odata.bind elsewhere within the same project with no errors.
After a good night's sleep I realised my error. To set the value of a lookup field, you need to use the relationship scheme name, and not the property name.
Once I changed that, all worked fine.
When you want to set the value of a lookup field during the creation or update of a (new) record via the web API, you have to use either the Schema Name or the Logical Name of the lookup followed by the bind annotation.
For default fields like primarycontactid the logical name has to be used (first column in the screenshot).
For custom fields like rob_FaqId the schema name has to be used (second column in the screenshot).
var params = {
'rob_FaqId#odata.bind': '/rob_faqs(guid-here)',
'rob_source': 180840000,
'subject': 'Signpost',
'actualstart': new Date(),
'actualend': new Date()
};
Screenshot of a solution > entities > your entity > fields:
So the general structure to create a new record with an already set lookup field via the web API is this:
{
"logicalorschemaName#odata.bind": "/relatedentitys(guid)" //don't forget the plural 's'
}
Or another example from the official documentation. How to create a new account record and directly assign an already existing contact as the primary contact.
var newAccountRecordObj = {
"name": "Sample Account",
"primarycontactid#odata.bind": "/contacts(00000000-0000-0000-0000-000000000001)"
}
While the accepted answer is correct in this instance, it doesn't seem to be the whole story. In some cases it's necessary to use <logical name>_<entity name>. For instance when doing a POST sharepointdocumentlocations, I had to use:
"regardingobjectid_contact#odata.bind": "/contacts(xxxx)"
"parentsiteorlocation_sharepointdocumentlocation#odata.bind" "/sharepointdocumentlocations(xxx)"
This may be something to do with the fact that those relationships can point to more than one type of entity, but I haven't found any Microsoft documentation about it.

Accessing Other Entities Attributes in Dynamics CRM/365 Forms with javaScript

This function buttonBuzz() works inside the Forms of the Entities Account, Contacts and Leads. But not in the Opportunity form.
Mainly because there is no telephone1 attribute. There is however a Contact entity added with "Quick View" in a section with a telephonenumber inside.
I think it can be accessed with the telephone1 as well just not with Xrm.page
Any ideas how i can grab the attribute from inside the "quick view"?
I dont know if the "Quick view" window is a form of an iFrame. And if it is i have no clue how to access it with the Xrm.Page.getAttribute("telephone1").getValue();
function buttonBuzz(exObj) {
var phoneNumber;
// Here i store the "telephone1" Attribute from the current .page
phoneNumber = Xrm.Page.getAttribute("telephone1").getValue();
if (phoneNumber != null) { **Sends phonenumber** } ...
Quick Views display data from a record selected in a lookup field, in this case a Contact. You can query data from related records using the OData endpoint.
You first need to get the Guid of the record selected:
var contactId = Xrm.Page.getAttribute("parentcontactid")[0].id || null;
You would then need to send a SDK.REST request, passing parameters for the Id of the record (contactId), entityName and the columns:
var entityName = "Contact";
var columns = "Address1_Telephone1, FirstName, LastName";
SDK.REST.retrieveRecord(contactId, entityName, columns, null, function(result) {
// Success, logic goes here.
var address1_Telephone1 = result.Address1_Telephone1;
}, function(e) {
console.error(e.message);
});
As well as your JavaScript file, you would need to include the SDK.REST.js file that is included in the MS CRM SDK download within your Opportunity form libraries.
You can pull that field up from the Contact into the Opportunity by creating a Calculated Field, setting it equal to parentcontactid.telephone1
Put the field on the form, and you'll be able to .getAttribute() it like any other Opportunity field (being Calculated, it updates itself whenever the source changes).

Backbone Sub-Collections & Resources

I'm trying to figure out a Collection/Model system that can handle retrieving
data given the context it's asked from, for example:
Available "root" resources:
/api/accounts
/api/datacenters
/api/networks
/api/servers
/api/volumes
Available "sub" resources:
/api/accounts/:id
/api/accounts/:id/datacenters
/api/accounts/:id/datacenters/:id/networks
/api/accounts/:id/datacenters/:id/networks/:id/servers
/api/accounts/:id/datacenters/:id/networks/:id/servers/:id/volumes
/api/accounts/:id/networks
/api/accounts/:id/networks/:id/servers
/api/accounts/:id/networks/:id/servers/:id/volumes
/api/accounts/:id/servers
/api/accounts/:id/servers/:id/volumes
/api/accounts/:id/volumes
Then, given the Collection/Model system, I would be able to do things like:
// get the first account
var account = AccountCollection.fetch().first()
// get only the datacenters associated to that account
account.get('datacenters')
// get only the servers associated to the first datacenter's first network
account.get('datacenters').first().get('networks').first().get('servers')
Not sure if that makes sense, so let me know if I need to clarify anything.
The biggest kicker as to why I want to be able to do this, is that if the
request being made (ie account.get('datacenters').first().get('networks'))
hasn't be made (the networks of that datacenter aren't loaded on the client)
that it is made then (or can be fetch()d perhaps?)
Any help you can give would be appreciated!
You can pass options to fetch that will be translated to querystring params.
For example:
// get the first account
var account = AccountCollection.fetch({data: {pagesize: 1, sort: "date_desc"}});
Would translate to:
/api/accounts?pagesize=1&sort=date_desc
It is not quite a fluent DSL but it is expressive and efficient since it only transmits the objects requested rather than filtering post fetch.
Edit:
You can lazy load your sub collections and use the same fetch params technique to filter down your list by query string criteria:
var Account = Backbone.Model.extend({
initialize: function() {
this.datacenters = new Datacenters;
this.datacenters.url = "/api/account/" + this.id + '/datacenters';
}
});
Then from an account instance:
account.datacenters.fetch({data: {...}});
Backbone docs on fetching nested models and collections

Django Dynamic Drop-down List from Database

I wanted to develop a Django app and one of the functionalities I'd like to have is dynamic drop-down lists...specifically for vehicle makes and models...selecting a specific make will update the models list with only the models that fall under that make....I know this is possible in javascript or jQuery (this would be my best choice if anyone has an answer) but I don't know how to go about it.
Also, I'd want the make, model, year and series to be common then the other attributes like color, transmission etc to be variables so that one needs only enter the make, model, year, and series only for a new vehicle. Any ideas would be highly appreciated.
The 3 things you mention being common, make, model, year, would be the 3 input values. When given to the server, an object containing the details would be returned to the calling page. That page would parse the object details (using JavaScript), and update the UI to display them to the user.
From the Django side, there needs to be the facilities to take the 3 inputs, and return the output. From the client-side, there needs to be the facilities to pass the 3 inputs to the server, and then appropriately parse the server's response.
There is a REST api framework for Django that makes it rather easy to add the "api" mentioned above -- Piston. Using Piston, you'd simply need to make a URL for that resource, and then add a handler to process it. (you'll still need to skim the Piston documentation, but this should give you an idea of what it looks like)
urls.py:
vehicle_details = Resource(handler=VehicleDetails)
url(r'^vehicle/(?<make>.*)/(?<model>.*)/(?<year\d{2,4}/(?P<emitter_format>[a-z]{1,4}), vehicle_details, name='vehicle_details'),
handler.py:
class VehicleDetails(BaseHandler):
methods_allowed = ('GET',)
model = Vehicles #whatever your Django vehicle model is
def read(self, request, *args, **kwargs):
# code to query the DB and select the options
# self.model.objects.filter()...
# Build a custom object or something to return
return custom_object
This simply sets up the url www.yoursite.com/vehicle/[make]/[model]/[year]/json to return a custom data object in JSON for jquery to parse.
On the client side, you could use jquery to setup an event (bind) so that when all 3 drop downs have a value selected, it will execute a $.get() to the api URL. When it gets this result back, it passes it into the Jquery JSON parser, and gives the custom object, as a javascript object. That object could then be used to populate more drop down menus.
(Big warning, I just wrote the following off the top of my head, so it's not meant to be copy and pasted. It's just for the general idea.)
<script type="text/javascript">
// On document load
$(function() {
$('#dropdown_make').bind('change', checkForValues());
$('#dropdown_model').bind('change', checkForValues());
$('#dropdown_year').bind('change', checkForValues());
});
function checkForValues() {
if ($('#dropdown_make').val() && $('#dropdown_model').val() && $('#dropdown_year').val())
updateOptions();
}
function updateOptions() {
url = '/vehicle/';
url += $('#dropdown_make').val() + '/';
url += $('#dropdown_model').val() + '/';
url += $('#dropdown_year').val() + '/';
url += 'json/';
$.get(url, function(){
// Custom data object will be returned here
})
}
</script>
This is uncanny: Dynamic Filtered Drop-Down Choice Fields With Django
His question:
"Here is the situation: I have a database with car makes and models. When a user selects a make, I want to update the models drop-down with only the models associated with that make. ... Therefore I want to use Ajax to populate the data."
You're not the same guy? :)

Categories

Resources