api for initial widgets

main
Gabe Farrell 3 years ago
parent 36f751a69f
commit 4aa8a2f822

@ -0,0 +1,33 @@
package db
import (
"github.com/jacobmveber-01839764/BudgetBuddy/money"
)
type UserSchema struct {
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
Session string `json:"session" bson:"session"`
Balance money.Money `json:"balance" bson:"balance"`
Budget money.Money `json:"budget" bson:"budget"`
Categories map[string]money.Money `json:"categories" bson:"categories"`
Expenses []Transaction `json:"expenses" bson:"expenses"`
Income []Transaction `json:"income" bson:"income"`
RecurringExpenses []RecurringTransaction `json:"recurring_expenses" bson:"recurring_expenses"`
RecurringIncome []RecurringTransaction `json:"recurring_income" bson:"recurring_income"`
}
type Transaction struct {
Timestamp int64 `json:"timestamp" bson:"timestamp"`
Category string `json:"category" bson:"category"`
Amount money.Money `json:"amount" bson:"amount"`
Type string `json:"type" bson:"type"`
}
type RecurringTransaction struct {
Transaction `json:"transaction" bson:"transaction"`
Period int `json:"period" bson:"period"` // in days
Since int64 `json:"since" bson:"since"` // unix timestamp
Until int64 `json:"until" bson:"until"` // 0 for no end date
}

@ -8,6 +8,15 @@ import (
"github.com/go-chi/chi/v5"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/routes"
"github.com/jacobmveber-01839764/BudgetBuddy/widgets"
)
// TODO: expire transactions after one month on login
// TODO: perform recurring transactions on login
// TODO: give transactions ids so you can delete them
const (
_PORT = ":3030"
)
func main() {
@ -20,6 +29,10 @@ func main() {
r.Post("/auth/login", routes.Login)
r.Post("/auth/login/session", routes.Login)
r.Post("/auth/createaccount", routes.CreateAccount)
r.Get("/userinfo", routes.UserInfo)
r.Mount("/w", widgets.Router())
log.Fatal(http.ListenAndServe(":3030", r))
log.Println("* Listening on " + _PORT)
log.Fatal(http.ListenAndServe(_PORT, r))
}

@ -0,0 +1,8 @@
package money
type Currency string
const (
USD Currency = "USD"
CAD = "CAD"
)

@ -0,0 +1,31 @@
package money
type Money struct {
Currency Currency `json:"currency" bson:"currency"`
Whole int `json:"whole" bson:"whole"`
Decimal int `json:"decimal" bson:"decimal"`
}
// add x to y
func Add(x, y Money) Money {
x.Decimal += y.Decimal
if x.Decimal >= 100 {
x.Whole++
x.Decimal -= 100
}
x.Whole += y.Whole
return x
}
// subtract x from y
func Subtract(x, y Money) Money {
x.Decimal = y.Decimal - x.Decimal
if x.Decimal < 0 {
x.Whole++
x.Decimal += 100
}
x.Whole = y.Whole - x.Whole
return x
}

@ -18,6 +18,7 @@ import (
)
func CreateAccount(w http.ResponseWriter, r *http.Request) {
log.Println("* /auth/createaccount")
// prepare DB
err := db.Client.Ping(context.Background(), readpref.Primary())
if err != nil {
@ -26,7 +27,7 @@ func CreateAccount(w http.ResponseWriter, r *http.Request) {
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
// var v contains POST credentials
var v UserSchema
var v db.UserSchema
r.ParseForm()
v.Email = r.FormValue("email")
v.Password = r.FormValue("password")
@ -72,6 +73,8 @@ func CreateAccount(w http.ResponseWriter, r *http.Request) {
// add the new user to the database
v.Session = sessionID
v.Password = string(hashedPass)
v.Balance.Currency = "USD"
v.Budget.Currency = "USD"
_, err = userCollection.InsertOne(r.Context(), v)
if err != nil {
log.Println("* Error inserting new user")

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
@ -16,13 +17,6 @@ import (
"golang.org/x/crypto/bcrypt"
)
type UserSchema struct {
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
Session string `json:"session" bson:"session"`
}
type LoginResponse struct {
Name string `json:"name"`
Email string `json:"email"`
@ -30,6 +24,7 @@ type LoginResponse struct {
}
func Login(w http.ResponseWriter, r *http.Request) {
log.Println("* /auth/login")
// prepare DB
err := db.Client.Ping(context.Background(), readpref.Primary())
if err != nil {
@ -38,7 +33,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
// var v contains POST credentials
var v UserSchema
var v db.UserSchema
r.ParseForm()
v.Email = r.FormValue("email")
v.Password = r.FormValue("password")
@ -48,7 +43,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
}
// cmp struct will be compared with v to verify credentials
var cmp UserSchema
var cmp db.UserSchema
found := userCollection.FindOne(r.Context(), bson.D{primitive.E{Key: "email", Value: strings.ToLower(v.Email)}})
if found.Err() != nil {

@ -0,0 +1,55 @@
package routes
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type UserInfoResponse struct {
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
}
func UserInfo(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
found := userCollection.FindOne(r.Context(), bson.D{primitive.E{Key: "session", Value: strings.ToLower(session)}})
if found.Err() != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"session key invalid\"}")
return
}
var user = db.UserSchema{}
err := found.Decode(&user)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem decoding user\"}")
return
}
info := UserInfoResponse{
Name: user.Name,
Email: user.Email,
}
ret, err := json.Marshal(info)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}

@ -0,0 +1,115 @@
package widgets
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// TODO: Put user info fetching code into middleware
type GetBalanceResponse struct {
Status int `json:"status"`
RequestID string `json:"request_id"`
Data money.Money `json:"data"`
}
func GetBalance(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
response := GetBalanceResponse{
Status: 200,
RequestID: "0",
Data: money.Money{
Currency: user.Balance.Currency,
Whole: user.Balance.Whole,
Decimal: user.Balance.Decimal,
},
}
ret, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}
func SetBalance(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get form values
r.ParseForm()
newWhole, err := strconv.Atoi(r.Form.Get("whole"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect whole value\"}")
return
}
newDecimal, err := strconv.Atoi(r.Form.Get("decimal"))
if err != nil || newDecimal < 0 || newDecimal > 99 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect decimal value\"}")
return
}
// TODO: figure out how to efficiently determing currency
newBalance := money.Money{
Currency: "USD",
Whole: newWhole,
Decimal: newDecimal,
}
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
filter := bson.D{primitive.E{Key: "session", Value: session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "balance", Value: newBalance}}}}
_, err = userCollection.UpdateOne(context.TODO(), filter, update, opts)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
response := GetBalanceResponse{
Status: 200,
RequestID: "0",
Data: money.Money{
Currency: newBalance.Currency,
Whole: newBalance.Whole,
Decimal: newBalance.Decimal,
},
}
ret, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}

@ -0,0 +1,40 @@
package widgets
import (
"context"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
func addToBalance(user db.UserSchema, amount money.Money) bool {
newBalance := money.Add(amount, user.Balance)
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
filter := bson.D{primitive.E{Key: "session", Value: user.Session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "balance", Value: newBalance}}}}
_, err := userCollection.UpdateOne(context.TODO(), filter, update, opts)
return err == nil
}
func subtractFromBalance(user db.UserSchema, amount money.Money) bool {
// create money object to store in db
newBalance := money.Subtract(amount, user.Balance)
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
filter := bson.D{primitive.E{Key: "session", Value: user.Session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "balance", Value: newBalance}}}}
_, err := userCollection.UpdateOne(context.TODO(), filter, update, opts)
return err == nil
}

@ -0,0 +1,173 @@
package widgets
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
type BudgetResponse struct {
// total amount allowed to spend in a month
Budget money.Money `json:"budget"`
// total amount allowed to spend by category
BudgetCategories map[string]money.Money `json:"budget_categories"`
// categories in the budget
Categories []string `json:"categories"`
// transactions mapped to a category
Expenses map[string][]db.Transaction `json:"expenses"`
}
func GetBudget(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
var response BudgetResponse
response.Budget = user.Budget
response.BudgetCategories = user.Categories
cats := make([]string, len(user.Categories))
i := 0
for k := range user.Categories {
cats[i] = k
i++
}
response.Categories = cats
response.Expenses = make(map[string][]db.Transaction)
for _, e := range user.Expenses {
if response.Expenses[e.Category] == nil {
response.Expenses[e.Category] = make([]db.Transaction, 0)
}
response.Expenses[e.Category] = append(response.Expenses[e.Category], e)
}
ret, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}
func SetCategoryBudget(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
// get form values
r.ParseForm()
cat := r.FormValue("category")
if cat == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"category must be specified\"}")
return
}
newWhole, err := strconv.Atoi(r.Form.Get("whole"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect whole value\"}")
return
}
newDecimal, err := strconv.Atoi(r.Form.Get("decimal"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect decimal value\"}")
return
}
// TODO: figure out how to efficiently determing currency
newBudget := money.Money{
Currency: money.Currency(r.FormValue("currency")),
Whole: newWhole,
Decimal: newDecimal,
}
if user.Categories == nil {
user.Categories = make(map[string]money.Money)
}
user.Categories[cat] = newBudget
filter := bson.D{primitive.E{Key: "session", Value: session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "categories", Value: user.Categories}}}}
_, err = userCollection.UpdateOne(context.TODO(), filter, update, opts)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
w.Write([]byte("{\"status\":200}"))
}
func SetBudget(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get form values
r.ParseForm()
newWhole, err := strconv.Atoi(r.Form.Get("whole"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect whole value\"}")
return
}
newDecimal, err := strconv.Atoi(r.Form.Get("decimal"))
if err != nil || newDecimal < 0 || newDecimal > 99 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect decimal value\"}")
return
}
// TODO: figure out how to efficiently determing currency
newBudget := money.Money{
Currency: money.Currency(r.FormValue("currency")),
Whole: newWhole,
Decimal: newDecimal,
}
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
filter := bson.D{primitive.E{Key: "session", Value: session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "budget", Value: newBudget}}}}
_, err = userCollection.UpdateOne(context.TODO(), filter, update, opts)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
w.Write([]byte("{\"status\":200}"))
}

@ -0,0 +1,55 @@
package widgets
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func GetMonthExpenses(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
if user.Expenses == nil {
user.Expenses = make([]db.Transaction, 0)
}
total := money.Money{}
total.Currency = user.Balance.Currency
for i := 0; i < len(user.Expenses); i++ {
// stop if/when we get past a month ago
if user.Expenses[i].Timestamp < time.Now().Add(-30*24*time.Hour).Unix() {
break
}
total = money.Add(total, user.Expenses[i].Amount)
}
ret, err := json.Marshal(total)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}

@ -0,0 +1,55 @@
package widgets
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func GetMonthIncome(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
if user.Income == nil {
user.Income = make([]db.Transaction, 0)
}
total := money.Money{}
total.Currency = user.Balance.Currency
for i := 0; i < len(user.Income); i++ {
// stop if/when we get past a month ago
if user.Income[i].Timestamp < time.Now().Add(-30*24*time.Hour).Unix() {
break
}
total = money.Add(total, user.Income[i].Amount)
}
ret, err := json.Marshal(total)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
return
}
w.Write(ret)
}

@ -0,0 +1,29 @@
package widgets
import "github.com/go-chi/chi/v5"
func Router() *chi.Mux {
r := chi.NewRouter()
// balance widget
r.Get("/balance", GetBalance)
r.Post("/balance", SetBalance)
// transaction widget
r.Get("/transactions/recent", GetRecentTransactions)
r.Post("/transactions", NewTransaction)
r.Post("/transactions/recurring", NewRecurring)
// budget widget
r.Get("/budget", GetBudget)
r.Post("/budget", SetBudget)
r.Post("/budget/categories", SetCategoryBudget)
// expenses
r.Get("/expenses/month", GetMonthExpenses)
// income
r.Get("/income/month", GetMonthIncome)
return r
}

@ -0,0 +1,248 @@
package widgets
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"github.com/jacobmveber-01839764/BudgetBuddy/db"
"github.com/jacobmveber-01839764/BudgetBuddy/money"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
const (
RecentTransactionAmount = 10
)
type RecentTransactionsResponse struct {
Status int `json:"status"`
Transactions []db.Transaction `json:"transactions"`
}
func GetRecentTransactions(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
var transactionA []db.Transaction
i := 0
j := 0
for i+j < RecentTransactionAmount {
// if we are out of transactions, return
if i >= len(user.Expenses) && j >= len(user.Income) {
break
} else if i > len(user.Expenses)-1 { // if we are out of expenses, just use income
transactionA = append(transactionA, user.Income[j])
j++
} else if j > len(user.Income)-1 { // if we are out of income, just use expenses
transactionA = append(transactionA, user.Expenses[i])
i++
} else if user.Expenses[i].Timestamp > user.Income[j].Timestamp {
transactionA = append(transactionA, user.Expenses[i])
i++
} else {
transactionA = append(transactionA, user.Income[j])
j++
}
}
response := RecentTransactionsResponse{
Status: 200,
Transactions: transactionA,
}
ret, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"problem marshalling response\"}")
}
w.Write(ret)
}
func NewTransaction(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
r.ParseForm()
whole, err := strconv.Atoi(r.Form.Get("whole"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect whole value\"}")
return
}
decimal, err := strconv.Atoi(r.Form.Get("decimal"))
if err != nil || decimal < 0 || decimal > 99 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect decimal value\"}")
return
}
var cat string
if r.FormValue("category") == "" {
cat = "uncategorized"
} else {
cat = r.FormValue("category")
}
newT := db.Transaction{
Timestamp: time.Now().Unix(),
Category: cat,
Amount: money.Money{
Currency: money.Currency(r.FormValue("currency")),
Whole: whole,
Decimal: decimal,
},
Type: r.FormValue("type"),
}
var newArr []db.Transaction
var success bool
if r.FormValue("type") == "income" {
newArr = append(user.Income, newT)
success = addToBalance(user, newT.Amount)
} else if r.FormValue("type") == "expenses" {
newArr = append(user.Expenses, newT)
success = subtractFromBalance(user, newT.Amount)
} else {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"invalid transaction type - only income or expenses are allowed\"}")
return
}
if !success {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"unable to update balance\"}")
return
}
// push the new transaction to db
filter := bson.D{primitive.E{Key: "session", Value: user.Session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: r.FormValue("type"), Value: newArr}}}}
userCollection.UpdateOne(context.TODO(), filter, update, opts)
w.Write([]byte("{\"status\": 200}"))
}
func NewRecurring(w http.ResponseWriter, r *http.Request) {
// get session key from request
session := r.Header.Get("x-session-key")
// get collection handle from db
var userCollection = db.Client.Database("budgetbuddy").Collection("users")
var user = db.UserSchema{}
err := userCollection.FindOne(context.Background(), bson.D{primitive.E{Key: "session", Value: session}}).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "{\"error\":\"invalid session key\"}")
return
}
r.ParseForm()
whole, err := strconv.Atoi(r.Form.Get("whole"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect whole value\"}")
return
}
decimal, err := strconv.Atoi(r.Form.Get("decimal"))
if err != nil || decimal < 0 || decimal > 99 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"incorrect decimal value\"}")
return
}
period, err := strconv.Atoi(r.Form.Get("period"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"period must be specified\"}")
return
}
var cat string
if r.FormValue("category") == "" {
cat = "uncategorized"
}
newT := db.Transaction{
Timestamp: time.Now().Unix(),
Category: cat,
Amount: money.Money{
Currency: money.Currency(r.FormValue("currency")),
Whole: whole,
Decimal: decimal,
},
Type: r.FormValue("type"),
}
newR := db.RecurringTransaction{
Transaction: newT,
Period: period,
Since: time.Now().Unix(),
Until: int64(0),
}
var newArr []db.RecurringTransaction
var success bool
if r.FormValue("type") == "income" {
newArr = append(user.RecurringIncome, newR)
success = addToBalance(user, newT.Amount)
} else if r.FormValue("type") == "expenses" {
newArr = append(user.RecurringExpenses, newR)
success = subtractFromBalance(user, newT.Amount)
} else {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "{\"error\":\"invalid transaction type - only income or expenses are allowed\"}")
return
}
if !success {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "{\"error\":\"unable to update balance\"}")
return
}
// push the new transaction to db
filter := bson.D{primitive.E{Key: "session", Value: user.Session}}
opts := options.Update().SetUpsert(true)
update := bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: r.FormValue("type"), Value: newArr}}}}
userCollection.UpdateOne(context.TODO(), filter, update, opts)
w.Write([]byte("{\"status\": 200}"))
}
Loading…
Cancel
Save