How to access Shared Mail Folder Using Microsoft Graph In .NET Application - asp.net-mvc

I have Generated Microsoft Graph app in ASP.NET MVC platform, that I have downloaded from Microsoft Graph site. I need to access the shared mail folder not sure exactly how can I get that?? In the following code I can access my mailFolder but not shared mailfolder!
public static async Task<IEnumerable<MailFolder>> GetMailFolderAsync()
{
var graphClient = GetAuthenticatedClient();
var mailFolder = await graphClient.Me.MailFolders.Request().GetAsync();
var sharedMailFolder = await graphClient.Users.Request().GetAsync();
return mailFolder;
}
Also, I want to know in above code where I can pass the parameter to access next page or all pages??
private static GraphServiceClient GetAuthenticatedClient()
{
return new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string signedInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
SessionTokenStore tokenStore = new SessionTokenStore(signedInUserId,
new HttpContextWrapper(HttpContext.Current));
var idClient = new ConfidentialClientApplication(
appId, redirectUri, new ClientCredential(appSecret),
tokenStore.GetMsalCacheInstance(), null);
var accounts = await idClient.GetAccountsAsync();
var result = await idClient.AcquireTokenSilentAsync(
graphScopes.Split(' '), accounts.FirstOrDefault());
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", result.AccessToken);
}));

I think it is not possible to access shared folders I am investigating as well. In regards to the question of getting pages, as soon as you get the first request
public static async Task<IEnumerable<MailFolder>> GetMailFolderAsync()
{
var graphClient = GetAuthenticatedClient();
var mailFolder = await graphClient.Me.MailFolders.Request().GetAsync();
var sharedMailFolder = await graphClient.Users.Request().GetAsync();
return mailFolder;
}
then you can review for example, mailFolder.NextPageRequest, if it is not null then you can request it by doing mailFolder.NextPageRequest.GetAsync() and you can use it as a loop conditional
while(mailfoldersCollection != null) {
// Do your stuff with items within for(var folder in mailfoldersCollection) {}
// when read all items in CurrentPage then
if (mailFolder.NextPageRequest != null) {
mailfoldersCollection = await mailFolder.NextPageRequest.GetAsync();
}
hope it works for you!

Related

Unable to cast object of type 'Microsoft.Graph.CalendarEventsCollectionPage' to type 'System.Collections.Generic.IEnumerable`

My controller code is lke this,
public async Task<IEnumerable<CalendarEvent>> Get()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "xxxxx";
var clientId = "xxxxxx";
var clientSecret = "xxxx";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphServiceClient = new GraphServiceClient(clientSecretCredential, scopes);
if (User == null!)
{
var user = await graphServiceClient.Users["xxxxx.com"].Calendar
.Events
.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location")
.GetAsync();
return (CalendarEvent)user;
}
}
Iam getting an error like
Unable to cast object of type 'Microsoft.Graph.CalendarEventsCollectionPage' to type 'System.Collections.Generic.IEnumerable'
I need query that sholud be given in controller.
It's not exactly clear what you are trying to achieve but you can't convert CalendarEventsCollectionPage to IEnumerable. I am assuming that you want to return all events of specific user.
public async Task<List<Event>> GetEventsOfUser(string userId)
{
var events = new List<Event>();
var eventsPages = _client.Users[userId].Calendar.Events.Request()
.Select("subject,body,bodyPreview,organizer,attendees,start,end,location");
while (eventsPages != null)
{
var current = await eventsPages.GetAsync();
events.AddRange(current.CurrentPage);
eventsPages = current.NextPageRequest;
}
return events;
}
You need to fetch every page with NextPageRequest in order to get all events.

Azure Durable Function - read users from Graph in a quicker way

I have an Azure Function with Premium Plan where users from multiple AzureAD groups are read and put to a queue. Currently, I'm looking into converting this function to Durable Function and use consumption plan. Here is my code:
Orchestrator:
public async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var groups = await context.CallActivityAsync<List<AzureADGroup>>("GroupsReaderFunction"), null);
if (groups != null && groups.Count > 0)
{
var processingTasks = new List<Task>();
foreach (var group in groups)
{
var processTask = context.CallSubOrchestratorAsync("SubOrchestratorFunction", group);
processingTasks.Add(processTask);
}
await Task.WhenAll(processingTasks);
}
}
SubOrchestrator:
public async Task RunSubOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var request = context.GetInput<Group>();
var users = await context.CallActivityAsync<List<AzureADUser>>("UsersReaderFunction", request.objectId);
return users;
}
Here is the function that gets users from AzureAD group:
public async Task<List<AzureADUser>> GetUsersInGroup(Guid objectId)
{
IGroupTransitiveMembersCollectionWithReferencesPage members;
members = await GraphServiceClient.Groups[objectId.ToString()].TransitiveMembers.Request().Select("id").GetAsync();
var toReturn = new List<AzureADUser>(ToUsers(members.CurrentPage));
while (members.NextPageRequest != null)
{
members = await members.NextPageRequest.GetAsync();
toReturn.AddRange(ToUsers(members.CurrentPage));
}
return toReturn;
}
private IEnumerable<AzureADUser> ToUsers(IEnumerable<DirectoryObject> fromGraph)
{
foreach (var users in fromGraph)
{
return new AzureADUser { ObjectId = Guid.Parse(user.Id) };
}
}
Number of users in groups vary - one group contains 10 users and another group contains ~500k users. Timeout occurs when reading users from larger groups (> 10 minutes). Is there a faster way to get users from AzureAD group (for example, get users in batches) so that I should be able to use Consumption Plan? Or is there a different way to use Durable Functions (fan in - fan out pattern or some other patterns) for a faster performance?
UPDATE:
var users = new List<AzureADUser>();
public async Task RunSubOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var request = context.GetInput<Group>();
var response = await context.CallActivityAsync<(List<AzureADUser> users, nextPageLink link)>("UsersReaderFunction", request.objectId);
users.AddRange(response.users);
return users;
}
Here response contains 2 values - users from current page and link to next page. I need to keep calling "UsersReaderFunction" activity function until link to next page is null.
var users = new List<AzureADUser>();
public async Task RunSubOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var request = context.GetInput<Group>();
var response = await context.CallActivityAsync<(List<AzureADUser> users, nextPageLink link)>("UsersReaderFunction", request.objectId);
users.AddRange(response.users);
while (response.link != null) {
var response = await context.CallActivityAsync<(List<AzureADUser> users, nextPageLink link)>("UsersReaderFunction", request.objectId);
users.AddRange(response.users);
}
return users;
}
But this is not working. What am I missing?

Woocommerce Create Order REST API and Xamarin Forms

I'm building a checkout page written in Xamarin Forms that creates an order in Woocommerce.
I've looked up documentation online but can't seem to find any examples or code that shows you how to do this.
My question is : is there an example code on how to create a simple order using Xamarin Forms and Woocommerce REST API?
I was able to use the REST API to pull the products from Woocommerce but can't seem to find any code examples of how to actually create an order using the REST API in Xamarin Forms.
Hope you can help.
Cheers
Here's my code woocommerceapi.cs class.
class WoocommerceAPI
{
private static string website_url = "xxxxx";
private static string consumer_key = "xxxxx";
private static string consumer_secret = "xxxxx";
private static string GetAllProductsApiUrl = string.Format("{0}/wc-api/v3/products?consumer_key={1}&consumer_secret={2}", website_url, consumer_key, consumer_secret);
private static string GetAllProductsInACategoryApiUrl = "xxxxx/wc-api/v3/products?category=379&consumer_key=xxxxx&consumer_secret=xxxxx";
public async Task<Products> GetAllProducts()
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(GetAllProductsApiUrl);
HttpContent content = response.Content;
var json = await content.ReadAsStringAsync();
var products = JsonConvert.DeserializeObject<Products>(json);
return products;
}
public async Task<Products> GetAllProductsInACategory()
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(GetAllProductsInACategoryApiUrl);
HttpContent content = response.Content;
var json = await content.ReadAsStringAsync();
var products = JsonConvert.DeserializeObject<Products>(json);
return products;
}
}
I managed to build a solution and it's now working!
Here's the code if you come across this problem and need a fix.
async void OrderBtnClicked(object sender, EventArgs e)
{
Console.WriteLine("Starting REST API");
var clientapi = new HttpClient();
clientapi = new Uri("xxxx?consumer_key=xxxx&consumer_secret=xxxx");
Console.WriteLine("Starting REST API");
var clientapi = new HttpClient();
clientapi.BaseAddress = new Uri("xxxx?consumer_key=xxxx&consumer_secret=xxxx");
// json data for adding customer
string jsonData = #"{
""first_name"" : ""John1"",
""last_name"" : ""Doe1"",
""email"" : ""john.doe1#example.com"",
""username"" : ""john.doe1"",
""password"" : ""mypassword"",
""billing"": {
""first_name"": ""John"",
""last_name"": ""Doe"",
""company"": ""john doe company"",
""address_1"": ""969 Market"",
""address_2"": """",
""city"": ""San Francisco"",
""state"": ""CA"",
""postcode"": ""94103"",
""country"": ""US"",
""email"": ""john.doe#example.com"",
""phone"": ""(555) 555-5555""
},
""shipping"": {
""first_name"": ""John"",
""last_name"": ""Doe"",
""company"": """",
""address_1"": ""969 Market"",
""address_2"": """",
""city"": ""San Francisco"",
""state"": ""CA"",
""postcode"": ""94103"",
""country"": ""US""
}
}";
Console.WriteLine("Here's the json string data");
Console.WriteLine(jsonData);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage apiresponse = await clientapi.PostAsync("xxxxx?consumer_key=xxxxx&consumer_secret=xxxx", content);
var apiresult = await apiresponse.Content.ReadAsStringAsync();
Console.WriteLine("Here's the result:");
Console.WriteLine(apiresult);
Console.WriteLine("REST API Post Completed.");
await DisplayAlert("Checkout", "Completed", "ok");
}
Obviously you can setup public variables to store the json data and you're more than welcome to do so, i'm not your dad.

MVC app stops after SendAsync to an API - The same code runs fine in Console App though

I built a simple Console Application to test the connection to an API. Calling the connection method from Console App Main works fine. I get a response with an access-token.
I though that I just could implement the same method/code to an MVC-project and add the method within the HomeController, then call the method from any ActionResult, getting the access-token and then put it in a ViewBag to display it in a view (just for testing). But it doesn't work in the MVC-project.
If I run the debugger, it seems like the app hangs when SendAsync is executed in the method. The console gives this output:
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.RemoteDependency","time":"2017-04-08T09:26:32.4945663Z","tags":{"ai.internal.sdkVersion":"rddf:2.2.0-738","ai.internal.nodeName":"XXXXXX","ai.cloud.roleInstance":"XXXXXXXX"},"data":{"baseType":"RemoteDependencyData","baseData":{"ver":2,"name":"/token","id":"XXXXXXXXX=","data":"https://api.vasttrafik.se/token","duration":"00:00:00.2810000","resultCode":"200","success":true,"type":"Http","target":"api.vasttrafik.se","properties":{"DeveloperMode":"true"}}}}
The thread 0x1f68 has exited with code 0 (0x0).
What can I do to make the API-call / response work in the MVC-application?
My knowledge in the area is ridiculously low. But I really want to understand whats going on here.
Thanks!
Best
J
MVC project
public class HomeController : Controller
{
public ActionResult Index()
{
string token = PostRequest().Result;
ViewBag.Token = token;
return View();
}
async static Task<string> PostRequest()
{
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.vasttrafik.se");
var request = new HttpRequestMessage(HttpMethod.Post, "/token");
// Key // Secret
string credentials = "xxxxxxxxxoVS5xDrcO6qZsAp0a" + ":" + "xxxxxxxxhn0STj1w4asDwixdMa";
var plainTextBytes = Encoding.UTF8.GetBytes(credentials);
//Key and secret encoded
string encodedCrentedials = Convert.ToBase64String(plainTextBytes);
//Console.WriteLine(encodedCrentedials);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedCrentedials);
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
formData.Add(new KeyValuePair<string, string>("scope", "xxxxxxxxw0oVS5xDrcO6qZsAp0a"));
request.Content = new FormUrlEncodedContent(formData);
// This is where the app hangs....
var response = await client.SendAsync(request);
var mycontentres = await response.Content.ReadAsByteArrayAsync();
var responseBody = Encoding.Default.GetString(mycontentres);
//Console.WriteLine(responseBody);
JavaScriptSerializer seri = new JavaScriptSerializer();
dynamic data = JObject.Parse(responseBody);
string tok = data.access_token;
return tok;
}
}
Don't block on async code:
public async Task<ActionResult> Index()
{
string token = await PostRequest();
ViewBag.Token = token;
return View();
}

Using Postal and Hangfire in Subsite

I have been trying to use Postal on my MVC5 site. When I host my webpage a subsite ie, http://localhost/Subsite I am receiving the error
The virtual path '/' maps to another application, which is not allowed
I have debugged it down to when the ControllerContext is being created the HttpContext isn't getting set correctly. Since I'm running Postal from Hangfire the HttpContext.Current is always null. Postal creates the ContollerContext using the code below.
ControllerContext CreateControllerContext()
{
// A dummy HttpContextBase that is enough to allow the view to be rendered.
var httpContext = new HttpContextWrapper(
new HttpContext(
new HttpRequest("", UrlRoot(), ""),
new HttpResponse(TextWriter.Null)
)
);
var routeData = new RouteData();
routeData.Values["controller"] = EmailViewDirectoryName;
var requestContext = new RequestContext(httpContext, routeData);
var stubController = new StubController();
var controllerContext = new ControllerContext(requestContext, stubController);
stubController.ControllerContext = controllerContext;
return controllerContext;
}
string UrlRoot()
{
var httpContext = HttpContext.Current;
if (httpContext == null)
{
return "http://localhost";
}
return httpContext.Request.Url.GetLeftPart(UriPartial.Authority) +
httpContext.Request.ApplicationPath;
}
How can I specify the UrlRoot so that instead of pulling the default of localhost to pull it based on my subsite?
I followed the directions here http://docs.hangfire.io/en/latest/tutorials/send-email.html to send my email. The method in the tutorial is below
public static void NotifyNewComment(int commentId)
{
// Prepare Postal classes to work outside of ASP.NET request
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var engines = new ViewEngineCollection();
engines.Add(new FileSystemRazorViewEngine(viewsPath));
var emailService = new EmailService(engines);
// Get comment and send a notification.
using (var db = new MailerDbContext())
{
var comment = db.Comments.Find(commentId);
var email = new NewCommentEmail
{
To = "yourmail#example.com",
UserName = comment.UserName,
Comment = comment.Text
};
emailService.Send(email);
}
}
I found the issue was that the FileSystemRazorViewEngine was not being used bty postal. To get the this to work I had to make sure that the FileSystemRazorViewEngine was the first engine in the available. I then removed it because I did not want it to be the default engine. Below is my updated method.
public static void NotifyNewComment(int commentId)
{
// Prepare Postal classes to work outside of ASP.NET request
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var eng = new FileSystemRazorViewEngine(viewsPath));
ViewEngines.Engines.Insert(0, eng);
var emailService = new EmailService(engines);
// Get comment and send a notification.
using (var db = new MailerDbContext())
{
var comment = db.Comments.Find(commentId);
var email = new NewCommentEmail
{
To = "yourmail#example.com",
UserName = comment.UserName,
Comment = comment.Text
};
emailService.Send(email);
ViewEngines.Engines.RemoveAt(0)
}
}
Below is another possible solution that I think is more elegant than above. It also resolves an issue that appears when accessing the MVC application while the background process is being executed.
public static void SendTypedEmailBackground()
{
try
{
var engines = new ViewEngineCollection();
var viewsPath = Path.GetFullPath(HostingEnvironment.MapPath(#"~/Views/Emails"));
var eng = new FileSystemRazorViewEngine(viewsPath);
engines.Add(eng);
var email = new WebApplication1.Controllers.EmailController.TypedEmail();
email.Date = DateTime.UtcNow.ToString();
IEmailService service = new Postal.EmailService(engines);
service.Send(email);
}
catch(Exception ex)
{
throw ex;
}
}

Resources