I’ve built a few Gateways for third parties (Pipedrive and others) using ServiceStack.
I usually roughly keep a simplified version of the structure of the StripeGateway. Currently, the API I’m building a Gateway for accepts only POST, so it’s relatively simple and This is the base structure I’m using:
public interface IRestGateway
{
HttpClient Client { get; set; }
T Post<T>(IReturn<T> request);
Task<T> PostAsync<T>(IReturn<T> request);
}
public class RestGateway : IRestGateway
{
.....
}
With dtos like these:
[Route($"/main/{ApiMethods.GetCustomerProperties}")]
public class GetCustomerProperties : IPost, IReturn<GetCustomerPropertiesResult>
{
public string Customer { get; set; }
public string[] PropertyNames { get; set; }
}
public class GetCustomerPropertiesResult
{
public string[]? Result { get; set; }
public Error? Error { get; set; }
}
public class Error
{
public string? Message { get; set; }
}
Years ago (2018!) @mythz said I should probably not base myself off the StripeGateway project since the Stripe API is peculiar etc. It’s a bit late to ask for clarifications but is there anything inherently wrong with this approach?
The issue I’m having is that sometimes the API will return things that do not translate well to a typed result.
For example, the API I’m building a gateway for right now is not uniform and will sometime return a simple string array when there are no error whereas 90% of the endpoints stick to the convention of returning a Result Properties (for whatever they are returning) and an Error with a message like in the class above if something goes wrong.
I’m trying to normalize what the API return. That GetCustomerProperties dto is a good example. The API returns an array of string when there’s no error and doesn’t stick to the convention and right now I’m resorting to this hack to map to my return type (is there better way to handle this maybe on the DTO itself somehow?):
public async Task<T> PostAsync<T>(IReturn<T> request)
{
var httpReq = CommonPost(request);
HttpResponseMessage httpRes = await Client.SendAsync(httpReq);
string? responseBody = await (await httpRes.Content.ReadAsStreamAsync()).ReadToEndAsync(Encoding.UTF8);
if (httpRes.IsSuccessStatusCode)
{
if (typeof(T) == typeof(GetCustomerPropertiesResult))
{
if (IsJsonArray(responseBody))
{
// Deserialize as string[] and convert to GetAllCustomerPropertiesResult
var strings = responseBody.FromJson<string[]>();
var result = new GetCustomerPropertiesResult
{
Result = strings
};
return (T)(object)result; // cast back to T
}
else
{
// Deserialize as string[] and convert to GetAllCustomerPropertiesResult
var error = responseBody.FromJson<Error>();
var result = new GetCustomerPropertiesResult
{
Error = error
};
return (T)(object)result; // cast back to T
}
}
return responseBody.FromJson<T>();
}
ThrowDefaultRestApiException(httpRes, responseBody);
throw httpRes.EnsureSuccessStatusCodeCreateException();
}
The other issue I have is that if you send [“name”,“phone”] for PropertyNames the API returns [“mythz”,“yourphonenumber”] there are lot’s of properties and I’d like to be able to offer a typed experience how would you go about implementing this?
I’m not sure if you had these things in mind when you said it was probably a bad idea to base myself off the stripe gateway and I’ve been going down the wrong road all this time or you simply meant their api had a lot of specific quirks.
I’m looking to improve my skills and any advice will be much appreciated.
Cheers,