Extracting ListItems from a List in a SharePoint Site - c#

I am trying to extract a list of items in a SharePoint Site below the root site at host.sharepoint.com/sites/mysite. I've tried a bunch of different methods, but only one seems to work:
var host = "host.sharepoint.com:/";
var siteName = "mysite";
var listName = "MyList";
// Generate the Client Connection
var graphHelper = new ApplicationAuthenticatedClient(ClientId, Tenant, ClientSecret);
await graphHelper.ConnectAsync().ConfigureAwait(false);
// Code: itemNotFound
//Message: The provided path does not exist, or does not represent a site
//var list = await graphHelper.GraphClient.Sites[$"{host}{siteName}"].Request().GetAsync();
// Returns a Site, no Lists.
//var list = await graphHelper.GraphClient.Sites[host].Sites[siteName].Request().GetAsync();
//Code: itemNotFound
//Message: The provided path does not exist, or does not represent a site
//var list = await graphHelper.GraphClient.Sites[host].Sites[siteName].Lists[listName].Request().GetAsync();
// List retrieved, but no Items
//var site = await graphHelper.GraphClient.Sites[host].Sites[siteName].Request().Expand("lists").GetAsync();
//var list = await graphHelper.GraphClient.Sites[site.Id].Lists[listName].Request().Expand("Items").GetAsync();
//Code: invalidRequest
//Message: Can only provide expand and select for expand options
//var queryOptions = new List<QueryOption>() { new QueryOption("expand", "fields") };
// This works
var site = await graphHelper.GraphClient.Sites[host].Sites[siteName].Request().GetAsync();
var list = await graphHelper.GraphClient.Sites[site.Id].Lists[listName].Items.Request().Expand("Fields").GetAsync();
I've finally managed to get it to connect, but I'm wondering if there's a better way to navigate to the list, rather than the two API calls? (Assuming that I don't know the Site ID beforehand)
Edit: Using the Graph Explorer, I can access the items using https://graph.microsoft.com/v1.0/sites/{host}.sharepoint.com:/sites/{siteName}:/lists/{listName}/items?expand=fields, but I don't know how to (or if) access that API call in a single call in the .NET API.

It appears that I was on the right track with var list = await graphHelper.GraphClient.Sites[$"{host}{siteName}"].Request().GetAsync(); but the URI was not formatted correctly.
The correct Site ID for https://host.sharepoint.com/sites/mysite/MyList is:
Sites[host.sharepoint.com:/sites/mysite:"]
Retrieving the list from the code in my original question would look like this:
var host = "host.sharepoint.com";
var siteName = "mysite";
var listName = "MyList";
// Generate the Client Connection
var graphHelper = new ApplicationAuthenticatedClient(ClientId, Tenant, ClientSecret);
await graphHelper.ConnectAsync().ConfigureAwait(false);
var list = await graphHelper.GraphClient.Sites[$"{host}:/sites/{siteName}:"].Lists[listName].Request().GetAsync();

it's possible in one API call.
GET https://graph.microsoft.com/v1.0/sites/{host}.sharepoint.com:/sites/{siteName}:/lists/{listTitle}/items?$expand=Fields

Related

PowerBI embed token with RLS through C# API

We want to provide embedded PowerBI reports to our customers. Initially, we're looking at using one report with a dataset that contains data for all customers. I have created a service principal (app only auth) that has access to a workspace where I've published the report. I've also created a role "business_customer" with a DAX expression ([CustomerNumber] = USERNAME()) on this report.
I'm trying to get an embed token for the report with row level security through the C# API (Microsoft.PowerBI.Api):
var tenantId = "<tenantId>";
var datasetId = "<datasetId>";
var reportId = "<reportId>";
var groupId = "<groupId>";
var applicationPassword = "<applicationPassword>";
var context = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}/");
var clientCredential = new ClientCredential(applicationId, applicationPassword);
var result = context.AcquireTokenAsync("https://analysis.windows.net/powerbi/api",
clientCredential).Result;
var authHeader = new TokenCredentials(result.AccessToken, "Bearer");
var client = new PowerBIClient(new Uri("https://api.powerbi.com"), authHeader);
var groups = client.Groups.GetGroups();
var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId,
new EffectiveIdentity("username", new List<string> { datasetId },
new List<string> { "business_customer" }));
var embedToken = client.Reports.GenerateTokenInGroupAsync(new Guid(groupId), new Guid(reportId),
tokenRequest, cancellationToken: CancellationToken.None).Result;
This fails on the GenerateTokenInGroupsAsync call:
"Invalid request: Creating embed token for accessing dataset <datasetId> shouldn't have effective identity"
I've tried with different usernames and I've tried leaving the username blank, but all of my attempts have failed.
Is there a way of getting an embed token this way or am I on the wrong path? What would be the "correct" way of solving this case?
Thanks in advance

In Google.Apis.Pagespeedonline.v5 how to get all categories in one call?

I am using Google.Apis.Pagespeedonline.v5 (nuget) to perform the analysis on my site.
By performing the analysis directly from the API Explorer site you can perform the test on all five categories.
https://developers.google.com/speed/docs/insights/v5/reference/pagespeedapi/runpagespeed
However, using the dotnet api, the category parameter only accepts one CategoryEnum, so, I can only get the result in one category per request.
I wonder if it is possible to get all categories in a single request
var initializer = new Initializer()
{
ApiKey = "XXXXXXXXXXXXXXX",
BaseUri = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed",
GZipEnabled = true
};
var service = new PagespeedonlineService(initializer);
var request = new RunpagespeedRequest(service, "https://www.google.com");
request.Category = CategoryEnum.Accessibility;
request.Locale = "pt";
request.Strategy = StrategyEnum.Desktop;
var response = request.Execute();
Well, you can do it this way:
GET https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?category=ACCESSIBILITY&category=PERFORMANCE&locale=pt&strategy=DESKTOP&url=https%3A%2F%2Fwww.google.com%2F&prettyPrint=true&key=[YOUR_API_KEY] HTTP/1.1
But in your situation maybe you can change base URL to look like:
BaseUri = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?category=ACCESSIBILITY&category=PERFORMANCE"
Give it a try this way

How to access Box folder shared with me using C# SDK

Someone has shared a Box.com folder with me using the link. I need to be able to use the C# SDK or REST API to download the documents from their folder.
I have tried all 3 authentication types and have attempted to access with both the C# SDK and REST API.
//SDK attempt
var findFolder = await client.SharedItemsManager.SharedItemsAsync("https://<userWhoSharedWithMe>.box.com/s/<folderHash>"); // notFound
var folder = await client.FoldersManager.GetInformationAsync(findFolder.Id);
var items = folder.ItemCollection;
//API Attempt
var client = new HttpClient
{
BaseAddress = new Uri("https://api.box.com")
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<bearerToken>");
var response = await client.GetAsync("2.0/folders/<folderId>/items");
var content = await response.Content.ReadAsStringAsync();
Is there any way to programmatically download documents from a box folder that was shared with me via link?
-- Edited 06/04/2019
The folder owner and I have tried various things and it seems the API still will not allow me to see the content of the shared folder. Is there anything the folder owner needs to do to make it visible?
Based on the suggestion that I received from a Box employee, I made the following changes.
First the snippet that didn't work as expected:
// DOES NOT WORK
var reader = new StreamReader("box-config.json");
var json = reader.ReadToEnd();
var config = BoxConfig.CreateFromJsonString(json);
var sdk = new BoxJWTAuth(config);
var token = sdk.AdminToken();
var session = new OAuthSession(token, "N/A", 3600, "bearer");
boxClient = new BoxClient(config, session, asUser: boxUserId);
Secondly, the modified version that worked, allowing me to see the folder that was shared to me and allowed me to traverse its contents:
// THIS WORKS !!!!!!!!
var reader = new StreamReader("box-config.json");
var json = reader.ReadToEnd();
var config = BoxConfig.CreateFromJsonString(json);
var sdk = new BoxJWTAuth(config);
var token = sdk.UserToken(boxUserId);
boxClient = sdk.UserClient(token, boxUserId);
And for completeness' sake, here's a snippet of code that will allow you to programmatically access a Box folder and traverse its contents:
//folderId <-- You can find this ID by logging into your box account and navigating to the folder that you're interested in accessing programmatically.
var items = await boxClient.FoldersManager.GetFolderItemsAsync(folderId, limit: 5000, offset: 0, autoPaginate: false,
sort: "name", direction: BoxSortDirection.DESC);
// How many things are this folder?
Console.WriteLine($"TotalCount: {items.TotalCount}");
// Loop through those items
foreach (var item in items.Entries)
{
// Get info on each item
var file = await boxClient.FilesManager.GetInformationAsync(item.Id);
// Print the filename
Console.WriteLine($"file: {item.Name}");
}

Cancel the StripeSubscriptionservice

Right now, I work as a member of the site to cancel membership. Just when I try to run through the test, it makes the mistake of saying it
Stripe.StripeException: 'No such subscription: cus_CCKeYhNjyKTYTh'
Here's how information is displayed by a user with this ID.
var api = Settings.ConstName.StrinpAPIKey;//Get Stripe Key from Class.
StripeConfiguration.SetApiKey(api);
var customerSerive = new StripeCustomerService(api);
var subservice = new StripeSubscriptionService(api);
var stripeCustomerID = customerSerive.Get(members.User.CustomerId);//members i get the userCustomerId from DB.
subservice.Cancel(subscriptionId: check.CustomerId, cancelAtPeriodEnd: true);
So my question is how it may be that it will not let me do it? Do I need anything more for it to stop the membership of the customer?
i have a check here:
It's 100% the same ID I have in the database* and i get the version from stripe 12.1.0
i have complete this:
var api = Settings.ConstName.StrinpAPIKey;
StripeConfiguration.SetApiKey(api);
var customerSerive = new StripeCustomerService(api);
var subservice = new StripeSubscriptionService(api);
var stripeCustomerID = customerSerive.Get(members.User.CustomerId);
var GetIdSubscriptions = stripeCustomerID.Subscriptions.FirstOrDefault(i => i.CustomerId == check.CustomerId).Id;

Search for user in Azure Active directory group using graph api

I would like to have some kind of people picker functionality with auto complete features in my asp.net mvc 5 app to search for a user in a specific Azure AD group. It's a demo "todo app" that allows to assign a todo to a user that is member of a the group.
I tried with both the Graph API directly and the Azure Graph Client library but I don't seem to find a way to achieve what I want. The graph api allows to get the members of a group but adding filter "startswith" fails as when adding the filter the api returns only directory object which don't include for example DisplayName property... the client library doesn't help much either except for the batch functionality which offers a way but with a lot of overhead... I then would have to get a filtered resultset of user regardless of group membership (using User List stuff in the api), all members of the group and then fish out using Linq the correct result set.... would work fine for dev/testing but in production with a couple of hundred users this would be insane...
Any ideas or suggestions would be much appreciated. Thanks!
EDIT
Below my code that is called from client side Javascript to search for user;
AccessGroupId is the Azure AD group used to authorize users. Only
members of this group can access the web app which I handle in custom
OWin Middleware
The method is intented to be used to find a user in that group
Code works fine as below only there is no filtering applied which is the intentaion with the input parameter pre (which comes from a textbox in the ui). I get all the members of the access group.
public async Task<JsonResult> FindUser(string pre)
{
string AccessGroupId = ConfigurationManager.AppSettings["AccessGroupId"];
AuthenticationContext authCtx = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, "{0}/{1}", SecurityConfiguration.LoginUrl, SecurityConfiguration.Tenant));
ClientCredential credential = new ClientCredential(SecurityConfiguration.ClientId, SecurityConfiguration.AppKey);
AuthenticationResult assertionCredential = await authCtx.AcquireTokenAsync(SecurityConfiguration.GraphUrl, credential);
var accessToken = assertionCredential.AccessToken;
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08, AccessGroupId );
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, graphUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.SendAsync(request);
String responseString = await response.Content.ReadAsStringAsync();
JObject jsonReponse = JObject.Parse(responseString);
var l = from r in jsonReponse["value"].Children()
select new
{
UserObjectId = r["objectId"].ToString(),
UserPrincipalName = r["userPrincipalName"].ToString(),
DisplayName = r["displayName"].ToString()
};
//users = Newtonsoft.Json.JsonConvert.DeserializeObject<List<User>>(responseString);
return Json(l, JsonRequestBehavior.AllowGet);
}
When I add a filter to the same api call instead of returning the members (users, groups and/or contacts), it returns directory objects (that doesn't have displayName) which are not really usefull in the above code, unless I would query the api again (in batch) to retrieve the users displayname but that looks like a lot of overhead to me.
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08&$filter=startswith(displayName,'{1}')", AccessGroupId, pre);
I'd highlight two possible approaches:
Execute requests to Graph API using a custom JS library.
You'd need still need to care for accesstokens and have a look at ADAL.js
A sample app (not finalized as of this writing) available at:
AzureADSamples WebApp-GroupClaims-DotNet
Have a look at AadPickerLibrary.js
Try using ActiveDirectoryClient
It would look something like:
public async Task<JsonResult> FindUser(string pre) {
ActiveDirectoryClient client = AADHelper.GetActiveDirectoryClient();
IPagedCollection<IUser> pagedCollection = await client.Users.Where(u => u.UserPrincipalName.StartsWith(pre, StringComparison.CurrentCultureIgnoreCase)).ExecuteAsync();
if (pagedCollection != null)
{
do
{
List<IUser> usersList = pagedCollection.CurrentPage.ToList();
foreach (IUser user in usersList)
{
userList.Add((User)user);
}
pagedCollection = await pagedCollection.GetNextPageAsync();
} while (pagedCollection != null);
}
return Json(userList, JsonRequestBehavior.AllowGet);
}
More detailed sample is available at:
AzureADSamples WebApp-GraphAPI-DotNet

Categories

Resources