Thanks for the prompt reply.
Here is the raw POST:
POST https://businessname-sandbox.pipedrive.com/v1/persons?api_token=the_token HTTP/1.1
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Length: 129
Host: businessname-sandbox.pipedrive.com
name=Test&email={label:test1,value:test1%40test.com,primary:True},{label:test2,value:test2%40test.com,primary:False}&visible_to=0
And the gateway code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Pipedrive.Bo;
using ServiceStack;
using ServiceStack.Text;
namespace Pipedrive
{
public class PipedriveGateway : IRestGateway
{
private const string BaseUrl = “Log in”;
private const string APIVersion = “v1”;
public TimeSpan Timeout { get; set; }
private string apiKey;
private string UserAgent { get; set; }
public PipedriveGateway(string apiKey)
{
this.apiKey = apiKey;
Timeout = TimeSpan.FromSeconds(60);
UserAgent = typeof(PipedriveGateway) + " v1";
JsConfig.InitStatics();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;//todo
}
protected virtual void InitRequest(HttpWebRequest req, string method)
{
req.Accept = MimeTypes.Json;
if (method == HttpMethods.Post || method == HttpMethods.Put)
{
req.ContentType = MimeTypes.FormUrlEncoded;
}
}
protected virtual void HandlePipedriveException(WebException ex)
{
string errorBody = ex.GetResponseBody();
var errorStatus = ex.GetStatus() ?? HttpStatusCode.BadRequest;
if (ex.IsAny400())
{
PipedriveError result = errorBody.FromJson<PipedriveError>();
throw new PipedriveException(result)
{
StatusCode = errorStatus
};
}
}
protected virtual string Send(string relativeUrl, string method, string body)
{
try
{
var url = BaseUrl.CombineWith(relativeUrl);
url = url.AddQueryParam("api_token", apiKey);//ma
var response = url.SendStringToUrl(method: method, requestBody: body, requestFilter: req => { InitRequest(req, method); });
return response;
}
catch (WebException ex)
{
HandlePipedriveException(ex);
throw;
}
}
protected virtual async Task<string> SendAsync(string relativeUrl, string method, string body)
{
try
{
var url = BaseUrl.CombineWith(relativeUrl);
url = url.AddQueryParam("api_token", apiKey);//ma
var response = await url.SendStringToUrlAsync(method: method, requestBody: body, requestFilter: req => { InitRequest(req, method); });
return response;
}
catch (Exception ex)
{
if (ex.UnwrapIfSingleException() is WebException webEx)
HandlePipedriveException(webEx);
throw;
}
}
public class ConfigScope : IDisposable
{
private readonly WriteComplexTypeDelegate holdQsStrategy;
private readonly JsConfigScope jsConfigScope;
public ConfigScope()
{
jsConfigScope = JsConfig.With(new Config
{
DateHandler = DateHandler.UnixTime,
PropertyConvention = PropertyConvention.Lenient,
EmitLowercaseUnderscoreNames = true,
EmitCamelCaseNames = false,
TreatEnumAsInteger = true
});
holdQsStrategy = QueryStringSerializer.ComplexTypeStrategy;
QueryStringSerializer.ComplexTypeStrategy = QueryStringStrategy.FormUrlEncoded;
}
public void Dispose()
{
QueryStringSerializer.ComplexTypeStrategy = holdQsStrategy;
jsConfigScope.Dispose();
}
}
public T Send<T>(IReturn<T> request, string method, bool sendRequestBody = true)
{
using (new ConfigScope())
{
var relativeUrl = request.ToUrl(method);
var body = sendRequestBody ? QueryStringSerializer.SerializeToString(request) : null; //original
var json = Send(relativeUrl, method, body);
var response = json.FromJson<T>();
return response;
}
}
public async Task<T> SendAsync<T>(IReturn<T> request, string method, bool sendRequestBody = true)
{
string relativeUrl;
string body;
using (new ConfigScope())
{
relativeUrl = request.ToUrl(method);
body = sendRequestBody ? QueryStringSerializer.SerializeToString(request) : null;
}
var json = await SendAsync(relativeUrl, method, body);
using (new ConfigScope())
{
var response = json.FromJson<T>();
return response;
}
}
private static string GetMethod<T>(IReturn<T> request)
{
var method = request is IPost ? HttpMethods.Post
: request is IPut ? HttpMethods.Put
: request is IDelete ? HttpMethods.Delete
: HttpMethods.Get;
return method;
}
public T Send<T>(IReturn<T> request)
{
var method = GetMethod(request);
return Send(request, method, sendRequestBody: method == HttpMethods.Post || method == HttpMethods.Put);
}
public Task<T> SendAsync<T>(IReturn<T> request)
{
var method = GetMethod(request);
return SendAsync(request, method, sendRequestBody: method == HttpMethods.Post || method == HttpMethods.Put);
}
public T Get<T>(IReturn<T> request)
{
return Send(request, HttpMethods.Get, sendRequestBody: false);
}
public Task<T> GetAsync<T>(IReturn<T> request)
{
return SendAsync(request, HttpMethods.Get, sendRequestBody: false);
}
public T Post<T> (IReturn<T> request)
{
return Send(request, HttpMethods.Post);
}
public Task<T> PostAsync<T>(IReturn<T> request)
{
return SendAsync(request, HttpMethods.Post);
}
public T Put<T>(IReturn<T> request)
{
return Send(request, HttpMethods.Put);
}
public Task<T> PutAsync<T>(IReturn<T> request)
{
return SendAsync(request, HttpMethods.Put);
}
public T Delete<T>(IReturn<T> request)
{
return Send(request, HttpMethods.Delete, sendRequestBody: false);
}
public Task<T> DeleteAsync<T>(IReturn<T> request)
{
return SendAsync(request, HttpMethods.Delete, sendRequestBody: false);
}
}
}