Azure Function SendGrid - tsql

Hi I'm basically executing a query which returns x amount of emails that exist on a specific day and I want to send an email to all of these emails using sendgrid's api here is my code - I am running into alot of errors listed below could anyone shed some light?
[code]
**#r "System.Data"
#r "SendGrid"
using System;
using System.Data;
using SendGrid.Helpers.Mail;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
using SendGrid;
private SqlConnection conn = null;
private SqlDataAdapter da = null;
private SqlCommandBuilder cb = null;
private DataSet ds = null;
private String location = null;
public void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
string connStr = "Data Source=sdc-hwsb.database.windows.net;Initial Catalog=SDC-HotelWSBooking;Integrated Security=False;User ID=sdchwsb;Password=Trivago17!;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string query = "SELECT email FROM dbo.test_bookings2 WHERE startDate = #startDate";
SqlCommand cmd = new SqlCommand(query, conn);
cmd.Parameters.AddWithValue("#startDate", DateTime.Today.ToShortDateString());
int k = 0;
int f = Convert.ToInt32(cmd.ExecuteNonQuery());
while (f > 0 & k < f)
{
conn = new SqlConnection(connStr);
da = new SqlDataAdapter(query, conn);
cb = new SqlCommandBuilder(da);
ds = new DataSet();
da.Fill(ds);
String Email = Convert.ToString(ds.Tables[0].Rows[k]);
Run1(Email,message);
k++;
}
}
public static void Run1(string email, out Mail message)
{
message = new Mail
{
Subject = "Azure news"
};
var personalization = new Personalization();
// change to email of recipient
personalization.AddTo(new Email(email));
Content content = new Content
{
Type = "text/plain",
Value = "DD"
};
message.AddContent(content);
message.AddPersonalization(personalization);
}
**
I Am getting errors reffering to the Message object sendgrid is using such as:
2017-09-25T18:50:37.754 Function started (Id=067b32b9-7bc3-47ca-9f32-b8b92c3b57e9)
2017-09-25T18:50:37.770 Function compilation error
2017-09-25T18:50:37.770 run.csx(38,28): error CS0103: The name 'message' does not exist in the current context
2017-09-25T18:50:37.807 Exception while executing function: Functions.TimerTriggerCSharp1. Microsoft.Azure.WebJobs.Script: Script compilation failed.
2017-09-25T18:50:37.948 Function completed (Failure, Id=067b32b9-7bc3-47ca-9f32-b8b92c3b57e9, Duration=196ms)

As Mike S mentioned about sending multiple emails via use an ICollector, I checked the official document about SendGrid output binding and did not find any sample, then I followed the code sample from Queue output sample in C# to test this feature as follows:
run.csx
#r "SendGrid"
using System;
using SendGrid.Helpers.Mail;
public static void Run(TimerInfo myTimer, TraceWriter log, ICollector<Mail> mails)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
for(int i=0;i<3;i++)
{
Mail message = new Mail()
{
Subject = $"Hello world from the SendGrid C# TimerTrigger!"
};
var personalization = new Personalization();
personalization.AddTo(new Email("the-email-address-of-recipient"));
Content content = new Content
{
Type = "text/plain",
Value = $"Hello world!{i}"
};
message.AddContent(content);
message.AddPersonalization(personalization);
mails.Add(message);
}
}
function.json
{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
},
{
"type": "sendGrid",
"name": "mails",
"apiKey": "sendgrid-apikey",
"direction": "out",
"from":"<the-email-address-of-sender>"
}
],
"disabled": false
}
Result:
Additionally, for creating Functions class library project via VS2017, you could refer to this tutorial about SendGrid output.

Some of those errors are compilation errors - fix those first. For example, you're missing a ')' on line 28.
You can also author functions in Visual Studio - which will give you the power of a real IDE with C# intellisense and error checking. That would catch the errors above. That's useful as soon as your functions are non-trivial. Check out details here:
https://blogs.msdn.microsoft.com/appserviceteam/2017/08/14/azure-functions-tools-released-for-visual-studio-2017-update-3/
The SendGrid binding should be on your Run() function.
public void Run(TimerInfo myTimer, TraceWriter log, out Mail message)
And then Run1 is just an internal helper to generate the message.
If you need to send multiple messages, use ICollector / IAsyncCollector. That has an 'add' method.

Related

Large http payloads aren't getting sent to the Serilog Http Sink endpoint in .NET Core 3.1

Summary
I'm having trouble posting from Serilog (Http Sink) to my custom .NET Core 3.1 WebAPI endpoint when the logging data is large. If I remove some log data when I do the logging, then Serilog sinks properly with my WebAPI endpoint.
My Configuration
new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Http(httpPath, httpClient: new CustomHttpClient(), batchPostingLimit: int.MaxValue, queueLimit: int.MaxValue)
.CreateLogger();
My Custom Http Client
public class CustomHttpClient : IHttpClient
{
private readonly HttpClient c_httpClient;
public CustomHttpClient()
{
c_httpClient = new HttpClient
{
MaxResponseContentBufferSize = 2147483647L
};
}
public void Configure(IConfiguration configuration)
{
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content) => c_httpClient.PostAsync(requestUri, content);
public void Dispose() => c_httpClient?.Dispose();
}
What actually does the logging
var exceptionModel = new AppMonModel
{
Application = "SerilogMvc Sample Application",
Message = ex.Message,
Source = "SerilogMvc.HomeController.Index",
StackTrace = ex.StackTrace,
InnerException = ex.InnerException?.StackTrace,
Details = "Sample details here",
InsertDate = DateTime.Now,
Severity = 100,
UserDescription = "Keyvan User",
ScreenshotBase64String = Convert.ToBase64String(System.IO.File.ReadAllBytes("C:/SamplePath/Untitled.png"))
};
c_logger.LogError(ex, "{exceptionModel}", exceptionModel);
My Endpoint
[HttpPost("log")]
[DisableRequestSizeLimit]
public void Log([FromBody] object logEvents) { ... }
Serilog Error
Event JSON representation exceeds the byte size limit of 262144 set for this sink and will be dropped;
Issue
When I remove ScreenshotBase64String = Convert.ToBase64String(System.IO.File.ReadAllBytes("C:/SamplePath/Untitled.png")) from my exceptionModel object, I see the error in my WebAPI endpoint. As soon as I add it back in, it doesn't even hit the endpoint.
Please let me know if you need additional details. I'd be more than glad to provide them.
The answer was quite simple after turning on Self logging. This is the change I needed to make to increase the batch formatter size:
var defaultBatchFormatter = new DefaultBatchFormatter(batchFormatterSize);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.Enrich.FromLogContext()
.WriteTo.Http(httpPath, batchFormatter: defaultBatchFormatter)
.CreateLogger();
The batch formatter size needed to be increased.
Need to add eventBodyLimitBytes
enter image description here
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl, eventBodyLimitBytes: 1048576)

Programatically Invoking LUIS Outside the Message Controler

I'm writting a bot application that uses a Prompt Dialog to interact with users.
The idea here is when a user selects a certain option a message should be sent to LUIS that will be processing the request via ML.
Thought about two ways to get it done.
1 - Invoke LUIS directly
2 - Simulate a user entry to make sure the message would pass by the Message Controller and finally the Root Dialog which will be making a LUIS call
Makes sense?
I've tried something like this but it didn't work.
public virtual async Task ChoiceReceivedAsync_MainMenuOption(IDialogContext context, IAwaitable<MainMenuOption> activity)
{
...
IMessageActivity message = Activity.CreateMessageActivity();
message.Text = "Como e a seguranca da escola?";
message.TextFormat = "plain";
message.Locale = "en-Us";
var luisAttributes = new LuisModelAttribute(BellaMain.GlobalVariable.LuisModelID, BellaMain.GlobalVariable.LuisSubscriptionKey);
var luisService = new LuisService(luisAttributes);
await Conversation.SendAsync(message, () => new Dialogs.RootDialog(luisService));
}
Any ideas?
Thanks
Trying to call LUIS by mocking up a new Activity to send to the bot is not recommended.
Instead you should call LUIS through a simple request using an HttpClient.
Here is a sample from the LUIS Endpoint API:
Copy-pasted example:
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Net.Http;
using System.Web;
namespace CSHttpClientSample
{
static class Program
{
static void Main()
{
MakeRequest();
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
static async void MakeRequest()
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
// Request parameters
queryString["timezoneOffset"] = "{number}";
queryString["verbose"] = "{boolean}";
queryString["spellCheck"] = "{boolean}";
queryString["staging"] = "{boolean}";
queryString["bing-spell-check-subscription-key"] = "{string}";
queryString["log"] = "{boolean}";
var uri = "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/{appId}?q={q}&" + queryString;
var response = await client.GetAsync(uri);
}
}
}

How create bug work item in visual studio online from wpf app or some other web app?

I have a bugs list managed in a WPF app. I would like to create the bug in the VSO work item. Is there an API available to create work items like bug or task in Visual Studio Online?
Yes, there has the REST API to create a work item. The example code as:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
...
public void CreateBug()
{
string _personalAccessToken = "your personal access token";
string _credentials = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", _personalAccessToken)));
Object[] patchDocument = new Object[4];
patchDocument[0] = new { op = "add", path = "/fields/System.Title", value = "Authorization Errors" };
patchDocument[1] = new { op = "add", path = "/fields/Microsoft.VSTS.TCM.ReproSteps", value = "Our authorization logic needs to allow for users with Microsoft accounts (formerly Live Ids) - http://msdn.microsoft.com/en-us/library/live/hh826547.aspx" };
patchDocument[2] = new { op = "add", path = "/fields/Microsoft.VSTS.Common.Priority", value = "1" };
patchDocument[3] = new { op = "add", path = "/fields/Microsoft.VSTS.Common.Severity", value = "2 - High" };
//use the httpclient
using (var client = new HttpClient())
{
//set our headers
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", _credentials);
//serialize the fields array into a json string
var patchValue = new StringContent(JsonConvert.SerializeObject(patchDocument), Encoding.UTF8, "application/json-patch+json");
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, "https://accountname.visualstudio.com/fabrikam/_apis/wit/workitems/$Bug?api-version=2.2") { Content = patchValue };
var response = client.SendAsync(request).Result;
//if the response is successfull, set the result to the workitem object
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
}
}
}
More details, you can refer create bug.

Xamarin.Forms Consume Rest Service

I'm new to Xamarin and developing native apps in general (I have made html5 apps in the past).
I have started on a Xamarin.Forms project and I'm trying to contact a REST like API (need to GET an URL which will return a json array).
Normally from C# I would use RestSharp and perform this call using the RestClient.
I'm not having any luck installing that package from Xamarin Studio though, but I have got the Microsoft HTTP Libraries installed.
I'm pretty sure this is a very trivial task to perform, I just haven't been able to adapt the samples I have found online to work for me.
Anyone who could post how this is done please (remember I'm new to this so don't expect me to understand everything that is different from say a normal console app)?
It is easy with HTTP Client and JSON.NET here is a example of a GET:
public async Task<List<Appointment>> GetDayAppointments(DateTime day)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + App.apiToken);
//Your url.
string resourceUri = ApiBaseAddress;
HttpResponseMessage result = await client.GetAsync (resourceUri, CancellationToken.None);
if (result.IsSuccessStatusCode) {
try {
return GetDayAppointmentsList(result);
} catch (Exception ex) {
Console.WriteLine (ex.Message);
}
} else {
if(TokenExpired(result)){
App.SessionExpired = true;
App.ShowLogin();
}
return null;
}
return null;
}
private List<Appointment> GetDayAppointmentsList(HttpResponseMessage result){
string content = result.Content.ReadAsStringAsync ().Result;
JObject jresponse = JObject.Parse (content);
var jarray = jresponse ["citas"];
List<Appointment> AppoinmentsList = new List<Appointment> ();
foreach (var jObj in jarray) {
Appointment newApt = new Appointment ();
newApt.Guid = (int)jObj ["id"];
newApt.PatientId = (string)jObj ["paciente"];
newApt.Name = (string)jObj ["nombre"];
newApt.FatherLstName = (string)jObj ["paterno"];
newApt.MotherLstName = (string)jObj ["materno"];
string strStart = (string)jObj ["horaIni"];
TimeSpan start;
TimeSpan.TryParse (strStart, out start);
newApt.StartDate = start;
string strEnd = (string)jObj ["horaFin"];
TimeSpan end;
TimeSpan.TryParse (strEnd, out end);
newApt.EndDate = end;
AppoinmentsList.Add (newApt);
}
return AppoinmentsList;
}
I use System.Net.WebClient and our asp.net WebAPI interface:
public string GetData(Uri uri)
{//uri like "https://webapi.main.cz/api/root"
string ret = "ERROR";
try
{
using (WebClient webClient = new WebClient())
{
//You can set webClient.Headers there
webClient.Encoding = System.Text.Encoding.UTF8;
ret = webClient.DownloadString(uri));//Test some data received
//In ret you can have JSON string
}
}
catch (Exception ex) { ret = ex.Message; }
return ret;
}
4
public string SendData(Uri uri, byte[] data)
{//uri like https://webapi.main.cz/api/PostCheckLicence/
string ret = "ERROR";
try
{
using (WebClient webClient = new WebClient())
{
webClient.Headers[HttpRequestHeader.Accept] = "application/octet-stream";
webClient.Headers[HttpRequestHeader.ContentType] = "text/bytes";
webClient.Encoding = System.Text.Encoding.ASCII;
byte[] result = webClient.UploadData(uri, data);
ret = Encoding.ASCII.GetString(result);
if (ret.Contains("\"ResultWebApi\":\"OK"))
{//In ret you can have JSON string
}
else
{
}
}
}
catch (Exception ex) { ret = ex.Message; }
return ret;
}
x
I've some examples in my Github repo. Just grab the classes there and give them a try. The API is really easy to use:
await new Request<T>()
.SetHttpMethod(HttpMethod.[Post|Put|Get|Delete].Method) //Obligatory
.SetEndpoint("http://www.yourserver.com/profilepic/") //Obligatory
.SetJsonPayload(someJsonObject) //Optional if you're using Get or Delete, Obligatory if you're using Put or Post
.OnSuccess((serverResponse) => {
//Optional action triggered when you have a succesful 200 response from the server
//serverResponse is of type T
})
.OnNoInternetConnection(() =>
{
// Optional action triggered when you try to make a request without internet connetion
})
.OnRequestStarted(() =>
{
// Optional action triggered always as soon as we start making the request i.e. very useful when
// We want to start an UI related action such as showing a ProgressBar or a Spinner.
})
.OnRequestCompleted(() =>
{
// Optional action triggered always when a request finishes, no matter if it finished successufully or
// It failed. It's useful for when you need to finish some UI related action such as hiding a ProgressBar or
// a Spinner.
})
.OnError((exception) =>
{
// Optional action triggered always when something went wrong it can be caused by a server-side error, for
// example a internal server error or for something in the callbacks, for example a NullPointerException.
})
.OnHttpError((httpErrorStatus) =>
{
// Optional action triggered when something when sending a request, for example, the server returned a internal
// server error, a bad request error, an unauthorize error, etc. The httpErrorStatus variable is the error code.
})
.OnBadRequest(() =>
{
// Optional action triggered when the server returned a bad request error.
})
.OnUnauthorize(() =>
{
// Optional action triggered when the server returned an unauthorize error.
})
.OnInternalServerError(() =>
{
// Optional action triggered when the server returned an internal server error.
})
//AND THERE'S A LOT MORE OF CALLBACKS THAT YOU CAN HOOK OF, CHECK THE REQUEST CLASS TO MORE INFO.
.Start();
And there's a couple of examples.
For all my Xamarin Forms app I use Tiny.RestClient.
It's easy to get it and easy to use it.
You have to download this nuget.
And after it just very easy to use it :
var client = new TinyRestClient(new HttpClient(), "http://MyAPI.com/api");
var cities = client.
GetRequest("City").
AddQueryParameter("id", 2).
AddQueryParameter("country", "France").
ExecuteAsync<City>> ();
Hopes that helps.

Calling WorkFlow Soap Service from PCL

I have a WorkFlow Service hosted in a server: http://myServer.net/MyWorkflowService.xamlx
and it's working normally, I called it from a Windows Phone app before and working.
Now, I wanted to call it from a PCL Project (profile 78) for Xamarin.
I got this error:
A correlation query yielded an empty result set. Please ensure
correlation queries for the endpoint are correctly configured.
I added it as a service reference, and I call an Async Method, and subscribes for completed event:
example
TaskCompletionSource<MyResponse> tsk = new TaskCompletionSource<MyResponse>();
WorkFlowService.SubmitModel serviceModel = new WorkFlowService.SubmitModel()
{
List = MyList.ToArray<string>(),
Guid = Guid,
Description = Description,
userid = UserId
};
WorkFlowClient.SubmitCompleted += (sender, eventArgs) => {
if (eventArgs.Error != null)
{
Debug.WriteLine("Exception : DataService : Adding New" + eventArgs.Error.Message);
tsk.TrySetResult(new MyResponse() {
HasError = true
});
}
else
{
tsk.TrySetResult(new MyResponse()
{
HasError = false
});
}
};
WorkFlowClient.SubmitAsync(new WorkFlowService.SubmitRequest((serviceModel)));
return tsk.Task;
I should send an array of strings with my request, Should I provide ServiceReferences.ClientConfig file and what is the build action for it inside the PCL?!