Skip to content

Commit

Permalink
Implement V1 enrichment sdk (#31)
Browse files Browse the repository at this point in the history
* Implement V1 enrichment sdk

implemented full enrichment sdk functionality.
  • Loading branch information
LandonSmarty authored Oct 26, 2023
1 parent 00e8028 commit f9f77c7
Show file tree
Hide file tree
Showing 6 changed files with 631 additions and 90 deletions.
30 changes: 13 additions & 17 deletions examples/us-enrichment-api/main.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
package main

import (
"context"
"fmt"
"github.com/smartystreets/smartystreets-go-sdk/wireup"
"log"
"os"

us_enrichment "github.com/smartystreets/smartystreets-go-sdk/us-enrichment-api"
"github.com/smartystreets/smartystreets-go-sdk/wireup"
)

func main() {
log.SetFlags(log.Ltime | log.Llongfile)

client := wireup.BuildUSEnrichmentAPIClient(
wireup.WebsiteKeyCredential(os.Getenv("SMARTY_AUTH_WEB"), os.Getenv("SMARTY_AUTH_REFERER")),
//wireup.SecretKeyCredential(os.Getenv("SMARTY_AUTH_ID"), os.Getenv("SMARTY_AUTH_TOKEN")),
//wireup.WebsiteKeyCredential(os.Getenv("SMARTY_AUTH_WEB"), os.Getenv("SMARTY_AUTH_REFERER")),
wireup.SecretKeyCredential(os.Getenv("SMARTY_AUTH_ID"), os.Getenv("SMARTY_AUTH_TOKEN")),
// The appropriate license values to be used for your subscriptions
// can be found on the Subscriptions page the account dashboard.
// https://www.smartystreets.com/docs/cloud/licensing
wireup.WithLicenses("us-enrichment-cloud"),
// https://www.smarty.com/docs/cloud/licensing
wireup.WithLicenses("us-property-data-principal-cloud"),
// wireup.DebugHTTPOutput(), // uncomment this line to see detailed HTTP request/response information.
)

// Documentation for input fields can be found at:
// https://smartystreets.com/docs/cloud/us-reverse-geo-api#http-request-input-fields

lookup := &us_enrichment.Lookup{
SmartyKey: "12345",
DataSet: "property-attributes",
}
smartyKey := "1682393594"

err, results := client.SendPropertyPrincipalLookup(smartyKey)

if err := client.SendLookupWithContext(context.Background(), lookup); err != nil {
log.Fatal("Error sending batch:", err)
if err != nil {
log.Fatal("Error sending lookup:", err)
}

fmt.Printf("Results for input: (%s, %s)\n", lookup.SmartyKey, lookup.DataSet)
for s, address := range lookup.Response {
fmt.Printf("#%d: %#v\n", s, address)
fmt.Printf("Results for input: (%s, %s)\n", smartyKey, "principal")
for s, response := range results {
fmt.Printf("#%d: %+v\n", s, response)
}

log.Println("OK")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/smartystreets/smartystreets-go-sdk

go 1.13
go 1.18

require (
github.com/smarty/assertions v1.15.1
Expand Down
53 changes: 32 additions & 21 deletions us-enrichment-api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package us_enrichment

import (
"context"
"encoding/json"
"net/http"
"strings"

Expand All @@ -17,15 +16,33 @@ func NewClient(sender sdk.RequestSender) *Client {
return &Client{sender: sender}
}

func (c *Client) SendLookup(lookup *Lookup) error {
return c.SendLookupWithContext(context.Background(), lookup)
func (c *Client) SendPropertyFinancialLookup(smartyKey string) (error, []*FinancialResponse) {
l := &financialLookup{
SmartyKey: smartyKey,
}
err := c.sendLookup(l)

return err, l.Response
}

func (c *Client) SendLookupWithContext(ctx context.Context, lookup *Lookup) error {
func (c *Client) SendPropertyPrincipalLookup(smartyKey string) (error, []*PrincipalResponse) {
l := &principalLookup{
SmartyKey: smartyKey,
}
err := c.sendLookup(l)

return err, l.Response
}

func (c *Client) sendLookup(lookup enrichmentLookup) error {
return c.sendLookupWithContext(context.Background(), lookup)
}

func (c *Client) sendLookupWithContext(ctx context.Context, lookup enrichmentLookup) error {
if lookup == nil {
return nil
}
if len(lookup.SmartyKey) == 0 && len(lookup.DataSet) == 0 {
if len(lookup.GetSmartyKey()) == 0 {
return nil
}

Expand All @@ -37,31 +54,25 @@ func (c *Client) SendLookupWithContext(ctx context.Context, lookup *Lookup) erro
return err
}

return deserializeResponse(response, lookup)
}

func deserializeResponse(body []byte, lookup *Lookup) error {
err := json.Unmarshal(body, &lookup.Response)
if err != nil {
return err
}
return nil
return lookup.UnmarshalResponse(response)
}

func buildRequest(lookup *Lookup) *http.Request {
func buildRequest(lookup enrichmentLookup) *http.Request {
request, _ := http.NewRequest("GET", buildLookupURL(lookup), nil) // We control the method and the URL. This is safe.
query := request.URL.Query()
request.URL.RawQuery = query.Encode()
return request
}

func buildLookupURL(lookup *Lookup) string {
newLookupURL := strings.Replace(lookupURL, lookupURLSmartyKey, lookup.SmartyKey, 1)
return strings.Replace(newLookupURL, lookupURLDataSet, lookup.DataSet, 1)
func buildLookupURL(lookup enrichmentLookup) string {
newLookupURL := strings.Replace(lookupURL, lookupURLSmartyKey, lookup.GetSmartyKey(), 1)
newLookupURL = strings.Replace(newLookupURL, lookupURLDataSet, lookup.GetDataSet(), 1)
return strings.Replace(newLookupURL, lookupURLDataSubSet, lookup.GetDataSubset(), 1)
}

const (
lookupURLSmartyKey = ":smartykey"
lookupURLDataSet = ":dataset"
lookupURL = "/lookup/" + lookupURLSmartyKey + "/" + lookupURLDataSet // Remaining parts will be completed later by the sdk.BaseURLClient.
lookupURLSmartyKey = ":smartykey"
lookupURLDataSet = ":dataset"
lookupURLDataSubSet = ":datasubset"
lookupURL = "/lookup/" + lookupURLSmartyKey + "/" + lookupURLDataSet + "/" + lookupURLDataSubSet // Remaining parts will be completed later by the sdk.BaseURLClient.
)
71 changes: 32 additions & 39 deletions us-enrichment-api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,89 +20,82 @@ type ClientFixture struct {
sender *FakeSender
client *Client

input *Lookup
input enrichmentLookup
}

func (f *ClientFixture) Setup() {
f.sender = &FakeSender{}
f.client = NewClient(f.sender)
f.input = new(Lookup)
f.input = new(principalLookup)
}

func (f *ClientFixture) TestLookupSerializedAndSentWithContext__ResponseSuggestionsIncorporatedIntoLookup() {
f.sender.response = validResponseJSON
f.input.SmartyKey = "12345"
f.input.DataSet = "property"
smartyKey := "123"
f.sender.response = validFinancialResponse
f.input = &financialLookup{
SmartyKey: smartyKey,
}

ctx := context.WithValue(context.Background(), "key", "value")
err := f.client.SendLookupWithContext(ctx, f.input)
err := f.client.sendLookupWithContext(ctx, f.input)

f.So(err, should.BeNil)
f.So(f.sender.request, should.NotBeNil)
f.So(f.sender.request.Method, should.Equal, "GET")
f.So(f.sender.request.URL.Path, should.Equal, "/"+f.input.SmartyKey+"/"+f.input.DataSet)
f.So(f.sender.request.URL.Path, should.Equal, "/lookup/"+smartyKey+"/"+f.input.GetDataSet()+"/"+f.input.GetDataSubset())
f.So(f.sender.request.Context(), should.Resemble, ctx)

f.So(f.input.Response, should.Resemble, []*Response{
response := f.input.(*financialLookup).Response

f.So(response, should.Resemble, []*FinancialResponse{
{
SmartyKey: "12345",
DataSetName: "property",
Attributes: []*Attribute{
{Key: "PA1", Value: "67890"},
{Key: "PA2", Value: "unknown"},
}},
SmartyKey: "123",
DataSetName: "property",
DataSubsetName: "financial",
Attributes: FinancialAttributes{
AssessedImprovementPercent: "Assessed_Improvement_Percent",
VeteranTaxExemption: "Veteran_Tax_Exemption",
WidowTaxExemption: "Widow_Tax_Exemption",
},
},
})
}

func (f *ClientFixture) TestNilLookupNOP() {
err := f.client.SendLookup(nil)
err := f.client.sendLookup(nil)
f.So(err, should.BeNil)
f.So(f.sender.request, should.BeNil)
}

func (f *ClientFixture) TestEmptyLookup_NOP() {
err := f.client.SendLookup(new(Lookup))
err := f.client.sendLookup(new(principalLookup))
f.So(err, should.BeNil)
f.So(f.sender.request, should.BeNil)
}

func (f *ClientFixture) TestSenderErrorPreventsDeserialization() {
f.sender.err = errors.New("gophers")
f.sender.response = validResponseJSON // would be deserialized if not for the err (above)
f.input.SmartyKey = "12345"
f.input.DataSet = "property"
f.sender.response = validPrincipalResponse // would be deserialized if not for the err (above)
f.input = &principalLookup{SmartyKey: "12345"}

err := f.client.SendLookup(f.input)
err := f.client.sendLookup(f.input)

f.So(err, should.NotBeNil)
f.So(f.input.Response, should.BeEmpty)
f.So(f.input.(*principalLookup).Response, should.BeEmpty)
}

func (f *ClientFixture) TestDeserializationErrorPreventsDeserialization() {
f.sender.response = `I can't haz JSON`
f.input.SmartyKey = "12345"
f.input.DataSet = "property"
f.input = &principalLookup{SmartyKey: "12345"}

err := f.client.SendLookup(f.input)
err := f.client.sendLookup(f.input)

f.So(err, should.NotBeNil)
f.So(f.input.Response, should.BeEmpty)
f.So(f.input.(*principalLookup).Response, should.BeEmpty)
}

var validResponseJSON = `[
{
"smarty-key": "12345",
"data-set-name": "property",
"attributes": [{
"key": "PA1",
"value": "67890"
},
{
"key": "PA2",
"value": "unknown"
}
]
}]`
var validFinancialResponse = `[{"smarty_key":"123","data_set_name":"property","data_subset_name":"financial","attributes":{"assessed_improvement_percent":"Assessed_Improvement_Percent","veteran_tax_exemption":"Veteran_Tax_Exemption","widow_tax_exemption":"Widow_Tax_Exemption"}}]`
var validPrincipalResponse = `[{"smarty_key":"123","data_set_name":"property","data_subset_name":"principal","attributes":{"1st_floor_sqft":"1st_Floor_Sqft",lender_name_2":"Lender_Name_2","lender_seller_carry_back":"Lender_Seller_Carry_Back","year_built":"Year_Built","zoning":"Zoning"}}]`

/**************************************************************************/

Expand Down
60 changes: 56 additions & 4 deletions us-enrichment-api/lookup.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,61 @@
package us_enrichment

// Lookup fields defined here: https://smarty.com/docs/cloud/us-enrichment-api#http-request-input-fields
type Lookup struct {
import (
"encoding/json"
)

type enrichmentLookup interface {
GetSmartyKey() string
GetDataSet() string
GetDataSubset() string

UnmarshalResponse([]byte) error
}

type financialLookup struct {
SmartyKey string
DataSet string
Response []*FinancialResponse
}

func (f *financialLookup) GetSmartyKey() string {
return f.SmartyKey
}

func (f *financialLookup) GetDataSet() string {
return propertyDataSet
}

Response []*Response
func (f *financialLookup) GetDataSubset() string {
return financialDataSubset
}

func (f *financialLookup) UnmarshalResponse(bytes []byte) error {
return json.Unmarshal(bytes, &f.Response)
}

type principalLookup struct {
SmartyKey string
Response []*PrincipalResponse
}

func (p *principalLookup) GetSmartyKey() string {
return p.SmartyKey
}

func (p *principalLookup) GetDataSet() string {
return propertyDataSet
}

func (p *principalLookup) GetDataSubset() string {
return principalDataSubset
}

func (p *principalLookup) UnmarshalResponse(bytes []byte) error {
return json.Unmarshal(bytes, &p.Response)
}

const (
financialDataSubset = "financial"
principalDataSubset = "principal"
propertyDataSet = "property"
)
Loading

0 comments on commit f9f77c7

Please sign in to comment.