I have an Durable Azure Function and I was hoping to add data to the response. Just one field really. I cant seem to find any possible way to do it and this is where I am basically leaving off on code wise. I thought about maybe adding to the request a deliminating character but that just adds to the entirety of the string for the urls.
So my question is: Is there a way for me to append a value to the CreateCheckStatusResponse so that way I can have an Id of the object instead of polling against the url for that response?
string instanceId = await starter.StartNewAsync("SendCorrespondence", request);
var correspondenceId = Guid.NewGuid().ToString();
req.HttpContext.Items.Add("correspondenceId", correspondenceId);
req.Headers.Add("correspondenceId", correspondenceId);
var test = starter.CreateCheckStatusResponse(req, instanceId+"."+correspondenceId);
return test;
We can add the object id to the URL in the HttpStart Class and the durable client by default polls the URL in the location header if the function is in Async operation.
To add the object id, this Microsoft documentation may helps you
Related
I'm working with the pipedrive API and trying to update a record (deal, but the endpoint isn't important). The format of the is as follows.
https://companyDomain.pipedrive.com/api/v1/deals/DealID?api_token=API-Token
Where companydomain specifies your account with them, dealID is the ID we're updating and API token is the token supplied by pipedrive to access the API. It's not a token that's returned by logging in, it's static one, given on day one and never changes.
HttpClient seems to want a base address so "https://companyDomain.pipedrive.com/", then something like the following:
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/v1/deals/{dealId}/", args);
Where args is a class with the field/s and value/s I want to update.
If I include the api_token as fields in "args", it returns a 404 not found. If I append it to the base url or the $api/v1/deals/{dealID}/ it returns permission denied.
Any suggestions as to how I can handle this?
I've managed to make a living coding for 30 years avoiding the web, now it's all anyone seems to want. Appears I have to finally get a handle on this, hence the recent questions! ;-)
Thanks in advance
Jim
Append it with a "?"
So your URL should look like api/v1/deals/YOUR_ID?api_token=THE_TOKEN
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/v1/deals/{dealId}?api_token={apiToken}", args);
I am trying to think of a best way to architect my back-end rest api. I have the following requirement:
Client makes a post request with to add a resource that contains an ID and meta information
Before I add this resource to the database I need to make a GET request to a third party API with the ID provided to fetch related data.
Then save the original resource and its related data to the database
Currently in the Repository inside the AddAsync method, before I persist the resource to the database, I make a call to the third party API to GET detail information about the resource based on the ID and THEN perform SaveChangesAsync on the model which has the rest of its properties populated by the GET request.
This however feels wrong since I am making a POST request from my client and then a GET request on the backend server. Is there a better way of solving this?
IAccountRepository:
public async Task<SupervisorResult> AddAsync(Account newAccount, CancellationToken ct = default)
{
// GetAccountDataAsync fetches order data that I need to save whenever new Account is added
SupervisorResult result = await GetAccountDataAsync(newAccount, ct);
if(result.Succeeded == false)
{
_logger.Here().Debug("Failed to get new account data.");
return result;
}
Account freshAccount = (Account)result.Value;
_dbContext.Accounts.Add(freshAccount);
await _dbContext.SaveChangesAsync(ct);
result.Succeeded = true;
result.Value = freshAccount;
return result;
}
From my point of view I don't think it's a problem to do a GET call in that situation.
Probably the best approach should be to manage possible exception from the GetAccountDataAsync.
In case a user send all the correct data, but your external system has some problem to create the account, is not responsibility of your user to "retry" the process.
This improvement could be costly, but should be better in the user experience.
I think the approach described from David (raise up one layer) is correct, so do I prefer to segregate the responsibility of the communication with external system to the application/logical layer.
In case of a partial completion of the saving process the user (Account) should be in a sort of waiting state.
I'm trying to write a webhook for Mailchimp events using API version three and I'm struggling a bit due to their lack of a library, documentation, and basic examples, but also my lack of experience.
I know we should secure the webhook by putting a secret in the URL, that's fine. By the way, MailChimp doesn't allow configuration of basic access authentication in their portal.
They say "While we do send HTTP POST for actual data, our webhook validator will only send HTTP GET requests. You'll need to allow both in order for your webhook to function properly." Ok, I guess I can use Request.HttpMethod to return a success status code if it's a GET and process some data if it's a POST.
Not sure how to pick stuff out of the request though, and ideally don't want to write heaps of classes and properties to cover all the event types, C# being statically typed, although I guess the dynamic keyword is also an option.
Do I need to deserialise JSON? I've only written one webhook before for another API with the help of a library, you could construct an API event using either a string, stream, or textreader, which came from the request. The library made everything very simple.
For reference, there's also this question which shows how to get some data using PHP: How to pass email address to webhook from mailchimp
The data that gets posted looks like this (supposedly, there doesn't seem to be any documentation for V3):
"type": "unsubscribe",
"fired_at": "2009-03-26 21:40:57",
"data[action]": "unsub",
"data[reason]": "manual",
"data[id]": "8a25ff1d98",
"data[list_id]": "a6b5da1054",
"data[email]": "api+unsub#mailchimp.com",
"data[email_type]": "html",
"data[merges][EMAIL]": "api+unsub#mailchimp.com",
"data[merges][FNAME]": "MailChimp",
"data[merges][LNAME]": "API",
"data[merges][INTERESTS]": "Group1,Group2",
"data[ip_opt]": "10.20.10.30",
"data[campaign_id]": "cb398d21d2",
"data[reason]": "hard"
I just basically need to get this data into variables so I can sync it with my database.
Here's my (skeleton) controller so far:
[Route("mailchimp/newsletter-webhook/super-secret-key-goes-here")]
public HttpStatusCodeResult ChargeBeeWebhook()
{
return new HttpStatusCodeResult(200);
}
Assuming you've already set up your MailChimp Webhooks as described here, you can get the posted data using Request.Form syntax. Using the example posted data from the question, here's how your controller code should look like:
[AllowAnonymous]
public void ChargeBeeWebhook()
{
// type variable will be "unsubscribe"
string type = Request.Form["type"];
// action variable will be "unsub"
string action = Request.Form["data[action]"];
// reason variable will be "manual"
string reason = Request.Form["data[reason]"];
// ...
// ...
// ... do the same with the rest of the posted variables
// ...
// sync the posted data above with your database
// ...
// ...
}
I have log data consisting of the original request body JSON and path it was received on. When a certain service is triggered I in effect want to run this.
Ie for each url/json object in the log I want to resolve the service and have it handle the request. The API is large and growing so I don't want to have to manually wire up each call.
Is there a way to do this internally? Ie without sending new requests out on the loopback?
Thanks,
Hector
It is possible to do using ServiceStack's BasicRequest which can be plugged into the ServiceStack request pipeline using the HostContext.ServiceController.Execute method.
Note this method does not trigger the filters or dependancy injection (You can still resolve from the container in your action method). So this is essentially the same behaviour as MQ requests. So if your requests require to use filters etc, this method of direct service execution may not be suitable for you.
What this does do:
Parses the provided URL to identify the request DTO type
Resolves the Service responsible for handling the DTO
Calls the action method passing in the DTO
Returns the result from the DTO
Use this method to process the request:
static object CallInternalService(string path, string method = "GET", string jsonData = null)
{
// Determine the request dto type based on the rest path
var restPath = HostContext.ServiceController.GetRestPathForRequest(method, path);
if(restPath == null || restPath.RequestType == null)
throw new Exception("No route matched the request"); // Note no fallbacks
// Create an instance of the dto
var dto = Activator.CreateInstance(restPath.RequestType);
if(jsonData != null)
{
var data = ServiceStack.Text.JsonSerializer.DeserializeFromString(jsonData, restPath.RequestType);
dto.PopulateWith(data);
}
// Create a basic request
var request = new BasicRequest(dto, RequestAttributes.None);
// Execute the request
return HostContext.ServiceController.Execute(dto, request);
}
So you simply pass the URL /Someaction/value and the method POST, along with the JSON data payload:
var result = CallInternalService("/users/123", "POST", "{\"Name\":\"Bob\",\"Age\":28}");
I hope that helps.
For one-way messages the Messaging API is your best option. At the moment there isn't any inter process communication option, such as, named pipes. However, I did submit a feature request for named pipes.
from a Wcf data service client (inherited from System.Data.Services.Client.DataServiceContext) I would like to invoke a service operation defined on a Wcf Data Service that returns void
[WebGet]
public void Operation()
{
// logic
}
The only reasonable method I found is the Execute, but how could I use it with a void operation?
Thank you
You can use just plain HttpWebRequest to do this. I think it will need to be POST service operation (as GET would assume some response, but since you declare it as void it would have no response). In which case Execute can't be used anyway (as it always issues a GET request).
Using plain HttpWebRequest just issue a simple POST to the service operation URL and just check the response status code (should be 204 No Content).
Currently WCF Data Services doesn't have native client support for service operations, so you need to write one for yourself.
I have found a workaround for this problem.
This website solved me quite a few problems before, so I thought it would be nice to share back.
The quick answer to your question is:
string empty = context.Execute<string>(new Uri("Operation", UriKind.Relative)).FirstOrDefault();
The "empty" string should be null or empty upon response. It "works around" the HttpWebRequest method mentioned on the post above.
Further more, it is also possible to get primitive types back using this technique.
Lets say I have this Method:
[WebGet]
public bool Authenticate(string Username, string Password)
{...do stuff here...}
When you try the normal execution it fails (Vitek Karas explains it well in his reponse above):
var query = context.CreateQuery<bool>("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
DataServiceCollection<bool> list = new DataServiceCollection<bool>();
list.Load(query);
But the following will do the trick:
var query = context.CreateQuery<bool>("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
bool authenticated = context.Execute<bool>(new Uri(query.RequestUri.ToString().Replace("Authenticate()", "Authenticate"))).FirstOrDefault();
Please note the Replace("Authenticate()", "Authenticate"), which omits () from the query string (otherwise it will cause error).
Hope it helps.
- Itye
Thanks Itye
I was looking for similar solution. Did using HttpWebRequest way first. But your two lines of code helped me doing the same task. Very Happy. Thanks Once Again..
var query = context.CreateQuery("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
bool authenticated = context.Execute(new Uri(query.RequestUri.ToString().Replace("Authenticate()", "Authenticate"))).FirstOrDefault();