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

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.

Related

Get all user properties from Microsoft graph

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.

API Connect - 500 error when including basic Javascript

I'm trying some basic API Connect tutorials on IBM's platform (running locally using loopback) and have got completely stuck at an early point.
I've built a basic API service with some in-memory data and setter / getter functions. I've then built a separate API which takes two GET parameters and uses one of my getter functions to perform a search based on two criteria. When I run it, I successfully get a response with the following JSON object:
[{"itemId":1,"charge":9,"itemSize":2,"id":2}]
I've then tried to add a piece of server logic that modifies the response data - at this point, I'm just trying to add an extra field. I've added a Javascript component in the Assemble view and included the following code (taken from a tutorial), which I thought should modify the message body returned by the API while still passing it through:
//APIC: get the payload
var json = apim.getvariable('message.body');
//console.error("json %s", JSON.stringify(json));
//same: code to inject new attribute
json.platform = 'Powered by IBM API Connect';
//APIC: set the payload
//message.body = json;
apim.setvariable('message.body', json);
Instead of getting an extra JSON parameter ("platform"), all I get is a 500 error when I call the service. I'm guessing that I'm doing something fundamentally wrong, but all the docs suggest these are the right variable names to use.
You can't access json.platform but at that point json variable is json type. Are you sure that you can add a property to a json type variable if your json object lacks of that property? I mean: What if you first parse the json variable of json type to a normal object, then add new property, and finally stringify to json type again for body assigning purposes?
var json = JSON.parse(apim.getvariable('message.body')); //convert to normal object
json.platform = 'Powered by IBM API Connect'; //add new property
apim.setvariable('message.body', JSON.stringify(json)); //convert to json again before setting as body value
You need to get the context in some determined format, and in this function do your logic. For example if your message is in json you need to do:
apim.readInputAsJSON(function (error, json) {
if (error)
{
// handle error
apim.error('MyError', 500, 'Internal Error', 'Some error message');
}
else
{
//APIC: get the payload
var json = apim.getvariable('message.body');
//console.error("json %s", JSON.stringify(json));
if(json){
//same: code to inject new attribute
json.platform = 'Powered by IBM API Connect';
//APIC: set the payload
//message.body = json;
apim.setvariable('message.body', json);
}
}
});
Reference:
IBM Reference
You have the message.body empty, put a invoke/proxy policy before your gateway/javascript policy for example.

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/

SAPUI5 Create OData entity with dates - generates incorrect request payload that ends in CX_SXML_PARSE_ERROR

We are trying to create an entity that has date attributes via an odata service. Backend is an sap system. This entity has only 3 key attributes plus a bunch of other attributes. We have identified that dates in the keys are the root cause of the problem.
Keys:
Pernr type string,
begda type datetime
endda type datetime.
The code below, (which does not work), has been severely simplified when trying to troubleshoot the issue. At the moment, it reads an entity from an entity set and immediately tries to create one with exactly the same data.
Code:
var oODataModel = new sap.ui.model.odata.ODataModel("/sap/opu/odata/sap/Z_PERSONAL_DATA_SRV/");
//Test entity to be saved
var entity = null;
//Handler for read error
var handleReadE = function (oEvent){
alert("error");
};
//Handler for read success
var handleRead = function (oEvent){
//Get the data read from backend
entity = oEvent.results[0];
//Try to create a new entity with same data
oODataModel.create('/PersDataSet', entity, null, function(){
alert("Create successful");
},function(oError){
alert("Create failed", oError);
});
};
oODataModel.read("/PersDataSet", null, [], true, handleRead, handleReadE);
In the gateway error log, an xml parsing error appears. In this log, we can see the request data and it can be seen that the dates are transported with String types. These dates are defined in the service as DateTimes so the request is rejected.
Example:
<m:properties>
<d:Pernr m:type="Edm.String">00000001</d:Pernr>
<d:Endda m:type="Edm.String">9999-12-31T00:00:00</d:Endda>
<d:Begda m:type="Edm.String">1979-05-23T00:00:00</d:Begda>
When the entity is read, the backend does not send any type information. It sends like the following example:
<m:properties>
<d:Pernr>72010459</d:Pernr>
<d:Endda>9999-12-31T00:00:00</d:Endda>
<d:Begda>1876-07-21T00:00:00</d:Begda>
And, indeed, if we try to save the same info without the type=".." it works. So the problem are the incorrect types ODataModel.create adds to the xml.
My question is:
Can I tell ODataModel.create to not add this type info? It is not doing a good job inferring the types.
Can anyone share an example reading and writing dates through odata?
Thank you very much in advance.
the data returned from oODataModel.read is raw, before you post you need to parse it
var handleRead = function (oEvent){
//Get the data read from backend
entity = oEvent.results[0];
var newEntity = jQuery.extend({},entity);
delete newEntity.__metadata;
newEntity.Begda = new Date(entity.Begda);
newEntity.Endda = new Date(entity.Endda);
//Try to create a new entity with same data
oODataModel.create('/PersDataSet', newEntity, null, function(){
why not use json instead of xml?
Thanks all for the help.
We got this working accounting for the following:
The problem of the wrong types appended to the attributes comes from the read itself. The object returned by read has a __metadata attribute which describes the values. In this object the dates are set with type=edm.string, even when the service says they are DateTime. To me this is a bug of the .read function.
When trying to use the same object to save, create sees the __metatada on the entry and uses those values, producing type edm.string type for the dates. This caused the request to be rejected. Manually changing these __metadata.properties...type to Edm.DateTime makes it work.
In the end, we did the following:
Dates are parsed manually from the Odata response, creating a js Date
object from the strings in format "yyyy-mm-ddT00:00:00", to make it work with control bindings. When we want to save, the reverse is done.
The object to be created is a new object with
only the attributes we care (no __metadata)

"A Currency is Required if a Value Exists in a Money Field" - Default Currency Is Set

I'm having a serious issue with one of my managed solutions, I have a custom workflow which creates a bunch of custom entities and these custom entites have several money fields on them.
Here's what happens when I run my workflow:
Custom workflow goes into 'waiting' state and throwns a null reference exception.
Several entities have been created, but one of them has null vlaues in the money fields (they default onLoad to £0.00).
When I open this record, I get the error message mentioned in the tite of this post.
A few things you should know:
The default currency is set in the system (to Pound Sterling).
I have tried to following JavaScript in the OnLoad() event of my form as has been suggested elsewhere on the web:
var lookupData = new Array();
var lookupItem= new Object();
lookupItem.id = "{7bCA916E76-FA28-E211-8C7C-0800273EE9D1}";
lookupItem.entityType = "transactioncurrency";
lookupItem.name = "Pound Sterling";
lookupData[0] = lookupItem;
Xrm.Page.getAttribute("transactioncurrencyid").setValue(lookupData);
If this JS is on thje form and I attempt to create a new record I get the error:
Error: 'Xrm.Page.getAttribute(..)' is null or not an object
Can anyone advise as to what it is I have to do here?
This error only occurs on the live system, not in the development environment.
That's because the ID of the currency you are trying to set is different in the live environment than in the dev environment.
You need to get the ID of the "Pound Sterling" currency from the Live environment and put it where "insert here":
lookupItem.id = "--insert here--";
lookupItem.entityType = "transactioncurrency";
lookupItem.name = "Pound Sterling";
Then it will work.

Categories

Resources