I am trying to find an example of how to properly instantiate ODataNavigationLink in case it's non-empty. The only code example I've found creates a non-expanded link but doesn't bind it to any data:
//create a non-expanded link for the orders navigation property
writer.WriteStart(new ODataNavigationLink()
{
IsCollection = true,
Name = "Orders",
Url = new Uri("http://microsoft.com/Customer(" +
dataSource.Customers.First().CustomerID + ")/Orders")
});
writer.WriteEnd(); //ends the orders link
So here we specify the link to "Orders". But how do I provide the actual values for the link (in this example the link is a collection but it can also be a single entry). When I was writing the payload manually I was providing "href" attribute with a linked entry ID. I can't figure out how this is done with ODataLib.
The value appear in the 'href' attribute just shows the Url property of ODataNavigationLink, so you can try the following code to set it manually:
//create a non-expanded link for the orders navigation property
writer.WriteStart(new ODataNavigationLink() {
IsCollection = true,
Name = "Orders",
Url = new Uri("http://microsoft.com/Orders(3)") });
writer.WriteEnd(); //ends the orders link
In common, the navigation link should be the source entity url followed by the navigation property,see here, while id should point to the real entry ID.
updated:
According to the latest feedback, you're trying to write the 'Collection of links' as described in section 14.1 of the atom spec. Thus you can try ODataEntityReferenceLinks class:
var referenceLink1 = new ODataEntityReferenceLink { Url = new Uri("http://host/Orders(1)") };
var referenceLink2 = new ODataEntityReferenceLink { Url = new Uri("http://host/Orders(2)") };
var referenceLink3 = new ODataEntityReferenceLink { Url = new Uri("http://host/Orders(3)") };
var referenceLinks = new ODataEntityReferenceLinks
{
Links = new[] { referenceLink1, referenceLink2, referenceLink3 }
};
writer.WriteEntityReferenceLinks(referenceLinks);
and the payload would be something like:
<links xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">
<uri>http://host/Orders(1)</uri>
<uri>http://host/Orders(2)</uri>
<uri>http://host/Orders(3)</uri>
</links>
Related
We are using the ms graph api to post messages to a teams channel from a internal desktop application. The main purpose is to attach images to the message. We upload the image files into the one-drive folder of the channel as shown below.
var uploadProps = new DriveItemUploadableProperties
{
ODataType = null,
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "replace" }
}
};
var session = await graphClient.Drives[driveId]
.Items[parentId].ItemWithPath(fileName).CreateUploadSession(uploadProps).Request().PostAsync(token);
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<DriveItem>(session, fileStream, maxSliceSize);
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(reportAsync);
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
if (uploadResult.UploadSucceeded)
{
return uploadResult.ItemResponse;
}
We then send a message to the channel and attach the images uploaded previously as reference attachments.
var chatMsg = new ChatMessage();
chatMsg.Body = new ItemBody();
chatMsg.Body.ContentType = BodyType.Html;
chatMsg.Body.Content = msg + " " + string.Join(" ", attachments.Select(d => $"<attachment id=\"{parseEtag(d.ETag)}\"></attachment>"));
chatMsg.Attachments = attachments.Select(d => new ChatMessageAttachment()
{
Id = parseEtag(d.ETag),
ContentType = "reference",
ContentUrl = d.WebUrl,
Name = d.Name
});
return await this.graphClient.Teams[teamId].Channels[channelId].Messages
.Request()
.AddAsync(chatMsg, token);
The problem is that the message only shows the names of the attachments with no preview as seen in the message at the bottom. We want to have a preview as seen (top message) when attaching a file within the teams application.
We've tried to set the thumbnailurl property of the attachment to the thumbnail url fetched from the ms-graph api with no success.
We've uploaded a file using the teams application (with preview) and then created an identical message with the same file (same driveitem id) in our application (show's no preview). Then we fetched both messages using the graph api and could not discern any differences between the two besides the message id's ofc.
We've scoured these forums, the ms documentations and even suggestion pages and found nothing.
We have been able to show previews separately in the body of the message referencing the thumbnail urls and in messagecards but ideally we want the preview directly in the attachments.
EDIT
The thumbnail urls seem to expire after 24 hours and are therefor not a great solution.
We managed to solve exactly this problem using the Simple Upload Api, with the added ?$expand=thumbnails query parameter. I haven't tried but the query param ought to work for the endpoint you're using as well.
Pick a size from the ThumbnailSet in the upload response and add it to the body of your message as an image tag. See below:
// channel, file, extractIdFromEtag, message omitted for brevity.
// PUT /groups/{group-id}/drive/items/{parent-id}:/{filename}:/content
const uploadUrl = `https://graph.microsoft.com/beta/groups/${channel.teamId}/drive/items/root:/${channel.displayName}/${file.name}:/content?$expand=thumbnails`;
const res = await this.http.put(uploadUrl, file).toPromise(); // FYI Using Angular http service
const attachment = {
id: extractIdFromEtag(res.eTag),
contentType: 'reference',
contentUrl: res.webUrl,
name: res.name,
thumbnailUrl: res.webUrl
};
const postBody = {
subject: null,
body: {
contentType: 'html',
content: message
},
};
// This is what makes the image show in the message as if posted from teams
postBody.body.content += `<br><br><img src="${res.thumbnails[0].large.url}" alt="${res.name}"/>`;
const messageUrl = `https://graph.microsoft.com/beta/teams/${channel.teamId}/channels/${channel.id}/messages`;
const result = await this.http.post(messageUrl, postBody).toPromise();
// Done
You can also keep adding the attachment as you already do, if you want the original image attached as a file, as well as showing the image preview in the message.
I have developed a powerBI report and deployed the same in Workspace.
From PowerBI interface I viewed the report and am able to add filter like this:
filter=tablename~2Fcolumnname eq 4
The condition is working fine from the interface. But, when I add filter condition in the code of my asp.ner MVC application, filter does not work.
The condition is working fine from the interface.
But, when I add filter condition in the code of my asp.ner MVC application, filter does not work.
My sample code:
var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials);
var reports = await client.Reports.GetReportsInGroupAsync(WorkspaceId);
report = reports.Value.FirstOrDefault(r => r.Id == "aa07v254-8t46-2387-4k53-2725y90n3456");
report.EmbedUrl = report.EmbedUrl + "?filter=tablename/columnname%20eq%204";
Please help me diagnose, why the filter is not working.
You don't need to change the embed url, instead try to use the following example:
// Build the filter you want to use. For more information, See Constructing
// Filters in https://github.com/Microsoft/PowerBI-JavaScript/wiki/Filters.
const filter = {
$schema: "http://powerbi.com/product/schema#basic",
target: {
table: "Geo",
column: "Region"
},
operator: "In",
values: ["West"]
};
// Get a reference to the embedded report HTML element
var embedContainer = $('#embedContainer')[0];
// Get a reference to the embedded report.
report = powerbi.get(embedContainer);
// Set the filter for the report.
// Pay attention that setFilters receives an array.
report.setFilters([filter])
.then(function () {
Log.logText("Report filter was set.");
})
.catch(function (errors) {
Log.log(errors);
});
I have two separate lists of Posts and Authors in my database, each Post containing an authorId to refer to the corresponding author.
The following method helps me retrieve the whole list of Posts by systematically including the name of the author for each Post:
app.factory('NormalizedPosts', function($firebaseArray, FirebaseFactory) {
var PostsWithAuthors = $firebaseArray.$extend({
// override $$added to include author name
$$added: function(snap) {
var record = $firebaseArray.prototype.$$added.call(this, snap);
FirebaseFactory.$getAuthorId( record.authorId ).$loaded(function( authorData ) {
record.authorData = authorData;
});
return record;
},
// ????????
$$updated: function(snap) {
var rec = $firebaseArray.prototype.$$updated.call(this, snap);
var updatedRecord = this.$getRecord(snap.key());
FirebaseFactory.$getAuthorId( updatedRecord.authorId )
.$loaded(function( authorData ) {
rec.authorData = authorData;
});
return rec;
}
});
return PostsWithAuthors;
});
PS: The FirebaseFactory is just a wrapper for firebase methods.
I then call
var list = new NormalizedPosts ( new Firebase(FBURL).child("posts") );
in my controller to get the full list. This works great.
I'm scratching my head with what should go into the $$updated method: When a new Post is added, the list gets updated as expected (through the $$added method). But when there's a change in a Post data (e.g. the post title), my list does not get updated, as I'm currently returning false in the $$updated method.
Question: What should go in the $$updated method so that when theres a change in a Post data, my list gets updated accordingly (and further returns the author's name!). Thanks
I think you're going through too many loops to get something simple done.
If the name of the author is "part of" the article than you should save it:
{
"articles": {
"firebase-uniq-id-1": {
"title": "My Writing Process",
"published": "2016-01-01",
"author": {
"firebase-uniq-id-2": "Ernest Hemingway"
}
}
},
"authors": {
"firebase-uniq-id-2": {
"name": "Ernest Hemingway",
"born": "1899-07-21",
"whatever": "Some Data"
}
}
}
When you'll want to show other details about the author, fetch it.
Edit:
If you still wish to use the extension option, I believe the only thing you're missing there is extending the updateRecord with rec:
FirebaseFactory.$getAuthorId( updatedRecord.authorId )
.$loaded(function( authorData ) {
updatedRecord.authorData = authorData;
angular.extend(updatedRecord, snap.val());
});
Also, in $$updated you need to return a boolean saying if the record changed or not, and not the record itself.
Keep in mind that if you go that path you shouldn't use the default $save() method of $firebaseArray since it will also save the full authorData
$$updated: function(snap) {
// boolean for the $$updated method
var rec = $firebaseArray.prototype.$$updated.call(this, snap);
// existing record as per this
var existingRecord = this.$getRecord(snap.key());
// record as per FB database
var updatedRecord = snap.val();
if ( rec ) {
FirebaseFactory.$getAuthorId( updatedRecord.authorId )
.$loaded(function( authorData ) {
updatedRecord.authorData = authorData;
angular.extend(existingRecord, updatedRecord);
});
} // end if loop
return rec;
}
I'am new in API's & trying to pull user profile from sharepoint i use following code but don't know about servername? domainname? and username?
const string serverUrl = "http://sharepoint.com/";
const string targetUser = "ttgdev-my.sharepoint.com\\testuser1#ttgdev.guru";
// Connect to the client context.
ClientContext clientContext = new ClientContext(serverUrl);
// Get the PeopleManager object and then get the target user's properties.
PeopleManager peopleManager = new PeopleManager(clientContext);
PersonProperties personProperties = peopleManager.GetPropertiesFor(targetUser);
// Load the request and run it on the server.
// This example requests only the AccountName and UserProfileProperties
// properties of the personProperties object.
clientContext.Load(personProperties, p => p.AccountName, p => p.UserProfileProperties);
clientContext.ExecuteQuery();
foreach (var property in personProperties.UserProfileProperties)
{
Console.WriteLine(string.Format("{0}: {1}",
property.Key.ToString(), property.Value.ToString()));
}
Console.ReadKey(false);
Please guide me it will give me the error in
{"The property or field 'UserProfileProperties' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."}
in the following line
clientContext.ExecuteQuery();
Most likely it is related with the format of targetUser variable. PeopleManager.GetPropertiesFor method expects accountName parameter to be specified in the proper format, in case of SharePoint Online it should be specified in claims format, for example:
i:0#.f|membership|jdow#contoso.onmicrosoft.com
For more details about Claims format follow this article.
So, in your case targetUser value should be replaced from ttgdev-my.sharepoint.com\\testuser1#ttgdev.guru to i:0#.f|membership|testuser1#ttgdev.guru
The following example demonstrates how to retrieve user profile picture via CSOM API:
using (var ctx = TokenHelper.GetClientContextWithAccessToken(webUri.ToString(), accessToken))
{
// Get the PeopleManager object and then get the target user's properties.
var peopleManager = new PeopleManager(ctx);
PersonProperties personProperties = peopleManager.GetPropertiesFor(targetUser);
//Retrieve picture property
var result = peopleManager.GetUserProfilePropertyFor(accountName, "PictureURL");
ctx.ExecuteQuery();
Console.WriteLine("Picture Url: {0}",result.Value);
}
I get a bunch of different URL from my sources and what I would like is to redirect to the same URL, but with campaign data added to URL (to track the referred clicks).
For example I have these URLs:
www.example.com/category/product/name.html
www.example.com/id_product=5
I want to add at the end the following: utm_source=SOURCE&utm_medium=MEDIUM&utm_campaign=CAMPAIGN
And the URLs to become
www.example.com/category/product/name.html?utm_source=SOURCE&utm_medium=MEDIUM&utm_campaign=CAMPAIGN
www.example.com/id_product=5&utm_source=SOURCE&utm_medium=MEDIUM&utm_campaign=CAMPAIGN
How to I correctly check and cover all the cases if a URL string has parameters, and add mine?
I want to do it in node.js
Thank you
Elaborating on #snkashis, a similar but arguably more elegant solution, again using node's url module, is:
var addQueryParams = function (cleanUrl) {
var obj = url.parse(cleanUrl, true, false);
obj.query['utm_source'] = 'SOURCE';
obj.query['utm_medium'] = 'MEDIUM';
obj.query['utm_campaign'] = 'CAMPAIGN';
delete obj.search; // this makes format compose the search string out of the query object
var trackedUrl = url.format(obj);
return trackedUrl;
};
This works, because url.format first looks for search and, if it can't find it, it composes the query string from the query object
(taken from node url module documentation http://nodejs.org/api/url.html#url_url_format_urlobj )
search will be used in place of query
query (object; see querystring) will only be used if search is absent.
Here is a example showing different scenarios using Node's URL module.
var url = require('url');
var exurls = ["www.example.com/category/product/name.html","www.example.com/id_product=5?hasparam=yes"]
var to_append = "utm_source=SOURCE&utm_medium=MEDIUM&utm_campaign=CAMPAIGN";
for (i=0;i<exurls.length;i++) {
var parsedobj = url.parse(exurls[i],true,false);
//Below checks if param obj is empty.
if (Object.keys(parsedobj.query).length!==0) {
var newpath = parsedobj.href+"&"+to_append;
}
else {
var newpath = parsedobj.href+"?"+to_append;
}
console.log(newpath);
}
Connect will help you:
var connect = require('connect');
var app = connect();
app.use(connect.query());
app.use(function (req, res, next) {
console.log(req.query);
res.end(JSON.stringify(req.query));
});
app.listen(3000);