Skip to content

Commit

Permalink
Universal Enrichment Lookups (#35)
Browse files Browse the repository at this point in the history
* Added a universal send lookup method to the us-enrichment go sdk
  • Loading branch information
JacobMcKenzieSmarty authored Feb 26, 2024
1 parent ee6c998 commit dce0d9c
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 6 deletions.
2 changes: 2 additions & 0 deletions examples/us-enrichment-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,7 @@ func main() {
fmt.Printf("#%d: %+v\n", s, response)
}

//TODO: Add a test for the "genericLookup" feature

log.Println("OK")
}
53 changes: 53 additions & 0 deletions examples/us-enrichment-api/universal/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"fmt"
"log"
"net/http"
"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")),
// The appropriate license values to be used for your subscriptions
// can be found on the Subscriptions page the account dashboard.
// 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://www.smarty.com/docs/cloud/us-address-enrichment-api#http-request-input-fields

smartyKey := "1682393594"

lookup := us_enrichment.Lookup{
SmartyKey: smartyKey,
Include: "group_structural,sale_date", // optional: only include these attributes in the returned data
Exclude: "", // optional: exclude attributes from the returned data
ETag: "", // optional: check if the record has been updated
}

err, results := client.SendUniversalLookup(&lookup, "property", "principal") //for datasets with no subsets, enter the empty string, "", for the dataSubset field

if err != nil {
// If ETag was supplied in the lookup, this status will be returned if the ETag value for the record is current
if client.IsHTTPErrorCode(err, http.StatusNotModified) {
log.Printf("Record has not been modified since the last request")
return
}
log.Fatal("Error sending lookup:", err)
}

fmt.Printf("Results for input: (%s, %s)\n", smartyKey, "principal")
fmt.Println(string(results))

log.Println("OK")
}
30 changes: 24 additions & 6 deletions us-enrichment-api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ func (c *Client) SendPropertyPrincipal(lookup *Lookup) (error, []*PrincipalRespo
return err, propertyLookup.Response
}

func (c *Client) SendUniversalLookup(lookup *Lookup, dataSet, dataSubset string) (error, []byte) {
g := &universalLookup{
Lookup: lookup,
DataSet: dataSet,
DataSubset: dataSubset,
}

err := c.sendLookup(g)
return err, g.Response
}

func (c *Client) sendLookup(lookup enrichmentLookup) error {
return c.sendLookupWithContext(context.Background(), lookup)
}
Expand Down Expand Up @@ -81,15 +92,22 @@ func buildRequest(lookup enrichmentLookup) *http.Request {
}

func buildLookupURL(lookup enrichmentLookup) string {
newLookupURL := strings.Replace(lookupURL, lookupURLSmartyKey, lookup.getSmartyKey(), 1)
var newLookupURL string
if len(lookup.getDataSubset()) == 0 {
newLookupURL = strings.Replace(lookupURLWithoutSubSet, lookupURLSmartyKey, lookup.getSmartyKey(), 1)
} else {
newLookupURL = strings.Replace(lookupURLWithSubSet, 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"
lookupURLDataSubSet = ":datasubset"
lookupURL = "/lookup/" + lookupURLSmartyKey + "/" + lookupURLDataSet + "/" + lookupURLDataSubSet // Remaining parts will be completed later by the sdk.BaseURLClient.
lookupETagHeader = "Etag"
lookupURLSmartyKey = ":smartykey"
lookupURLDataSet = ":dataset"
lookupURLDataSubSet = ":datasubset"
lookupURLWithSubSet = "/lookup/" + lookupURLSmartyKey + "/" + lookupURLDataSet + "/" + lookupURLDataSubSet // Remaining parts will be completed later by the sdk.BaseURLClient.
lookupURLWithoutSubSet = "/lookup/" + lookupURLSmartyKey + "/" + lookupURLDataSet // Remaining parts will be completed later by the sdk.BaseURLClient.
lookupETagHeader = "Etag"
)
23 changes: 23 additions & 0 deletions us-enrichment-api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,29 @@ func (f *ClientFixture) TestDeserializationErrorPreventsDeserialization() {
f.So(f.input.(*principalLookup).Response, should.BeEmpty)
}

func (f *ClientFixture) TestGenericLookupUnmarshallingWithEtag() {
lookup := genericLookup{

Check failure on line 97 in us-enrichment-api/client_test.go

View workflow job for this annotation

GitHub Actions / build

undefined: genericLookup
Response: []byte(validFinancialResponse),
}
httpHeaders := http.Header{"Etag": []string{"ABCDEFG"}}

_ = lookup.unmarshalResponse(lookup.Response, httpHeaders)

f.So(lookup.Response, should.Equal, []byte(`[{"eTag": "ABCDEFG","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"}}]`))
}

func (f *ClientFixture) TestGenericLookupUnmarshallingWithNoEtag() {
lookup := genericLookup{

Check failure on line 108 in us-enrichment-api/client_test.go

View workflow job for this annotation

GitHub Actions / build

undefined: genericLookup
Response: []byte(validPrincipalResponse),
}

httpHeaders := http.Header{"NotAnEtag": []string{"ABC"}}

_ = lookup.unmarshalResponse(lookup.Response, httpHeaders)

f.So(lookup.Response, should.Equal, []byte(validPrincipalResponse))
}

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
40 changes: 40 additions & 0 deletions us-enrichment-api/lookup.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package us_enrichment

import (
bytesPackage "bytes"
"encoding/json"
"net/http"
"net/url"
Expand All @@ -23,6 +24,45 @@ type enrichmentLookup interface {
populate(query url.Values)
}

type universalLookup struct {
Lookup *Lookup
DataSet string
DataSubset string
Response []byte
}

func (g *universalLookup) getSmartyKey() string { return g.Lookup.SmartyKey }
func (g *universalLookup) getDataSet() string { return g.DataSet }
func (g *universalLookup) getDataSubset() string { return g.DataSubset }
func (g *universalLookup) getLookup() *Lookup { return g.Lookup }
func (g *universalLookup) getResponse() interface{} { return g.Response }
func (g *universalLookup) unmarshalResponse(bytes []byte, headers http.Header) error {
g.Response = bytes
if headers != nil {
if etag, found := headers[lookupETagHeader]; found && len(etag) > 0 {

eTagAttribute := []byte(`"eTag": "` + etag[0] + `",`)
insertLocation := bytesPackage.IndexByte(bytes, '{') + 1

if insertLocation > 0 && insertLocation < len(bytes) {
var modifiedResponse bytesPackage.Buffer
modifiedResponse.Write(bytes[:insertLocation])
modifiedResponse.Write(eTagAttribute)
modifiedResponse.Write(bytes[insertLocation:])
g.Response = modifiedResponse.Bytes()
}

}
}

return nil

}
func (g *universalLookup) populate(query url.Values) {
g.Lookup.populateInclude(query)
g.Lookup.populateExclude(query)
}

////////////////////////////////////////////////////////////////////////////////////////

type financialLookup struct {
Expand Down

0 comments on commit dce0d9c

Please sign in to comment.