I have the following piece of code that retrieves transactions from a Dynamics CRM (querying with OData):
public async Task<IEnumerable<Transaccion>> GetTransactions()
{
var tableName = Transaccion.CrmTableName;
var request = new RestRequest($"/api/data/v8.0/{tableName}");
request.AddHeader("Prefer", "odata.maxpagesize=500");
var responseData = await client.ExecuteGetTaskAsync<ODataResponse<List<Transaccion>>>(request);
var transactions = responseData.Data.Value;
while (responseData.Data.NextLink != null)
{
request = new RestRequest(responseData.Data.NextLink);
request.AddHeader("Prefer", "odata.maxpagesize=500");
responseData = await client.ExecuteGetTaskAsync<ODataResponse<List<Transaccion>>>(request);
transactions.AddRange(responseData.Data.Value);
}
return transactions;
}
once I execute the first "ExecuteGetTaskAsync", I get for my example and as expected a NextLink attribute that points to the next set of entities that I need to retrieve. However, when I try to perform the next RestRequest, I don't get a JSON as response, but a Html page corresponding to a redirect, where I can read the error message "".
It's weird, since the first call could be made correctly because the Restclient was correctly authenticated.
What's going on? How can I do paging with Dynamics CRM in .Net and use the NextLink?
In my case URL in #odata.nextLink was with an error.
How it was:
http://[Organization URI]/api/data/v8.2/[entity]/(68e95f08-d372-e711-966b-defe0719ce9e)/[relation entity]?$select=ne_name
And that did not work, but this did:
http://[Organization URI]/api/data/v8.2/[entity](68e95f08-d372-e711-966b-defe0719ce9e)/[relation entity]?$select=ne_name
There is no "/" between [entity] and (id)
The odada nextlink returns the full URL of the next request so you'll need to parse it to get only the /api/** portion.
Related
Is there any way I can get the email activity from my API key without a limit? According to the documentation, the limit parameter is not required, but any time I don't specify a limit I get a BadRequest response.
public async Task<SentEmailModel> GetEmails()
{
var client = new SendGridClient("SENDGRID_API_KEY");
var queryParams = #"{
'limit': 100 //I dont't want to specify a limit, since I want to get the full list
}";
var response = await client.RequestAsync(method: SendGridClient.Method.GET, urlPath: "messages", queryParams: queryParams);
if (response.IsSuccessStatusCode)
{
var responseString = response.Body.ReadAsStringAsync().Result;
var responseMessages = JsonConvert.DeserializeObject<SentEmailModel>(responseString);
return responseMessages;
}
return null;
}
As you said, the docs say the limit parameter isn't required, but the API returns an error when you don't provide it. I have notified our docs team to fix this.
However, if the limit parameter were optional, it would default to 10 which wouldn't achieve the result you desire.
Some of the APIs you can paginate over to get all the data, but this API does not support pagination.
If you need to go beyond the 1000 limit, I'd recommend setting up the event webhook to receive the data in realtime, and retain the data in your application.
I am connecting to a 3rd party API that requires calls to retrieve an authorization code and a token.
The API requires a redirect URL, which when returned, contains the authorization code attached to the query string.
So, on my side so I set up this controller below that will read the queryString.
Then, my app needs to fire a POST request to the API to get the token, and you will see me calling the controller the contains the POST request.
When I click my button to connect to the API, in my browser window, I do see that I am redirected to the GetGeologicalPeriod() controller below because I see the message:
you have reached the GeologicalPeriod endpoint.
And I do see the authorization code in the query string.
But I don't see anything else, no errors, nothing.
I was expecting to see the results retuned from the call to GetGeologicalPeriodToken, or at least an error that it failed, but I am getting nothing...not even in the browser console window.
So I am kind of at a loss as to what is actually happening.
Since this is on a development server, I can't step through it locally in Visual Studio.
Is there anyway to show messages or write to console so I can see what's going on?
Thanks!
[ApiController]
public class GeologicalPeriodController : ControllerBase
{
[HttpGet]
public ActionResult<String> GetGeologicalPeriod()
{
string getTest = "you have reached the GeologicalPeriod endpoint.";
var queryString = Request.Query["code"];
var postResult = GetGeologicalPeriodToken(queryString);
return postResult;
}
[HttpPost("AuthRequest")]
public ActionResult<String> GetGeologicalPeriodToken(string authCode)
{
string authToken = authCode;
string authString = "admin";
var queryString = Request.Query["code"];
var client = new RestClient("https://geologicalPeriod.geo.gov/oauth/token?accessType=professor&code=" + authCode + "&redirect_uri=https://jamaica.round.astro.edu/api/geologicalPeriodauth/geologicalPeriod/AuthRequest");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", authString);
IRestResponse response = client.Execute(request);
var apiResponse = response;
return apiResponse.Content.ToString();
}
I am trying to use the MS Graph SDK (beta) for C# to get all the groups a user belongs to transitively with a filter to only get "security groups".
My code looks like this:
var upn = "some.body#org.com";
var request = await _graphClient.Users[upn].TransitiveMemberOf
.Request()
.Filter("securityEnabled eq true")
.GetAsync();
When I run this code, I get an error that The specified filter to the reference property query is currently not supported. I know that the same API endpoint can be called successfully using Postman with the same filter so I assume I'm missing something in my C# code?
Please use the below code for filtering the securityEnabled in c# using graph sdk
try
{
List<Option> requestOptions = new List<Option>();
requestOptions.Add(new QueryOption("$count", "true"));
var request = await graphClient.Users["sruthi#xx.live"].TransitiveMemberOf
.Request(requestOptions).Header("ConsistencyLevel", "eventual")
.Filter("securityEnabled eq true")
.GetAsync();
Console.WriteLine(JsonConvert.SerializeObject(request));
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Thanks to Sruthi's answer, I was able to get user transitive memberships. Obviously, this can often result in a huge list and the Graph API will only provide you a paginated response so you have to cycle through every page to get everything.
This is what the code looks like now:
public async Task<List<Group>> ListTransitiveGroupMembershipsOfUserAsync(string upn)
{
var requestOptions = new List<Option>
{
new QueryOption("$count", "true")
};
var directoryObjects = new List<DirectoryObject>();
var request = await _graphClient.Users[upn].TransitiveMemberOf
.Request(requestOptions).Header("ConsistencyLevel", "eventual")
.Filter("securityEnabled eq true")
.GetAsync();
directoryObjects.AddRange(request.CurrentPage);
while (request.NextPageRequest != null)
{
var currentHeaders = request.NextPageRequest.Headers;
IUserTransitiveMemberOfCollectionWithReferencesPage newPage;
if (!currentHeaders.Any())
request = await request.NextPageRequest.Header("ConsistencyLevel", "eventual").GetAsync();
else
request = await request.NextPageRequest.GetAsync();
directoryObjects.AddRange(request.CurrentPage);
}
return directoryObjects.Cast<Group>().ToList();
}
Initially, my code failed when it tried to get the results for page 2 onwards because it kept appending eventual to the ConsistencyLevel header. So when sending the HTTP request for page 2, the header was like: ConsistencyLevel: eventual, eventual or ConsistencyLevel: eventual, eventual, eventual for page 3 and so on so forth.
To get around this, I added the little if (!currentHeaders.Any()) block to only add the ConsistencyLevel header if it doesn't already exist.
Hopefully that'll help anyone else who gets caught out by cycling through paginated responses!.
What is the best approach for knowing when a long running Elasticsearch request is complete?
Today I have a process that periodically purges ~100K documents from an AWS hosted ES that contains a total of ~60M documents.
var settings = new ConnectionSettings(new Uri("https://mycompany.es.aws.com"));
settings.RequestTimeout(TimeSpan.FromMinutes(3)); // not sure this helps
var client = new ElasticClient(settings);
var request = new DeleteByQueryRequest("MyIndex") { ... };
// this call will return an IsValid = true, httpstatus = 504 after ~60s,
var response = await client.DeleteByQueryAsync(request);
Even with timeout set to 3 minutes, the call always returns in ~60s with an empty response and a 504 status code. Though through Kibana, I can see that the delete action continues (and properly completes) over the next several minutes.
Is there a better way to request and monitor (wait for completion) a long running ES request?
UPDATE
Based on Simon Lang's response I updated my code to make use of ES Tasks. The final solution looks something like this...
var settings = new ConnectionSettings(new Uri("https://mycompany.es.aws.com"));
settings.RequestTimeout(TimeSpan.FromMinutes(3)); // not sure this helps
var client = new ElasticClient(settings);
var request = new DeleteByQueryRequest("MyIndex")
{
Query = ...,
WaitForCompletion = false
};
var response = await client.DeleteByQueryAsync(request);
if (response.IsValid)
{
var taskCompleted = false;
while (!taskCompleted)
{
var taskResponse = await client.GetTaskAsync(response.Task);
taskCompleted = taskResponse.Completed;
if (!taskCompleted)
{
await Task.Delay(5000);
}
}
}
I agree with #LeBigCat that the timeout comes from AWS and it is not a NEST problem.
But to address your question:
The _delete_by_query request supports the wait_for_completion parameter. If you set it to false, the request returns immediately with a task id. You then can request the task status by the task api.
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
This isnot a nest - elastic problem, the default timeout in nest query is 0 (no timeout).
You got timeout from amazon server (60s default)
https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/ts-elb-error-message.html
This link explain everything you need to know :)
Regarding #Simon Lang's answer, this is also the case with the _update_by_query api. For those unfamiliar with the _tasks api, you can query for your task in Kibana. The string returned by the update or delete by query will be of the form:
{
"tasks" : "nodeId:taskId"
}
and you can view the status of the task using this command in Kibana:
GET _tasks/nodeId:taskId
I have a webApi set up that has a PostStudent method. The API call works fine and creates a new student in the DB, but I can't get it to return the value in the response body. I have tried returning Ok(newStudent) and Created("~/api/poststudent", newStudent) but neither of them have returned the newStudent value that I need.
I have gone through all of the response and can't find the actual newStudent object. Is it there and I am just missing it or do I have a problem with my code?
This is the PostStudent method from the API;
var newStudent = new Student
{
studentId = nextStudentId,
studentFirstName = studentEntry.StudentFirstName,
studentLastName = studentEntry.StudentLastName,
studentDOB = studentEntry.StudentDob,
studentEmergencyContactName = studentEntry.StudentEmergencyContactName,
studentEmergencyContactNum = studentEntry.StudentEmergencyContactNum,
ticketNumber = studentEntry.TicketNumber
};
db.Student.Add(newStudent);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (StudentExists(newStudent.studentId))
return BadRequest("That student id already exists");
throw;
}
return Ok(newStudent.studentId);
// return Created("~/api/poststudent", newStudent);
}
This is where I call postasync and try to save the response body;
var response = client.PostAsync("api/poststudent", content);
return response.Result.Content.ReadAsStringAsync().ToString();
And this is where I want to use the value;
var newStudentId = controller.PostStudent(studentFirstName, studentLastName, studentDob, ticketNumber);
var url = "~/AddGuardian/AddGuardian/" + newStudentId;
Response.Redirect(url);
I hope someone can help me. I never thought redirecting to another page would be so damn hard!
Cheers.
You're not awaiting the async calls:
var response = await client.PostAsync("api/poststudent", content);
return (await response.Result.Content.ReadAsStringAsync()).ToString();
There are 2 official tutorials for quickstarting a ASP.NET WebApi project.
Using Web API 2 with Entity Framework 6
Calling a Web API From a .NET Client (C#)
I want to recommend to work through these. If you find your application is doing things wrong, fix your application towards these samples. If your requirements differ from what is given in the samples, you can emphasize on this in your question.