Skip to content

Commit 6ed5ee7

Browse files
International Postal Code API Support (#58)
* add international-code-api * A space is a %20 in a URL * remove useless tests * Add make target for the international-postal-code-api example * preserve net7.0 * It's time to upgrade
1 parent c3d20ef commit 6ed5ee7

9 files changed

Lines changed: 444 additions & 0 deletions

File tree

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ international_autocomplete_api:
3636
international_street_api:
3737
dotnet run --project $(EXAMPLES_PROJECT) -- international_street
3838

39+
international_postal_code_api:
40+
dotnet run --project $(EXAMPLES_PROJECT) -- international_postal_code
41+
3942
us_autocomplete_pro_api:
4043
dotnet run --project $(EXAMPLES_PROJECT) -- us_autocomplete_pro
4144

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
namespace Examples
2+
{
3+
using System;
4+
using System.IO;
5+
using SmartyStreets;
6+
using SmartyStreets.InternationalPostalCodeApi;
7+
8+
internal static class InternationalPostalCodeExample
9+
{
10+
public static void Run()
11+
{
12+
13+
// You don't have to store your keys in environment variables, but we recommend it.
14+
var authId = Environment.GetEnvironmentVariable("SMARTY_AUTH_ID");
15+
var authToken = Environment.GetEnvironmentVariable("SMARTY_AUTH_TOKEN");
16+
17+
using var client = new ClientBuilder(authId, authToken).BuildInternationalPostalCodeApiClient();
18+
19+
// Documentation for input fields can be found at:
20+
// https://smartystreets.com/docs/cloud/international-postal-code-api
21+
22+
var lookup = new Lookup
23+
{
24+
InputId = "ID-8675309", // Optional ID from your system
25+
Locality = "Sao Paulo",
26+
AdministrativeArea = "SP",
27+
Country = "Brazil",
28+
PostalCode = "02516"
29+
};
30+
31+
//uncomment the line below to add a custom parameter
32+
//lookup.AddCustomParameter("postal_code", "02516");
33+
34+
try
35+
{
36+
client.Send(lookup);
37+
}
38+
catch (SmartyException ex)
39+
{
40+
Console.WriteLine(ex.Message);
41+
Console.WriteLine(ex.StackTrace);
42+
return;
43+
}
44+
catch (IOException ex)
45+
{
46+
Console.WriteLine(ex.StackTrace);
47+
return;
48+
}
49+
50+
var results = lookup.Result;
51+
52+
if (results == null || results.Count == 0)
53+
{
54+
Console.WriteLine("No results.");
55+
return;
56+
}
57+
58+
Console.WriteLine("Results:");
59+
Console.WriteLine();
60+
61+
for (var c = 0; c < results.Count; c++)
62+
{
63+
var candidate = results[c];
64+
Console.WriteLine("Candidate: " + c);
65+
Display(candidate.InputId);
66+
Display(candidate.CountryIso3);
67+
Display(candidate.Locality);
68+
Display(candidate.DependentLocality);
69+
Display(candidate.DoubleDependentLocality);
70+
Display(candidate.SubAdministrativeArea);
71+
Display(candidate.AdministrativeArea);
72+
Display(candidate.SuperAdministrativeArea);
73+
Display(candidate.PostalCode);
74+
Console.WriteLine();
75+
}
76+
}
77+
78+
private static void Display(string value)
79+
{
80+
if (!string.IsNullOrEmpty(value))
81+
{
82+
Console.WriteLine(" " + value);
83+
}
84+
}
85+
}
86+
}
87+

src/examples/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ private static void Main(string[] args)
3838
case "international_autocomplete":
3939
InternationalAutocompleteExample.Run();
4040
break;
41+
case "international_postal_code":
42+
InternationalPostalCodeExample.Run();
43+
break;
4144
case "us_extract":
4245
USExtractExample.Run();
4346
break;
@@ -68,6 +71,7 @@ private static void RunAllExamples()
6871
USZipCodeMultipleLookupsExample.Run();
6972
InternationalStreetExample.Run();
7073
InternationalAutocompleteExample.Run();
74+
InternationalPostalCodeExample.Run();
7175
USExtractExample.Run();
7276
USAutocompleteProExample.Run();
7377
USReverseGeoExample.Run();

src/sdk/ClientBuilder.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class ClientBuilder
2828
private bool logHttpRequestAndResponse;
2929
private const string InternationalStreetApiUrl = "https://international-street.api.smarty.com/verify";
3030
private const string InternationalAutocompleteApiUrl = "https://international-autocomplete.api.smarty.com/v2/lookup";
31+
private const string InternationalPostalCodeApiUrl = "https://international-postal-code.api.smarty.com/lookup";
3132
private const string UsAutocompleteProApiUrl = "https://us-autocomplete-pro.api.smarty.com/lookup";
3233
private const string UsExtractApiUrl = "https://us-extract.api.smarty.com/";
3334
private const string UsStreetApiUrl = "https://us-street.api.smarty.com/street-address";
@@ -174,6 +175,12 @@ public InternationalAutocompleteApi.Client BuildInternationalAutocompleteApiClie
174175
return new InternationalAutocompleteApi.Client(this.BuildSender(), this.serializer);
175176
}
176177

178+
public InternationalPostalCodeApi.Client BuildInternationalPostalCodeApiClient()
179+
{
180+
this.EnsureURLPrefixNotNull(InternationalPostalCodeApiUrl);
181+
return new InternationalPostalCodeApi.Client(this.BuildSender(), this.serializer);
182+
}
183+
177184
public USAutocompleteProApi.Client BuildUsAutocompleteProApiClient()
178185
{
179186
this.EnsureURLPrefixNotNull(UsAutocompleteProApiUrl);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace SmartyStreets.InternationalPostalCodeApi
2+
{
3+
using System.Runtime.Serialization;
4+
5+
/// <summary>
6+
/// A candidate is a possible match for a postal code lookup.
7+
/// A lookup can have multiple candidates if the postal code was ambiguous.
8+
/// </summary>
9+
/// <remarks>See "https://smartystreets.com/docs/international-postal-code-api"</remarks>
10+
[DataContract]
11+
public class Candidate
12+
{
13+
#region [ Fields ]
14+
15+
[DataMember(Name = "input_id")]
16+
public string InputId { get; set; }
17+
18+
[DataMember(Name = "administrative_area")]
19+
public string AdministrativeArea { get; set; }
20+
21+
[DataMember(Name = "sub_administrative_area")]
22+
public string SubAdministrativeArea { get; set; }
23+
24+
[DataMember(Name = "super_administrative_area")]
25+
public string SuperAdministrativeArea { get; set; }
26+
27+
[DataMember(Name = "country_iso_3")]
28+
public string CountryIso3 { get; set; }
29+
30+
[DataMember(Name = "locality")]
31+
public string Locality { get; set; }
32+
33+
[DataMember(Name = "dependent_locality")]
34+
public string DependentLocality { get; set; }
35+
36+
[DataMember(Name = "dependent_locality_name")]
37+
public string DependentLocalityName { get; set; }
38+
39+
[DataMember(Name = "double_dependent_locality")]
40+
public string DoubleDependentLocality { get; set; }
41+
42+
[DataMember(Name = "postal_code")]
43+
public string PostalCode { get; set; }
44+
45+
[DataMember(Name = "postal_code_extra")]
46+
public string PostalCodeExtra { get; set; }
47+
48+
#endregion
49+
}
50+
}
51+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace SmartyStreets.InternationalPostalCodeApi
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
8+
/// <summary>
9+
/// This client sends lookups to the SmartyStreets International Postal Code API,
10+
/// and attaches the results to the appropriate Lookup objects.
11+
/// </summary>
12+
public class Client : IInternationalPostalCodeClient
13+
{
14+
private bool senderIsDisposed;
15+
private readonly ISender sender;
16+
private readonly ISerializer serializer;
17+
18+
public Client(ISender sender, ISerializer serializer)
19+
{
20+
this.sender = sender;
21+
this.serializer = serializer;
22+
}
23+
24+
public void Send(Lookup lookup)
25+
{
26+
SendAsync(lookup).GetAwaiter().GetResult();
27+
}
28+
29+
public async Task SendAsync(Lookup lookup)
30+
{
31+
if (lookup == null)
32+
throw new ArgumentNullException("lookup");
33+
34+
var request = BuildRequest(lookup);
35+
36+
var response = await this.sender.SendAsync(request);
37+
38+
using (var payloadStream = new MemoryStream(response.Payload))
39+
{
40+
var candidates = this.serializer.Deserialize<List<Candidate>>(payloadStream) ?? new List<Candidate>();
41+
AssignCandidatesToLookups(candidates, lookup);
42+
}
43+
}
44+
45+
private static void AssignCandidatesToLookups(IEnumerable<Candidate> candidates, Lookup lookup)
46+
{
47+
foreach (var candidate in candidates)
48+
lookup.AddToResult(candidate);
49+
}
50+
51+
private static Request BuildRequest(Lookup lookup)
52+
{
53+
var request = new Request();
54+
55+
request.SetParameter("input_id", lookup.InputId);
56+
request.SetParameter("country", lookup.Country);
57+
request.SetParameter("locality", lookup.Locality);
58+
request.SetParameter("administrative_area", lookup.AdministrativeArea);
59+
request.SetParameter("postal_code", lookup.PostalCode);
60+
61+
foreach (KeyValuePair<string, string> line in lookup.CustomParamDict)
62+
{
63+
request.SetParameter(line.Key, line.Value);
64+
}
65+
66+
return request;
67+
}
68+
69+
public void Dispose()
70+
{
71+
if (!this.senderIsDisposed)
72+
{
73+
this.sender.Dispose();
74+
this.senderIsDisposed = true;
75+
}
76+
}
77+
}
78+
}
79+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace SmartyStreets.InternationalPostalCodeApi
2+
{
3+
using SmartyStreets;
4+
5+
// marker interface for easy dependency injection and unit test mocking
6+
public interface IInternationalPostalCodeClient : IClient<Lookup>
7+
{
8+
9+
}
10+
}
11+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
namespace SmartyStreets.InternationalPostalCodeApi
2+
{
3+
using System.Collections.Generic;
4+
5+
/// <summary>
6+
/// In addition to holding all of the input data for this lookup, this class also
7+
/// will contain the result of the lookup after it comes back from the API.
8+
/// </summary>
9+
/// <remarks>
10+
/// Lookups must have certain required fields set with non-blank values.
11+
/// These can be found at "https://smartystreets.com/docs/cloud/international-postal-code-api"
12+
/// </remarks>
13+
public class Lookup : ILookup
14+
{
15+
#region [ Fields ]
16+
17+
public List<Candidate> Result { get; private set; }
18+
19+
public string InputId { get; set; }
20+
public string Country { get; set; }
21+
public string Locality { get; set; }
22+
public string AdministrativeArea { get; set; }
23+
public string PostalCode { get; set; }
24+
public Dictionary<string, string> CustomParamDict = new Dictionary<string, string>{};
25+
26+
#endregion
27+
28+
#region [ Constructors ]
29+
30+
public Lookup()
31+
{
32+
this.Result = new List<Candidate>();
33+
}
34+
35+
#endregion
36+
37+
public void AddToResult(Candidate newCandidate)
38+
{
39+
this.Result.Add(newCandidate);
40+
}
41+
42+
public void AddCustomParameter(string parameter, string value)
43+
{
44+
CustomParamDict.Add(parameter, value);
45+
}
46+
}
47+
}
48+

0 commit comments

Comments
 (0)