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

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";
}
}

Related

Azure Search CreateIndexAsync fails with CamelCase field names FieldBuilder

Azure Search V11
I can't get this to work. But with the standard FieldBuilder the index is created.
private static async Task CreateIndexAsync(SearchIndexClient indexClient, string indexName, Type type)
{
var builder = new FieldBuilder
{
Serializer = new JsonObjectSerializer(new JsonSerializerOptions {PropertyNamingPolicy = new CamelCaseNamingPolicy()})
};
var searchFields = builder.Build(type).ToArray();
var definition = new SearchIndex(indexName, searchFields);
await indexClient.CreateIndexAsync(definition);
}
`
public class CamelCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
{
return char.ToLower(name[0]) + name.Substring(1);
}
}
See our sample for FieldBuilder. Basically, you must use a naming policy for both FieldBuilder and the SearchClient:
var clientOptions = new SearchClientOptions
{
Serializer = new JsonObjectSerializer(
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
}),
};
var builder = new FieldBuilder
{
Serializer = clientOptions.Serializer,
};
var index = new SearchIndex("name")
{
Fields = builder.Build(type),
};
var indexClient = new SearchIndexClient(uri, clientOptions);
await indexClient.CreateIndexAsync(index);
await Task.DelayAsync(5000); // can take a little while
var searchClient = new SearchClient(uri, clientOptions);
var response = await searchClient.SearchAsync("whatever");
While this sample works (our sample code comes from oft-executed tests), if you have further troubles, please be sure to post the exact exception message you are getting.

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;
}

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.

Box.com API Usage - Get Folder Count as a service app

We are creating an app that is meant to be used with a Service Account in your system; another user (user-2) has authorized this app by adding our app key to their Custom Application list. How do I get this User-2's UserID, so we can impersonate him and access his files list and files, etc. We need their UserID, so we can pass the "AS-User: " Header. And can this header be set using some property from within the .NET SDK - a sample code will be appreciated.
This does it for all enterprise users but you can easily put an if statement to get the user you're looking for.
static async Task MainAsync()
{
// rename the private_key.pem.example to private_key.pem and put your JWT private key in the file
var privateKey = File.ReadAllText(PRIVATE_KEY_FILE);
var boxConfig = new BoxConfig(CLIENT_ID, CLIENT_SECRET, ENTERPRISE_ID, privateKey, JWT_PRIVATE_KEY_PASSWORD, JWT_PUBLIC_KEY_ID);
var boxJWT = new BoxJWTAuth(boxConfig);
var adminToken = boxJWT.AdminToken();
Console.WriteLine("Admin Token: " + adminToken);
Console.WriteLine();
var adminClient = boxJWT.AdminClient(adminToken); // adminClient == serviceAccount
var userDetails = await adminClient.UsersManager.GetCurrentUserInformationAsync();
Console.WriteLine("\tAdmin User Details:");
Console.WriteLine("\tId: {0}", userDetails.Id);
Console.WriteLine("\tName: {0}", userDetails.Name);
Console.WriteLine("\tStatus: {0}", userDetails.Status);
Console.WriteLine();
var users = await adminClient.UsersManager.GetEnterpriseUsersAsync();
users.Entries.ForEach(i =>
{
Console.WriteLine("\t{0}", i.Name);
Console.WriteLine("\t{0}", i.Status);
if (i.Status == "active")
{
var userToken = boxJWT.UserToken(i.Id);
var userClient = boxJWT.UserClient(userToken, i.Id);
Task u = getUserItems(userClient, i.Id);
u.Wait();
}
});
}
static async Task getUserItems(BoxClient userClient, string id)
{
var userDetails = await userClient.UsersManager.GetCurrentUserInformationAsync();
Console.WriteLine("\nManaged User Details:");
Console.WriteLine("\tId: {0}", userDetails.Id);
Console.WriteLine("\tName: {0}", userDetails.Name);
Console.WriteLine("\tStatus: {0}", userDetails.Status);
Console.WriteLine();
Console.WriteLine("managed users older items");
var items = await userClient.FoldersManager.GetFolderItemsAsync("0", 500);
items.Entries.ForEach(i =>
{
Console.WriteLine("\t{0}", i.Name);
});
Console.WriteLine();
}

Office365 Rest Structure - ChildFolders

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();
}