1
0
mirror of https://github.com/aclindsa/moneygo.git synced 2024-10-31 16:00:05 -04:00

API: Move prices under securities

For example, instead of GETting /prices/5 to query a price with ID 5,
you now must GET /securities/2/prices/5 (assuming price 5's SecurityId
is 2)
This commit is contained in:
Aaron Lindsay 2017-11-16 19:17:51 -05:00
parent 81610adb72
commit c48c50d2c5
5 changed files with 95 additions and 50 deletions

View File

@ -94,8 +94,6 @@ func (ah *APIHandler) route(r *http.Request) ResponseWriterWriter {
return ah.txWrapper(SecurityHandler, r, context) return ah.txWrapper(SecurityHandler, r, context)
case "securitytemplates": case "securitytemplates":
return SecurityTemplateHandler(r, context) return SecurityTemplateHandler(r, context)
case "prices":
return ah.txWrapper(PriceHandler, r, context)
case "accounts": case "accounts":
return ah.txWrapper(AccountHandler, r, context) return ah.txWrapper(AccountHandler, r, context)
case "transactions": case "transactions":

View File

@ -69,19 +69,19 @@ func CreatePriceIfNotExist(tx *Tx, price *Price) error {
return nil return nil
} }
func GetPrice(tx *Tx, priceid, userid int64) (*Price, error) { func GetPrice(tx *Tx, priceid, securityid int64) (*Price, error) {
var p Price var p Price
err := tx.SelectOne(&p, "SELECT * from prices where PriceId=? AND SecurityId IN (SELECT SecurityId FROM securities WHERE UserId=?)", priceid, userid) err := tx.SelectOne(&p, "SELECT * from prices where PriceId=? AND SecurityId=?", priceid, securityid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &p, nil return &p, nil
} }
func GetPrices(tx *Tx, userid int64) (*[]*Price, error) { func GetPrices(tx *Tx, securityid int64) (*[]*Price, error) {
var prices []*Price var prices []*Price
_, err := tx.Select(&prices, "SELECT * from prices where SecurityId IN (SELECT SecurityId FROM securities WHERE UserId=?)", userid) _, err := tx.Select(&prices, "SELECT * from prices where SecurityId=?", securityid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -129,10 +129,10 @@ func GetClosestPrice(tx *Tx, security, currency *Security, date *time.Time) (*Pr
} }
} }
func PriceHandler(r *http.Request, context *Context) ResponseWriterWriter { func PriceHandler(r *http.Request, context *Context, user *User, securityid int64) ResponseWriterWriter {
user, err := GetUserFromSession(context.Tx, r) security, err := GetSecurity(context.Tx, securityid, user.UserId)
if err != nil { if err != nil {
return NewError(1 /*Not Signed In*/) return NewError(3 /*Invalid Request*/)
} }
if r.Method == "POST" { if r.Method == "POST" {
@ -142,8 +142,7 @@ func PriceHandler(r *http.Request, context *Context) ResponseWriterWriter {
} }
price.PriceId = -1 price.PriceId = -1
_, err = GetSecurity(context.Tx, price.SecurityId, user.UserId) if price.SecurityId != security.SecurityId {
if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }
_, err = GetSecurity(context.Tx, price.CurrencyId, user.UserId) _, err = GetSecurity(context.Tx, price.CurrencyId, user.UserId)
@ -160,10 +159,10 @@ func PriceHandler(r *http.Request, context *Context) ResponseWriterWriter {
return ResponseWrapper{201, &price} return ResponseWrapper{201, &price}
} else if r.Method == "GET" { } else if r.Method == "GET" {
if context.LastLevel() { if context.LastLevel() {
//Return all prices //Return all this security's prices
var pl PriceList var pl PriceList
prices, err := GetPrices(context.Tx, user.UserId) prices, err := GetPrices(context.Tx, security.SecurityId)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return NewError(999 /*Internal Error*/) return NewError(999 /*Internal Error*/)
@ -178,7 +177,7 @@ func PriceHandler(r *http.Request, context *Context) ResponseWriterWriter {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }
price, err := GetPrice(context.Tx, priceid, user.UserId) price, err := GetPrice(context.Tx, priceid, security.SecurityId)
if err != nil { if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }
@ -212,7 +211,7 @@ func PriceHandler(r *http.Request, context *Context) ResponseWriterWriter {
return &price return &price
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
price, err := GetPrice(context.Tx, priceid, user.UserId) price, err := GetPrice(context.Tx, priceid, security.SecurityId)
if err != nil { if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }

View File

@ -10,22 +10,22 @@ import (
func createPrice(client *http.Client, price *handlers.Price) (*handlers.Price, error) { func createPrice(client *http.Client, price *handlers.Price) (*handlers.Price, error) {
var p handlers.Price var p handlers.Price
err := create(client, price, &p, "/v1/prices/") err := create(client, price, &p, "/v1/securities/"+strconv.FormatInt(price.SecurityId, 10)+"/prices/")
return &p, err return &p, err
} }
func getPrice(client *http.Client, priceid int64) (*handlers.Price, error) { func getPrice(client *http.Client, priceid, securityid int64) (*handlers.Price, error) {
var p handlers.Price var p handlers.Price
err := read(client, &p, "/v1/prices/"+strconv.FormatInt(priceid, 10)) err := read(client, &p, "/v1/securities/"+strconv.FormatInt(securityid, 10)+"/prices/"+strconv.FormatInt(priceid, 10))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &p, nil return &p, nil
} }
func getPrices(client *http.Client) (*handlers.PriceList, error) { func getPrices(client *http.Client, securityid int64) (*handlers.PriceList, error) {
var pl handlers.PriceList var pl handlers.PriceList
err := read(client, &pl, "/v1/prices/") err := read(client, &pl, "/v1/securities/"+strconv.FormatInt(securityid, 10)+"/prices/")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -34,7 +34,7 @@ func getPrices(client *http.Client) (*handlers.PriceList, error) {
func updatePrice(client *http.Client, price *handlers.Price) (*handlers.Price, error) { func updatePrice(client *http.Client, price *handlers.Price) (*handlers.Price, error) {
var p handlers.Price var p handlers.Price
err := update(client, price, &p, "/v1/prices/"+strconv.FormatInt(price.PriceId, 10)) err := update(client, price, &p, "/v1/securities/"+strconv.FormatInt(price.SecurityId, 10)+"/prices/"+strconv.FormatInt(price.PriceId, 10))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -42,7 +42,7 @@ func updatePrice(client *http.Client, price *handlers.Price) (*handlers.Price, e
} }
func deletePrice(client *http.Client, p *handlers.Price) error { func deletePrice(client *http.Client, p *handlers.Price) error {
err := remove(client, "/v1/prices/"+strconv.FormatInt(p.PriceId, 10)) err := remove(client, "/v1/securities/"+strconv.FormatInt(p.SecurityId, 10)+"/prices/"+strconv.FormatInt(p.PriceId, 10))
if err != nil { if err != nil {
return err return err
} }
@ -84,7 +84,7 @@ func TestGetPrice(t *testing.T) {
curr := d.prices[i] curr := d.prices[i]
userid := data[0].securities[orig.SecurityId].UserId userid := data[0].securities[orig.SecurityId].UserId
p, err := getPrice(d.clients[userid], curr.PriceId) p, err := getPrice(d.clients[userid], curr.PriceId, curr.SecurityId)
if err != nil { if err != nil {
t.Fatalf("Error fetching price: %s\n", err) t.Fatalf("Error fetching price: %s\n", err)
} }
@ -109,7 +109,12 @@ func TestGetPrice(t *testing.T) {
func TestGetPrices(t *testing.T) { func TestGetPrices(t *testing.T) {
RunWith(t, &data[0], func(t *testing.T, d *TestData) { RunWith(t, &data[0], func(t *testing.T, d *TestData) {
pl, err := getPrices(d.clients[0]) for origsecurityid, security := range d.securities {
if data[0].securities[origsecurityid].UserId != 0 {
continue
}
pl, err := getPrices(d.clients[0], security.SecurityId)
if err != nil { if err != nil {
t.Fatalf("Error fetching prices: %s\n", err) t.Fatalf("Error fetching prices: %s\n", err)
} }
@ -119,14 +124,14 @@ func TestGetPrices(t *testing.T) {
for i := 0; i < len(data[0].prices); i++ { for i := 0; i < len(data[0].prices); i++ {
orig := data[0].prices[i] orig := data[0].prices[i]
if data[0].securities[orig.SecurityId].UserId != 0 { if orig.SecurityId != int64(origsecurityid) {
continue continue
} }
numprices += 1 numprices += 1
found := false found := false
for _, p := range *pl.Prices { for _, p := range *pl.Prices {
if p.SecurityId == d.securities[orig.SecurityId].SecurityId && p.CurrencyId == d.securities[orig.CurrencyId].SecurityId && p.Date != orig.Date && p.Value != orig.Value && p.RemoteId != orig.RemoteId { if p.SecurityId == d.securities[orig.SecurityId].SecurityId && p.CurrencyId == d.securities[orig.CurrencyId].SecurityId && p.Date == orig.Date && p.Value == orig.Value && p.RemoteId == orig.RemoteId {
if _, ok := foundIds[p.PriceId]; ok { if _, ok := foundIds[p.PriceId]; ok {
continue continue
} }
@ -143,6 +148,7 @@ func TestGetPrices(t *testing.T) {
if numprices != len(*pl.Prices) { if numprices != len(*pl.Prices) {
t.Fatalf("Expected %d prices, received %d", numprices, len(*pl.Prices)) t.Fatalf("Expected %d prices, received %d", numprices, len(*pl.Prices))
} }
}
}) })
} }
@ -196,7 +202,7 @@ func TestDeletePrice(t *testing.T) {
t.Fatalf("Error deleting price: %s\n", err) t.Fatalf("Error deleting price: %s\n", err)
} }
_, err = getPrice(d.clients[userid], curr.PriceId) _, err = getPrice(d.clients[userid], curr.PriceId, curr.SecurityId)
if err == nil { if err == nil {
t.Fatalf("Expected error fetching deleted price") t.Fatalf("Expected error fetching deleted price")
} }

View File

@ -253,6 +253,17 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
} }
if r.Method == "POST" { if r.Method == "POST" {
if !context.LastLevel() {
securityid, err := context.NextID()
if err != nil {
return NewError(3 /*Invalid Request*/)
}
if context.NextLevel() != "prices" {
return NewError(3 /*Invalid Request*/)
}
return PriceHandler(r, context, user, securityid)
}
var security Security var security Security
if err := ReadJSON(r, &security); err != nil { if err := ReadJSON(r, &security); err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
@ -285,6 +296,14 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
if err != nil { if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }
if !context.LastLevel() {
if context.NextLevel() != "prices" {
return NewError(3 /*Invalid Request*/)
}
return PriceHandler(r, context, user, securityid)
}
security, err := GetSecurity(context.Tx, securityid, user.UserId) security, err := GetSecurity(context.Tx, securityid, user.UserId)
if err != nil { if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
@ -297,6 +316,13 @@ func SecurityHandler(r *http.Request, context *Context) ResponseWriterWriter {
if err != nil { if err != nil {
return NewError(3 /*Invalid Request*/) return NewError(3 /*Invalid Request*/)
} }
if !context.LastLevel() {
if context.NextLevel() != "prices" {
return NewError(3 /*Invalid Request*/)
}
return PriceHandler(r, context, user, securityid)
}
if r.Method == "PUT" { if r.Method == "PUT" {
var security Security var security Security
if err := ReadJSON(r, &security); err != nil || security.SecurityId != securityid { if err := ReadJSON(r, &security); err != nil || security.SecurityId != securityid {

View File

@ -200,6 +200,15 @@ var data = []TestData{
Type: handlers.Currency, Type: handlers.Currency,
AlternateId: "978", AlternateId: "978",
}, },
handlers.Security{
UserId: 0,
Name: "EUR",
Description: "Euro",
Symbol: "€",
Precision: 2,
Type: handlers.Currency,
AlternateId: "978",
},
}, },
prices: []handlers.Price{ prices: []handlers.Price{
handlers.Price{ handlers.Price{
@ -230,6 +239,13 @@ var data = []TestData{
Value: "227.21", Value: "227.21",
RemoteId: "12387-129831-1241", RemoteId: "12387-129831-1241",
}, },
handlers.Price{
SecurityId: 0,
CurrencyId: 3,
Date: time.Date(2017, time.November, 16, 18, 49, 53, 0, time.UTC),
Value: "0.85",
RemoteId: "USDEUR819298714",
},
}, },
accounts: []handlers.Account{ accounts: []handlers.Account{
handlers.Account{ handlers.Account{