I have been following some tutorials (e.g., https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client) and trying to retrieve a list of jobs and print them out in my .Net console app.
The test data I am using is located at https://boards-api.greenhouse.io/v1/boards/vaulttec/jobs and supplied it to client.BaseAddress.
Since I am able to compile and run the tutorial succesfully, I simply used the same code and changed some of it to run the above test data (see below).
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace GreenhouseJobs
{
public class Job
{
public string Id { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public DateTime LastUpdated { get; set; }
}
class GreenhouseJobsClient
{
static HttpClient client = new HttpClient();
static void ShowJob(Job job)
{
Console.WriteLine($"Id: {job.Id}\tTitle: " +
$"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
}
static async Task<Uri> CreateJobAsync(Job job)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"vaulttec/jobs", job);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
static async Task<Job> GetJobAsync(string path)
{
Job job = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
job = await response.Content.ReadAsAsync<Job>();
}
return job;
}
//static async Task<Product> UpdateProductAsync(Product product)
//{
// HttpResponseMessage response = await client.PutAsJsonAsync(
// $"api/products/{product.Id}", product);
// response.EnsureSuccessStatusCode();
// // Deserialize the updated product from the response body.
// product = await response.Content.ReadAsAsync<Product>();
// return product;
//}
//static async Task<HttpStatusCode> DeleteProductAsync(string id)
//{
// HttpResponseMessage response = await client.DeleteAsync(
// $"api/products/{id}");
// return response.StatusCode;
//}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
Console.ReadLine();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
Job job = new Job
{
Id = "323232",
Title = "Test",
Location = "Test",
LastUpdated = DateTime.Now
};
var url = await CreateJobAsync(job);
Console.WriteLine($"Created at {url}");
// Get the product
job = await GetJobAsync(url.PathAndQuery);
ShowJob(job);
// Update the product
//Console.WriteLine("Updating price...");
//product.Price = 80;
//await UpdateProductAsync(product);
// Get the updated product
//product = await GetProductAsync(url.PathAndQuery);
//ShowProduct(product);
// Delete the product
//var statusCode = await DeleteProductAsync(product.Id);
//Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
The problem is, when I run the app, it does not return anything. Error message: "Response status code does not indicate success: 404 (Not Found)".
There are couple issues here
You are not just trying to retrieve but also create first. I am not sure if this api supports a POST or not, it probably doesn't, hence a 404
The Job entity is incorrect. Location needs to be an object itself and not a mere string if you need it deserialized correctly. Here is a working version of get call
https://pastebin.com/85NnAQY9
namespace ConsoleApp3
{
public class JobsJson
{
public List<Job> Jobs { get; set; }
}
public class Job
{
public string Id { get; set; }
public string Title { get; set; }
public Location Location { get; set; }
public DateTime LastUpdated { get; set; }
}
public class Location
{
public string Name { get; set; }
}
class GreenhouseJobsClient
{
static HttpClient client = new HttpClient();
static void ShowJobs(List<Job> jobs)
{
foreach (var job in jobs)
{
Console.WriteLine($"Id: {job.Id}\tTitle: " +
$"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
}
}
static async Task<List<Job>> GetJobAsync(string path)
{
var jobs = new List<Job>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
var re = JsonConvert.DeserializeObject<JobsJson>(stringResponse);
jobs = re.Jobs;
}
return jobs;
}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
Console.ReadLine();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Get the product
var jobs = await GetJobAsync("vaulttec/jobs");
ShowJobs(jobs);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
The 404 is being returned by this line of code:
HttpResponseMessage response = await client.PostAsJsonAsync("vaulttec/jobs", job);
This is because the URL https://boards-api.greenhouse.io/v1/boards/vaulttec/jobs returns 404 when you try and make a POST request. Probably you need to be authorised to create jobs.
You can however make a GET request to this URL just fine.
Related
So, I have been struggling with this for a while now, but I am getting a bit further each day. But now I am really stuck.
What I am trying to do:
I have an automation account with a runbook (PowerShell)
I want to create a schedule
Then I want to connect that schedule to the runbook so the runbook is executed at a specific date and time.
I want to achieve this through C# with the Management API of Azure. I found a few webpages that should give me all the information I needed, but it doesn't work.
The first one is this: https://learn.microsoft.com/en-us/rest/api/automation/schedule/create-or-update?tabs=HTTP
I get this to work and I can see the schedule after I ran the code.
The second one is this: https://learn.microsoft.com/en-us/rest/api/automation/job-schedule/create?tabs=HTTP
And here I get stuck. I get a 404 error when I execute the request and I have no idea what I am doing wrong.
The code below is far from perfect, but it's a POC and not the actual application. I want to make it work before I refactor.
This class has all the logic for the schedule:
public class ScheduleLogic
{
const string subscriptionId = "######";
const string resourceGroupName = "######";
const string automationAccountName = "######";
const string tenantId = "######";
const string clientId = "######";
const string clientSecret = "######";
const string resourceId = "https://management.azure.com/";
public string accessToken { get; set; }
// THIS ONE DOESN'T WORK!
public async Task ConnectScheduleToRunbook(RunbookSchedule runbookSchedule)
{
StringContent body = new(JsonSerializer.Serialize(runbookSchedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/jobSchedules/{runbookSchedule.Properties.Schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task CreateSchedule(Schedule schedule)
{
if (string.IsNullOrEmpty(schedule.JobScheduleId))
throw new Exception("Job schedule ID is empty");
StringContent body = new(JsonSerializer.Serialize(schedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task DeleteSchedule(string scheduleName)
{
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{scheduleName}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Delete, url, null);
}
private async Task ExecuteRequest(RequestType requestType, string url, StringContent? body)
{
HttpResponseMessage response;
using (HttpClient client = new())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {await GetAccessToken()}");
switch (requestType)
{
case RequestType.Put:
if (body == null)
throw new Exception("Body cannot be empty");
response = await client.PutAsync(url, body);
break;
case RequestType.Delete:
response = await client.DeleteAsync(url);
break;
default:
throw new Exception("Unknown request type");
}
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
throw new Exception(content);
}
}
}
private async Task<string> GetAccessToken()
{
if (!string.IsNullOrEmpty(accessToken))
return accessToken;
AuthenticationContext authContext = new("https://login.microsoftonline.com/" + tenantId);
ClientCredential clientCreds = new(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(resourceId, clientCreds);
accessToken = authResult.AccessToken;
return accessToken;
}
}
Models:
public class RunbookSchedule
{
public RunbookScheduleProperties Properties { get; set; }
}
public class RunbookScheduleProperties
{
public Schedule Schedule { get; set; }
public Runbook Runbook { get; set; }
public object Parameters { get; set; }
}
public class Runbook
{
public string Name { get; set; }
}
public class Schedule
{
public string Name { get; set; }
[JsonIgnore]
public string JobScheduleId { get; set; }
[JsonPropertyName("properties")]
public ScheduleProperties ScheduleProperties { get; set; }
}
public class ScheduleProperties
{
public string Description { get; set; }
public DateTime StartTime { get; set; }
public DateTime ExpiryTime { get; set; }
public string Frequency { get; set; }
public object AdvancedSchedule => new { };
}
And the unit tests I use to test the logic:
public class ScheduleTests
{
private readonly string jobId = "d52004cc-b7ec-4b9b-99c1-3922492f6e1b-1";
private readonly string runbookName = "RunFunctionApp";
private readonly ScheduleLogic scheduleService;
public ScheduleTests()
{
scheduleService = new();
}
[Fact]
public async Task Should_CreateASchedule()
{
Schedule newScheduleSettings = new()
{
Name = jobId,
JobScheduleId = jobId,
ScheduleProperties = new()
{
Description = "Here is another example",
StartTime = new DateTime(2024, 03, 27, 9, 0, 0, 0),
ExpiryTime = new DateTime(2024, 03, 27, 10, 0, 0, 0),
Frequency = "OneTime"
}
};
await scheduleService.CreateSchedule(newScheduleSettings);
}
[Fact]
public async Task Should_DeleteSchedule()
{
await scheduleService.DeleteSchedule(jobId);
}
[Fact]
public async Task Should_ConnectScheduleAndRunbook()
{
await scheduleService.ConnectScheduleToRunbook(new RunbookSchedule
{
Properties = new()
{
Schedule = new() { Name = jobId, JobScheduleId = jobId },
Runbook = new() { Name = runbookName },
Parameters = new { id = 12 }
}
});
}
}
Somehow I think I am doing something stupid and the fix/answer is really simple.
I tried to mess around with the URL because I think there is a problem with that, not sure why... Just a feeling.
Google did show me some results, but those were for schedules of other services. Runbooks and automation don't seem to be very popular.
ChatGPT comes with older, obsolete solutions. Visual Studio gives a lot of green and even red lines when I try those suggestions.
I have the following. It seems to not be waiting after the request has been sent out in the GetAllProjects. It waits for the response, then doesn't wait for the response content to be read in.
NOTE: using nuget package for ReadAsAsync (could have used a json object to deserialize however)
public class Project
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? Url { get; set; }
}
public class Main
{
public static void Main(string[] args)
{
AzureClient ac = new AzureClient();
var projects = ac.GetAllProjects();
}
}
public class AzureClient
{
private readonly HttpClient _client;
private const string PAT = "myToken";
private const string API_VERSION = "api-version=5.0";
public AzureClient()
{
_client = new HttpClient()
{
BaseAddress = new Uri("some uri"),
Timeout = TimeSpan.FromSeconds(30)
};
// added media type and passed in auth token to _client. client returns 200 on requests
}
public async Task<ICollection<Project>> GetAllProjects()
{
var response = await _client.GetAsync("_apis/projects?{API_VERSION }");
var projects = await response.Content.ReadAsAsync<dynamic>();
return projects.value.ToObject<ICollection<Project>>();
}
}
You are missing an await here
var projects = await ac.GetAllProjects();
you will also need to make your Main method async
I trie to pass my item Reach_DeclarationBc
public class Reach_DeclarationBc
{
public int ArticleId { get; set; }
public DateTime DateDeclaration { get; set; }
}
I use this code for call my api
var client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:3001/");
Reach_DeclarationBc reach_DeclarationBc = new Reach_DeclarationBc
{
ArticleId = 129,
DateDeclaration = DateTime.Now
};
Reach_DeclarationBc result = await client.PostJsonAsync<Reach_DeclarationBc>("http://localhost:3009/reach", reach_DeclarationBc);
But a this line this give me an error
Reach_DeclarationBc result = await client.PostJsonAsync<Reach_DeclarationBc>("http://localhost:3009/reach", reach_DeclarationBc);
The error is : "TypeLoadException: Could not load type 'Microsoft.JSInterop.Json' from assembly 'Microsoft.JSInterop, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'."
The using in my class is
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Projet_Airbus.Data;
using Projet_Airbus.Models;
using Projet_Airbus.Models.Declaration;
using Microsoft.AspNetCore.Blazor;
using System.Net.Http;
For solve i tri to use asp.net core 3.1 but this not work
This is how I usually do it.
I inject a singleton HttpClient and logger with a dependency injector like Ninject.
I make a generic SendAsync which can handle PUT/DELETE/POST/GET.
I make ApiResponse class that contains the properties I'm interested in.
I create a Request class (InitRequest) that I use for the request.
I create a Response class (InitResponse) that I use for the response.
I have TimeOutInMs to set the time out for the api call.
I have a logger that logs error.
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
using Newtonsoft.Json;
public class HomeController : Controller
{
private readonly WebApi _webApi;
//Inject singleton httpclient and logger
public HomeController(HttpClient httpClient, ILogger logger)
{
_webApi = new WebApi(httpClient, logger);
_webApi.BaseAddress = "https://www.webapi.com/";
_webApi.TimeOutInMs = 2000;
}
public async Task<ActionResult> Index()
{
//You might want to move the web api call to a service class
var method = "init";
var request = new InitRequest
{
Id = 1,
Name = "Bob"
};
var response = await _webApi.SendAsync<InitResponse>(method, request, HttpMethod.Post);
if (response.StatusCode == HttpStatusCode.OK)
{
var viewModel = response.Data.ToIndexViewModel();
}
else
{
//Handle Error
}
return View(viewModel);
}
}
public class IndexViewModel
{
public string Name { get; set; }
public string Address { get; set; }
}
public static class ModelMapper
{
public static IndexViewModel ToIndexViewModel(this InitResponse response)
{
return new IndexViewModel
{
Name = response.Name,
Address = response.Address
};
}
}
public class InitRequest
{
public int Id { get; set; }
public string Name { get; set; }
}
public class InitResponse
{
public string Name { get; set; }
public string Address { get; set; }
}
public class WebApi
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public Uri BaseAddress { get; set; }
public int TimeOutInMs { get; set; }
public WebApi(HttpClient httpClient, ILogger logger)
{
_logger = logger ?? throw new Exception($"Missing constructor ceference - {nameof(logger)} can not be null!");
_httpClient = httpClient ?? throw new Exception($"Missing constructor ceference - {nameof(httpClient)} can not be null!");
}
public async Task<ApiResponse<TOut>> SendAsync<TOut>(string method, object param, HttpMethod httpMethod)
{
if (string.IsNullOrWhiteSpace(BaseAddress.ToString()))
throw new Exception($"{nameof(BaseAddress)} can not be null or empty.");
if (string.IsNullOrWhiteSpace(method))
throw new Exception($"{nameof(method)} can not be null or empty.");
var paramListForLog = JsonConvert.SerializeObject(param);
//Set timeout
if (TimeOutInMs <= 0)
{
TimeOutInMs = (int)TimeSpan.FromSeconds(100.0).TotalMilliseconds;
}
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(TimeOutInMs));
var cancellationToken = cts.Token;
var url = new Uri($"{BaseAddress}{method}", UriKind.Absolute);
try
{
HttpResponseMessage response;
using (var request = new HttpRequestMessage(httpMethod, url))
{
//Add content
if (param != null)
{
var content = JsonConvert.SerializeObject(param);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
}
//Add headers
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_logger.Info($"Calling {httpMethod} for {url}", paramListForLog);
//Send the request
response = await _httpClient.SendAsync(request, cancellationToken);
}
//If success
if (response.IsSuccessStatusCode)
{
_logger.Info($"Successfully called {httpMethod} for {url}", paramListForLog);
var data = await response.Content.ReadAsAsync<TOut>(cancellationToken);
return new ApiResponse<TOut>
{
StatusCode = response.StatusCode,
Data = data
};
}
//If failure
var error = await response.Content.ReadAsStringAsync();
_logger.Error($"An error occured calling {httpMethod} for {url}. Error was {error}", paramListForLog);
return new ApiResponse<TOut>
{
StatusCode = response.StatusCode,
Message = error
};
}
//If timeout
catch (OperationCanceledException ex)
{
var message = cancellationToken.IsCancellationRequested ?
$"Request timed out after {TimeOutInMs} ms occured calling {httpMethod} for {url}. Error was: {ex.Message}" :
$"An error occured calling {httpMethod} for {url}. Error was: {ex.Message}";
var webEx = new Exception(message, ex);
_logger.Error(webEx, webEx.Message, paramListForLog);
return new ApiResponse<TOut>
{
StatusCode = HttpStatusCode.RequestTimeout,
Message = message
};
}
//If unknown error
catch (Exception ex)
{
var webEx = new Exception($"An error occured calling {httpMethod} for {url}. Error was: {ex.Message}", ex);
_logger.Error(webEx, webEx.Message, paramListForLog);
throw webEx;
}
}
}
public interface ILogger
{
void Info(string message, string param);
void Error(string message, string param);
void Error(Exception e, string message, string param);
}
public class ApiResponse<T>
{
public HttpStatusCode StatusCode { get; set; }
public string Message { get; set; }
public T Data { get; set; }
}
I have 2 viewmodel, aviewmodel and bviewmodel. in aviewmodel and bviewmodel are used method GetListEmployees(). i have a problem.
In aviewmodel,I have successfully run the GetListEmployees().but in bviewmodel then it does not work. I debug and I see it was thrown when it came to response. I don't know why it's like that.
video description. https://youtu.be/CD3lHKx3igs
Function to get the list.
async private Task GetListEmployees()
{
var result = await GeneralMethods.Ins.GetDataFromDB<Employee>("Employee/employees");
// ListEmployee = new ObservableCollection<Employee>(result);
}
This is class read api.
public class GeneralMethods
{
private static GeneralMethods _Ins;
public static GeneralMethods Ins
{
get
{
if (_Ins == null)
_Ins = new GeneralMethods();
return GeneralMethods._Ins;
}
private set
{
GeneralMethods._Ins = value;
}
}
public async Task<List<T>> GetDataFromDB<T>(string urlDB)
{
string url = "http://webapi.local/api/" + urlDB;
using (HttpResponseMessage response = await APIHelper.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsAsync<List<T>>().Result;
return result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
}
APIHelper
public static class APIHelper
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
I am having trouble with adding both my AAD1 Authentication and custom Conversation logger onto my ChatBot. One or the other works fine, but when combining the two, I get HTTP timeouts. Any assistance would be greatly appreciated. Relevant code below:
Global.asax.cs
protected void Application_Start()
{
// Adding DocumentDB endpoint and primary key
var docDbServiceEndpoint = new Uri("-----------------------------------");//REMOVED Uri for question, no issue with connection as is
var docDbKey = "--------------------------------------------"; //REMOVED Key for question, no issue with connection as is
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new DocumentDbBotDataStore(docDbServiceEndpoint, docDbKey); // requires Microsoft.BotBuilder.Azure Nuget package
builder.RegisterType<DebugActivityLogger>().AsImplementedInterfaces().InstancePerDependency();
});
//authorization stuff
AuthSettings.Mode = ConfigurationManager.AppSettings["ActiveDirectory.Mode"];
AuthSettings.EndpointUrl = ConfigurationManager.AppSettings["ActiveDirectory.endpointUrl"];
AuthSettings.Tenant = ConfigurationManager.AppSettings["ActiveDirectory.Tenant"];
AuthSettings.RedirectUrl = ConfigurationManager.AppSettings["ActiveDirectory.RedirectUrl"];
AuthSettings.ClientId = ConfigurationManager.AppSettings["ActiveDirectory.ClientId"];
AuthSettings.ClientSecret = ConfigurationManager.AppSettings["ActiveDirectory.ClientSecret"];
GlobalConfiguration.Configure(WebApiConfig.Register);
}
AuthenticationHelper.cs
[Serializable]
public class AuthenticationHelper : IDialog<string>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(ProcessMessageAsync);
}
public async Task ProcessMessageAsync(IDialogContext context, IAwaitable<IMessageActivity> item)
{
var message = await item;
if (string.IsNullOrEmpty(await context.GetAccessToken("https://graph.microsoft.com/")))
{
//NO ACCESS TOKEN, GET IT
await context.Forward(new AzureAuthDialog("https://graph.microsoft.com"), this.ProcessAuthResultAsync, message, System.Threading.CancellationToken.None);
}
else
{
//have token
await context.Forward(new LuisAskQuestionDialog(), this.QuitMessageReceivedAsync, message, System.Threading.CancellationToken.None);
}
}
public async Task ProcessAuthResultAsync(IDialogContext context, IAwaitable<string> result)
{
var message = await result;
await context.PostAsync(message);
context.Wait(ProcessMessageAsync);
}
protected async Task QuitMessageReceivedAsync(IDialogContext context, IAwaitable<object> item)
{
var message = await item;
//StartRecordingProcess();
context.Done(message);
}
}
ChatBotLogging.cs
public class DebugActivityLogger : IActivityLogger
{
private const string EndpointUrl = "------------------------------";
private const string PrimaryKey = "------------------------------------";
private DocumentClient client;
// ADD THIS PART TO YOUR CODE
public async Task LogAsync(IActivity activity)
{
//Update this information
//What this needs to have: ConversationID, From, To, Date, Message
//Get all the texts information ready for upload;
//Get connection to table
//upload the inforamtion onto the table
//disconnect from the table
// Retrieve the storage account from the connection string.
//This Task is called to intercept messages
var fromid = activity.From.Id;
var toId = activity.Recipient.Id;
var chatMessage = activity.AsMessageActivity()?.Text;
var timeStamp = activity.Timestamp;
var conversationId = activity.Conversation.Id;
//timestamp converted to string.
string strTimeStamp = timeStamp.ToString();
try
{
this.client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);
await this.client.CreateDatabaseIfNotExistsAsync(new Database { Id = "botdb" });
await this.client.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri("botdb"), new DocumentCollection { Id = "botcollection" });
ChatLogEntity chatLog1 = new ChatLogEntity
{
TimeStamp = strTimeStamp,
ConversationId = conversationId,
FromID = fromid,
ToID = toId,
ChatMessage = chatMessage
};
await this.CreateChatDocumentIfNotExists("botdb", "botcollection", chatLog1);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
//entity class for demo purposes
// ADD THIS PART TO YOUR CODE
private async Task CreateChatDocumentIfNotExists(string databaseName, string collectionName, ChatLogEntity chatEntity)
{
try
{
await this.client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseName, collectionName, chatEntity.TimeStamp));
}
catch (DocumentClientException de)
{
if (de.StatusCode == HttpStatusCode.NotFound)
{
await this.client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseName, collectionName), chatEntity);
}
else
{
throw;
}
}
}
public class ChatLogEntity
{
[JsonProperty(PropertyName = "timestamp")]
public string TimeStamp { get; set; }
public string ConversationId { get; set; }
public string ToID { get; set; }
public string FromID { get; set; }
public string ChatMessage { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
The AuthBot package has been discontinued. As mentioned in the comments, You should use the BotAuth for AADv1 authentication. The BotAuth supports the state data conversation support (so you will not get the deprecated state client warnings too).