Office365 Rest Structure - ChildFolders - rest

I am trying to figure out a way of returning messages from a sub folder in outlook office 365 api.
Everything seems to point to this;
HttpResponseMessage response = await client.GetAsync("https://outlook.office365.com/api/v1.0/me/folders/inbox/childfolders/Odata/messages");
But I always get a bad request returned.
Here is my resource.
MSDN
Thanks Scott

The URL syntax is:
https://outlook.office365.com/api/v1.0/me/folders/<FOLDER ID>/Messages
So you need to get the ID for the folder you want to query. For example, if it's a subfolder of the Inbox, you could do a GET to:
https://outlook.office365.com/api/v1.0/me/folders/inbox/childfolders
And you'd get back something like:
{
"#odata.context": "https://outlook.office365.com/api/v1.0/$metadata#Me/Folders('inbox')/ChildFolders",
"value": [
{
"#odata.id": "https://outlook.office365.com/api/v1.0/Users('JasonJ#contoso.com')/Folders('AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=')",
"Id": "AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=",
"ParentFolderId": "AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAAAAEMAAA=",
"DisplayName": "New Subfolder",
"ChildFolderCount": 0
}
]
}
Then take the value of the Id field and plug it into the URL:
https://outlook.office365.com/api/v1.0/me/folders/AAMkADNhMjcxM2U5LWY2MmItNDRjYy05YzgwLWQwY2FmMTU1MjViOAAuAAAAAAC_IsPnAGUWR4fYhDeYtiNFAQCDgDrpyW-uTL4a3VuSIF6OAAAeY0W3AAA=/Messages

public void EnsureConnectionValid()
{
if (AuthenticationContext == null)
{
AuthenticationContext = new AuthenticationContext(authority);
AuthenticationResult = AuthenticationContext.AcquireToken(resource, clientId, new Uri(redirectUri), PromptBehavior.Auto);
}
}
public async Task<string> GetFolderId(string Path)
{
EnsureConnectionValid();
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationResult.AccessToken);
var restCommand = "https://outlook.office365.com/api/v1.0/me/folders/Inbox/childfolders?$filter=DisplayName eq " + "'" + Path + "'";
HttpResponseMessage response = await client.GetAsync(restCommand);
response.EnsureSuccessStatusCode();
string jsonMessage;
using (var responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
var folderObject = JObject.Parse(jsonMessage)["value"].ToObject<FoldersList[]>();
return folderObject.Select(r => r.Id).SingleOrDefault();
}

Related

Sending email using Microsoft graph with attachment. Microsoft code example unclear

HI I am trying to use Microsoft graph api to send messages.
Previously, I was sending messages/emails with the graph api without attachment. Now I need to attach 10 attachment each.
So I looked for examples and got to the Microsoft document and it shows the following code
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var attachment = new FileAttachment
{
Name = "smile",
ContentBytes = Convert.FromBase64String("R0lGODdhEAYEAA7")
};
await graphClient.Me.Messages["{message-id}"].Attachments
.Request()
.AddAsync(attachment);
Link:
https://learn.microsoft.com/en-us/graph/api/message-post-attachments?view=graph-rest-1.0&tabs=csharp
My question is what it is showing is not clear I am not sure I would I use message-id. Also I dont see if the Message is created and how the attachment is created.
Can someone help please.
You may refer to this document to learn the example about how to send email with attachments. And the below is my test code, it worked for me, I used client credential flow to provide authentication..
using Azure.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
public class HomeController : Controller
{
private readonly IWebHostEnvironment _appEnvironment;
public HomeController(IWebHostEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
public async Task<string> sendMailAsync() {
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_clientid";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var a = _appEnvironment.WebRootPath;//I have a file stored in my project
var file = a + "\\hellow.txt";
byte[] fileArray = System.IO.File.ReadAllBytes(#file);
//string base64string = Convert.ToBase64String(fileArray);
var message = new Message
{
Subject = "Meet for lunch?",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = "The new cafeteria is open."
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "xxx#outlook.com"
}
}
},
Attachments = new MessageAttachmentsCollectionPage()
{
new FileAttachment
{
Name = "attachment.txt",
ContentType = "text/plain",
ContentBytes = fileArray
}
}
};
await graphClient.Users["user_id"]
.SendMail(message, null)
.Request()
.PostAsync();
return "success";
}
}

Xamarin Essentials Unable to exchange Okta authorization code for token

I was using OpenID and we have to switch to Xamarin.Essentials.WebAuthenticator.
I can get an authorization code from Okta using WebAuthenticator.AuthenticateAsync().
But, everything I try to then translate that code into an access token returns 400 Bad Request.
Okta's API error is "E0000021: HTTP media type not supported exception" and it goes on to say, "Bad request. Accept and/or Content-Type headers likely do not match supported values."
I have tried to follow https://developer.okta.com/blog/2020/07/31/xamarin-essentials-webauthenticator as much as possible, but we are not using the hybrid grant type like he is.
We are using only Authorization Code, which means I have to make a secondary call, and I have spent two days trying to figure out how.
private async Task LoginOktaAsync()
{
try
{
var loginUrl = new Uri(BuildAuthenticationUrl()); // that method is down below
var callbackUrl = new Uri("com.oktapreview.dev-999999:/callback"); // it's not really 999999
var authenticationResult = await Xamarin.Essentials.WebAuthenticator.AuthenticateAsync(loginUrl, callbackUrl);
string authCode;
authenticationResult.Properties.TryGetValue("code",out authCode);
// Everything works fine up to this point. I get the authorization code.
var url = $"https://dev-999999.oktapreview.com/oauth2/default/v1/token"
+"?grant_type=authorization_code"
+$"&code={authCode}&client_id={OktaConfiguration.ClientId}&code_verifier={codeVerifier}";
var request = new HttpRequestMessage(HttpMethod.Post, url);
var client = new HttpClient();
var response = await client.SendAsync(request); // this generates the 400 error.
}
catch(Exception e)
{
Debug.WriteLine($"Error: {e.Message}");
}
}
Here are the methods that produce the login url and a couple of other things:
public string BuildAuthenticationUrl()
{
var state = CreateCryptoGuid();
var nonce = CreateCryptoGuid();
CreateCodeChallenge();
var url = $"https://dev-999999.oktapreview.com/oauth2/default/v1/authorize?response_type=code"
+ "&response_mode=fragment"
+ "&scope=openid%20profile%20email"
+ "&redirect_uri=com.oktapreview.dev-999999:/callback"
+$"&client_id={OktaConfiguration.ClientId}"
+$"&state={state}"
+$"&code_challenge={codeChallenge}"
+ "&code_challenge_method=S256"
+$"&nonce={nonce}";
return url;
}
private string CreateCryptoGuid()
{
using (var generator = RandomNumberGenerator.Create())
{
var bytes = new byte[16];
generator.GetBytes(bytes);
return new Guid(bytes).ToString("N");
}
}
private string CreateCodeChallenge()
{
codeChallenge = GenerateCodeToVerify();
codeVerifier = codeChallenge;
using (var sha256 = SHA256.Create())
{
var codeChallengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeChallenge));
return Convert.ToBase64String(codeChallengeBytes);
}
}
private string GenerateCodeToVerify()
{
var str = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
Random rnd = new Random();
for (var i = 0; i < 100; i++)
{
str += possible.Substring(rnd.Next(0,possible.Length-1),1);
}
return str;
}
'''
After much online research, I discovered the issue was with how I was doing my post to get the token. This is how I made it work:
public static Dictionary<string, string> JsonDecode(string encodedString)
{
var inputs = new Dictionary<string, string>();
var json = JValue.Parse(encodedString) as JObject;
foreach (KeyValuePair<string, JToken> kv in json)
{
if (kv.Value is JValue v)
{
if (v.Type != JTokenType.String)
inputs[kv.Key] = v.ToString();
else
inputs[kv.Key] = (string)v;
}
}
return inputs;
}
private async Task<string> ExchangeAuthCodeForToken(string authCode)
{
string accessToken = string.Empty;
List<KeyValuePair<string, string>> kvdata = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "authorization_code"),
new KeyValuePair<string, string>("code", authCode),
new KeyValuePair<string, string>("redirect_uri", OktaConfiguration.Callback),
new KeyValuePair<string, string>("client_id", OktaConfiguration.ClientId),
new KeyValuePair<string, string>("code_verifier", codeVerifier)
};
var content = new FormUrlEncodedContent(kvdata);
var request = new HttpRequestMessage(HttpMethod.Post, OktaConfiguration.TokenUrl)
{Content = content, Method = HttpMethod.Post};
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.SendAsync(request);
string text = await response.Content.ReadAsStringAsync();
Dictionary<string, string> data = JsonDecode(text);
data.TryGetValue("access_token", out accessToken);
return accessToken;
}

Download mp3 file at browser through API .net core

Was trying to download an mp3 file in a browser through the API that I created. But instead of receiving an mp3 file. I keep getting JSON format response. I had referred from answer in return-file-in-asp-net-core-web-api, but still, I can't download the mp3 file.
Is there any mistake that I've overlooked, please kindly help?
This is my downloading method from UI
void DownloadRecording(RecordingHistory voicehistory)
{
try
{
using (var client = new WebClient())
{
client.DownloadFile("https://2d489fd863a2.ngrok.io/api/download/" + voicehistory.RecordingId + ".mp3", voicehistory.RecordingId + ".mp3");
}
}
catch { }
}
This is my api function for downloading mp3 from server
[HttpGet("download/{recordingFile}")]
public async Task<IActionResult> DownloadVoiceRecording(string recordingFile)
{
string filePath = Directory.GetCurrentDirectory() + #"\audio\Processed\" + recordingFile;
var memory = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var types = GetMimeTypes();
var ext = Path.GetExtension(filePath).ToLowerInvariant();
return File(filePath, types[ext], recordingFile);
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".mp3", "audio/mpeg"},
{".wav","audio/wav" }
};
}
This is the response I get from browser and Postman
{
"Version": "2.0.0.0",
"StatusCode": 200,
"Message": "Status 200 OK",
"Result":"��#� ... ... /// A lot of random symbol here
}
Because the first parameter of the return value File is a type of Stream, memory needs to be passed in.
[HttpGet("download/{recordingFile}")]
public async Task<IActionResult> DownloadVoiceRecording(string recordingFile)
{
string filePath = Directory.GetCurrentDirectory() + #"\audio\Processed\" + recordingFile;
var memory = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var types = GetMimeTypes();
var ext = Path.GetExtension(filePath).ToLowerInvariant();
return File(memory, types[ext], recordingFile);
}
I'm using Blazor for this. It turns out that there was an API response wrapper in Blazor APIReponse middleware. I had to put my API into an exception so it won't turn into JSON when I access it. It works finally.
Below is the APIReponse wrapper in Blazor.
var formattedRequest = await FormatRequest(request);
var originalBodyStream = httpContext.Response.Body;
using (var responseBody = new MemoryStream())
{
try
{
string responseBodyContent = null;
var response = httpContext.Response;
if (new string[] { "/api/localization", "/api/data", "/api/externalauth", "/api/download" }.Any(e => request.Path.StartsWithSegments(new PathString(e.ToLower()))))
await _next.Invoke(httpContext);
else
{
response.Body = responseBody;
await _next.Invoke(httpContext);
//wrap response in ApiResponse
if (httpContext.Response.StatusCode == Status200OK)
{
responseBodyContent = await FormatResponse(response);
await HandleSuccessRequestAsync(httpContext, responseBodyContent, Status200OK);
}
else
await HandleNotSuccessRequestAsync(httpContext, httpContext.Response.StatusCode);
}

How to post JSON data to Pardot API via Httpclient

I am trying to post the JSON data to Pardot. I have used the info from here to call the Pardot API and currently using Pardot form handler to post the data. I want to know if i could the data via Pardot API call by using CREATE or UPSERT instead of using a form handler.
Below is my code
class SendingDataToPardot
{
public string Login()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
var url = "https://pi.pardot.com/api/login/version/3";
string apiKey = null;
var loginInfo = new Dictionary<string, string>
{
{"email", "xx"},
{"password", "xxx"},
{"user_key", "xxx"}
};
var httpContent = new FormUrlEncodedContent(loginInfo);
using (var client = new HttpClient())
{
HttpResponseMessage response = client.PostAsync(url, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string resultValue = response.Content.ReadAsStringAsync().Result;
apiKey = XDocument.Parse(resultValue).Element("rsp").Element("api_key").Value;
return apiKey;
}
else
{
return null;
}
}
}
public string POST()
{
string Api_Key = Login();
var url = "form handler url";
var contactFormData = new Dictionary<string, string>
{
{"email", "test#test.com"},
{"FirstName", "xxx"},
{"LastName", "xxxxx"},
{"Comments", "this is a test"}
};
var data= new FormUrlEncodedContent(contactFormData);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Api_Key);
HttpResponseMessage response = client.PostAsync(url, data).Result;
string result = response.Content.ReadAsStringAsync().Result;
return result;
}
}
}
}
For most of the APIs Pardot exposes, you need to do XML work with it.
Looks like you are using Java, so you might have luck using a public library, even if just for understanding communication patterns (we had to rewrite it for our purposes, but it did serve as a great blueprint).
Have a look at the https://github.com/Crim/pardot-java-client project and see if it helps you out.

Get id of last Rest API POST using Entity Framework

I need to be able to access the id of a new Post. I will be using this id to populate another field called LocationId like this: "L" + id = LocationId (example L22) where 22 is the id of the new Post. Here is the code for my Post request:
private async void BtnSubmit_Clicked(object sender, EventArgs e)
{
var imageArray = FilesHelper.ReadFully(file.GetStream());
file.Dispose();
var location = new Models.Location()
{
LocationName = EntName.Text,
ImageArray = imageArray,
};
ApiServices apiServices = new ApiServices();
bool response = await apiServices.PostLocation(location);
bool response2 = await apiServices.InputLocationId(id, location);
if (!response || !response2)
{
await DisplayAlert("Alert", "Something wrong", "Cancel");
}
else
{
await DisplayAlert("Hi", "Your record has beed added successfully", "Alright");
}
await Navigation.PushAsync(new SetupPage());
This is on the client side. I have all the APIs created (such as PostLocation and InputLocationId)on Azure SQL Server. This is for a mobile inventory app built using Xamarin.
public async Task<bool> PostLocation(Location location)
{
var json = JsonConvert.SerializeObject(location);
var httpClient = new HttpClient();
var content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Settings.AccessToken);
var wimsApiUrl = "http://xxxxxxx.azurewebsites.net/api/Locations";
//Get the Body of the Post
var body = await httpClient.PostAsync(wimsApiUrl, content);
//Convert it to a string
var jString = await body.Content.ReadAsStringAsync();
//Place it in a JSON Object
JObject joResponse = JObject.Parse(jString);
//Parse the JSON Object into an Int from a String
var id = int.Parse(joResponse["Id"].ToString());
//This is used in my other script to Put the LocationId of Lxx
AddNewLocationPage.NewLocationId = id;
return body.IsSuccessStatusCode;
}
My Post Location API:
// POST: api/Locations
[ResponseType(typeof(Location))]
public IHttpActionResult PostLocation([FromBody] Location location)
{
string userId = User.Identity.GetUserId();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var stream = new MemoryStream(location.ImageArray);
var guid = Guid.NewGuid().ToString();
var file = String.Format("{0}.jpg", guid);
var folder = "~/Content/Images";
var fullPath = String.Format("{0}/{1}", folder, file);
var response = FilesHelper.UploadPhoto(stream, folder, file);
if (response)
{
location.ImagePath = fullPath;
}
var newLocation = new Location()
{
LocationName = location.LocationName,
User = userId,
ImagePath = location.ImagePath
};
db.Locations.Add(newLocation);
db.SaveChanges();
return Ok(new { newLocation.Id});
}
I will then take the id and put it in this Put Request to create the LocationId:
public async Task<bool> InputLocationId(int id, Location location)
{
var json = JsonConvert.SerializeObject(location);
var httpClient = new HttpClient();
var content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Settings.AccessToken);
var wimsApiUrl = "http://xxxxxxx.azurewebsites.net/api/Locations/InputLocationId/";
var completeUrl = String.Format("{0}{1}", wimsApiUrl, id);
var response = await httpClient.PutAsync(completeUrl, content);
return response.IsSuccessStatusCode;
}
The InputLocationId API will automatically create the LocationId. Here is my API:
// PUT: api/Locations/5
[HttpPut]
[ResponseType(typeof(void))]
[Route("api/Locations/InputLocationId/{id}")]
public IHttpActionResult InputLocationId(int id, [FromBody] Location location)
{
//string userId = User.Identity.GetUserId();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = db.Locations.FirstOrDefault(locationId => locationId.Id == id);
var resultant = String.Format("L{0}", id);
location.LocationName = location.LocationName;
result.LocationId = resultant;
db.SaveChanges();
return Ok("The record has been updated");
}
I am simply stuck on how to access that id!
// get the response body
var body = await httpClient.PostAsync(wimsApiUrl, content);
// load it into a JSON object using Newtonsoft
JObject data = JObject.Parse(body);
// get the id
var id = int.Parse(data["id"]);
The returns need to be converted into a string from the HttpResponseMessage.
var body = await httpClient.PostAsync(wimsApiUrl, content);
var jString = await body.Content.ReadAsStringAsync();
Then we can place it into a JSON Object:
JObject joResponse = JObject.Parse(jString);
Now this JSON Object can be parsed into an Int. Note it needs to be converted to a string.
var id = int.Parse(joResponse["Id"].ToString());