Skip to content

Instantly share code, notes, and snippets.

@mdwhatcott
Created January 16, 2023 23:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdwhatcott/eff2c18aa8cef4d3f99492cbe1d0df56 to your computer and use it in GitHub Desktop.
Save mdwhatcott/eff2c18aa8cef4d3f99492cbe1d0df56 to your computer and use it in GitHub Desktop.
A tiny address list processor
package main
import (
"encoding/csv"
"encoding/json"
"io"
"log"
"net/http"
"os"
)
func main() {
inputs := make(chan []string)
go readRecords(inputs)
outputs := make(chan Address)
go validateRecords(inputs, outputs)
writeRecords(outputs)
}
func readRecords(results chan<- []string) {
defer close(results)
inputFile, err := os.Open("input.csv")
if err != nil {
log.Panicln("Failed to open input file:", err)
}
defer func() {
err := inputFile.Close()
if err != nil {
log.Panicln("Failed to close input file:", err)
}
}()
reader := csv.NewReader(inputFile)
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
log.Panicln("Failed to read input record:", err)
}
results <- record
}
}
type Address struct {
DeliveryLine1 string `json:"delivery_line_1"`
DeliveryLine2 string `json:"delivery_line_2"`
LastLine string `json:"last_line"`
}
func validateRecords(inputs <-chan []string, outputs chan<- Address) {
defer close(outputs)
for record := range inputs {
outputs <- validateRecord(record)
}
}
var (
smartyAPI = "https://us-street.api.smartystreets.com/street-address"
smartyAuthID = os.Getenv("SMARTY_AUTH_ID")
smartyAuthToken = os.Getenv("SMARTY_AUTH_TOKEN")
)
func validateRecord(record []string) Address {
request, err := http.NewRequest(http.MethodGet, smartyAPI, nil)
if err != nil {
log.Panicln("Failed to create http request:", err)
}
params := request.URL.Query()
params.Set("auth-id", smartyAuthID)
params.Set("auth-token", smartyAuthToken)
params.Set("street", record[0])
params.Set("city", record[1])
params.Set("state", record[2])
params.Set("zipcode", record[3])
request.URL.RawQuery = params.Encode()
response, err := http.DefaultClient.Do(request)
if err != nil {
log.Panicln("Failed to send http request:", err)
}
if response.StatusCode != http.StatusOK {
log.Panicln("Non-200 http response status:", response.Status)
}
defer func() {
err := response.Body.Close()
if err != nil {
log.Panicln("Failed to close http response body:", err)
}
}()
var parsed []Address
err = json.NewDecoder(response.Body).Decode(&parsed)
if err != nil {
log.Panicln("Failed to decode http response body:", err)
}
return parsed[0]
}
func writeRecords(records <-chan Address) {
outputFile, err := os.Create("output.csv")
if err != nil {
log.Panicln("Failed to create output file:", err)
}
defer func() {
err := outputFile.Close()
if err != nil {
log.Panicln("Failed to close output file:", err)
}
}()
writer := csv.NewWriter(outputFile)
for record := range records {
err = writer.Write([]string{record.DeliveryLine1, record.DeliveryLine2, record.LastLine})
if err != nil {
log.Panicln("Failed to write output record:", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment