Full API implementation

This commit is contained in:
2025-09-09 07:03:32 -04:00
parent 5a63c5939a
commit 94f5802498
3 changed files with 54 additions and 57 deletions

View File

@@ -3,43 +3,53 @@ package controllers
import ( import (
"addrss/pkg/postal" "addrss/pkg/postal"
"addrss/pkg/router" "addrss/pkg/router"
"fmt"
) )
type Api struct{} type Api struct{}
type ParseRequest struct {
Address string `json:"address"`
}
func (a Api) AddRoutes() { func (a Api) AddRoutes() {
router.AddPost("/v1/expand", expandAddress).Anonymous() router.AddGet("/suggestions", suggestions).Anonymous()
router.AddPost("/v1/parse", parseAddress).Anonymous() router.AddGet("/parse", parse).Anonymous()
} }
func expandAddress(ctx *router.Context) { func suggestions(ctx *router.Context) {
expansions := postal.ExpandAddress("1080 Brayden Ct. Hebron KY 41048") address, err := ctx.Request.Query("address")
for i := 0; i < len(expansions); i++ { if err != nil {
fmt.Println(expansions[i])
}
ctx.Response.NoContent()
}
func parseAddress(ctx *router.Context) {
pr := ParseRequest{}
if err := ctx.Request.Bind(&pr); err != nil {
ctx.Response.BadRequest(err) ctx.Response.BadRequest(err)
} }
options := postal.ParserOptions{} var parsedSlice []map[string]any
pa := postal.ParseAddressOptions(pr.Address, options) expansions := postal.ExpandAddress(address.(string))
for i := 0; i < len(expansions); i++ {
parsed := parseAddress(expansions[i])
parsedSlice = append(parsedSlice, parsed)
}
ctx.Response.OK(parsedSlice)
}
func parse(ctx *router.Context) {
address, err := ctx.Request.Query("address")
if err != nil {
ctx.Response.BadRequest(err)
}
parsed := parseAddress(address.(string))
ctx.Response.OK(parsed)
}
func parseAddress(address string) map[string]any {
pa := postal.ParseAddress(address)
addr := map[string]any{} addr := map[string]any{}
for i := 0; i < len(pa); i++ { for i := 0; i < len(pa); i++ {
// This is hacky, but renaming in libpostal involves retraining the model.
if pa[i].Label == "postcode" {
pa[i].Label = "zip_code"
}
addr[pa[i].Label] = pa[i].Value addr[pa[i].Label] = pa[i].Value
} }
ctx.Response.OK(addr) return addr
} }

View File

@@ -1,21 +0,0 @@
package controllers
import (
"addrss/pkg/router"
)
type Guestbook struct{}
type guestbookError struct {
Fields []string `json:"fields"`
}
func (g Guestbook) AddRoutes() {
router.AddPost("/guestbook", signGuestbook).Anonymous()
}
func signGuestbook(ctx *router.Context) {
//gb := ctx.Request.Model.(repo.Guestbook)
ctx.Response.NoContent()
}

View File

@@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"strconv" "net/url"
"strings" "strings"
) )
@@ -16,36 +16,44 @@ type Request struct {
} }
func (req Request) Param(key string) (any, error) { func (req Request) Param(key string) (any, error) {
value, ok := req.params[key] if value, ok := req.params[key]; ok {
if ok {
return value, nil return value, nil
} }
return nil, fmt.Errorf("param %s not found", key) return nil, fmt.Errorf("param %s not found", key)
} }
func (req Request) ParamInt64(key string) (int64, error) { func (req Request) Query(key string) (any, error) {
param, err := req.Param(key) values, err := url.ParseQuery(req.Request.URL.RawQuery)
if err != nil { if err != nil {
return 0, err return nil, err
} }
i, err := strconv.Atoi(param.(string)) value, ok := values[key]
if err != nil { if !ok {
return 0, err return nil, fmt.Errorf("query string parameter %s not found", key)
} }
return int64(i), nil if len(value) > 1 {
return value, nil
}
return value[0], nil
} }
// Bind Binds the request body to the struct pointer v using the Content-Type header to select a binder. This function panics if v cannot be bound. // Bind Binds the request body to the struct pointer v using the Content-Type header to select a binder.
func (req Request) Bind(v any) error { func (req Request) Bind(v any) error {
body, err := req.Request.GetBody()
if err != nil {
return err
}
mime := req.Header.Get("Content-Type") mime := req.Header.Get("Content-Type")
mime = strings.Split(mime, ";")[0] mime = strings.Split(mime, ";")[0]
switch mime { switch mime {
case "application/json": case "application/json":
err := json.NewDecoder(req.Body).Decode(&v) err := json.NewDecoder(body).Decode(&v)
if err != nil { if err != nil {
return fmt.Errorf("error while decoding http request body as json: %w", err) return fmt.Errorf("error while decoding http request body as json: %w", err)
} }
@@ -57,13 +65,13 @@ func (req Request) Bind(v any) error {
return nil return nil
} }
func (req Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) { func (req Request) File(key string) (multipart.File, *multipart.FileHeader, error) {
maxMegs, err := config.GetInt64("uploads.maxMegabytes") maxMegs, err := config.GetInt64("uploads.maxMegabytes")
if err != nil { if err != nil {
maxMegs = 8000000 maxMegs = 8000000
} }
if err := req.ParseMultipartForm(maxMegs << 20); err != nil { if err = req.ParseMultipartForm(maxMegs << 20); err != nil {
return nil, nil, err return nil, nil, err
} }