Compare commits
5 Commits
0397253ba7
...
19974a5f44
| Author | SHA1 | Date | |
|---|---|---|---|
| 19974a5f44 | |||
| a7359aacea | |||
| 771e4cad0c | |||
| eca17d0262 | |||
| 81c8f1a601 |
130
cmd/api-server/main.go
Normal file
130
cmd/api-server/main.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.rosemyrtle.work/personal-finance/server/internal/dal"
|
||||||
|
"git.rosemyrtle.work/personal-finance/server/internal/entities"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transaction struct represents a transaction
|
||||||
|
type Transaction struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
Category string `json:"category,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bank struct represents a bank
|
||||||
|
type Bank struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NordigenID string `json:"nordigenId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
db *dal.Connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Env) retrieveTransactions(c *gin.Context) {
|
||||||
|
// Handle the logic for retrieving transactions here
|
||||||
|
// limit := c.Query("limit")
|
||||||
|
// offset := c.Query("offset")
|
||||||
|
// bank := c.Query("bank")
|
||||||
|
// sort := c.Query("sort")
|
||||||
|
|
||||||
|
transactions := e.db.Transactions()
|
||||||
|
var ret []Transaction
|
||||||
|
|
||||||
|
for _, t := range transactions {
|
||||||
|
year, month, day := t.Date.Date()
|
||||||
|
var b strings.Builder
|
||||||
|
fmt.Fprintf(&b, "%d-%d-%d", year, month, day)
|
||||||
|
ret = append(ret, Transaction{t.Id, b.String(), t.Description, t.Value.InexactFloat64(), t.Category})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Env) retrieveTransactionByID(c *gin.Context) {
|
||||||
|
// Handle the logic for retrieving a transaction by ID here
|
||||||
|
// transactionID := c.Param("transactionId")
|
||||||
|
|
||||||
|
// Placeholder response
|
||||||
|
transaction := entities.Transaction{
|
||||||
|
Id: 1,
|
||||||
|
Date: time.Date(2024, 01, 24, 0, 0, 0, 0, time.UTC),
|
||||||
|
Description: "Groceries",
|
||||||
|
Value: decimal.NewFromFloat(-50.0),
|
||||||
|
Category: "Food",
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Env) retrieveBanks(c *gin.Context) {
|
||||||
|
// Handle the logic for retrieving banks here
|
||||||
|
|
||||||
|
// Placeholder response
|
||||||
|
banks := []Bank{
|
||||||
|
{ID: "1", Name: "Bank A", NordigenID: "uuid1"},
|
||||||
|
{ID: "2", Name: "Bank B", NordigenID: "uuid2"},
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, banks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Env) retrieveBankByID(c *gin.Context) {
|
||||||
|
// Handle the logic for retrieving a bank by ID here
|
||||||
|
bankID := c.Param("bankId")
|
||||||
|
|
||||||
|
// Placeholder response
|
||||||
|
bank := Bank{ID: bankID, Name: "Bank A", NordigenID: "uuid1"}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, bank)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := godotenv.Load(); err != nil {
|
||||||
|
log.Fatal("Error loading .env file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := os.Getenv("PG_USER")
|
||||||
|
pass := os.Getenv("PG_PASSWORD")
|
||||||
|
location := os.Getenv("PG_URL")
|
||||||
|
database := os.Getenv("PG_DB")
|
||||||
|
|
||||||
|
credentials := entities.Credentials{Database: "postgres://" + user + ":" + pass + "@" + location + "/" + database}
|
||||||
|
db, err := dal.Open(&credentials)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
env := Env{db: db}
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
router.GET("/transactions", env.retrieveTransactions)
|
||||||
|
router.GET("/transactions/:transactionId", env.retrieveTransactionByID)
|
||||||
|
router.GET("/banks", env.retrieveBanks)
|
||||||
|
router.GET("/banks/:bankId", env.retrieveBankByID)
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
port := 8080
|
||||||
|
fmt.Printf("Server is running on :%d...\n", port)
|
||||||
|
router.Run(fmt.Sprintf(":%d", port))
|
||||||
|
}
|
||||||
160
docs/openapi.yaml
Normal file
160
docs/openapi.yaml
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Personal Finance - OpenAPI 3.0
|
||||||
|
description: |
|
||||||
|
Personal Finance Server
|
||||||
|
contact:
|
||||||
|
email: finance@murta.dev
|
||||||
|
license:
|
||||||
|
name: GNU General Public License v3.0 or later
|
||||||
|
url: https://www.gnu.org/licenses/gpl-3.0-standalone.html
|
||||||
|
version: 0.0.1
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/transactions:
|
||||||
|
get:
|
||||||
|
summary: Retrieve existing transactions
|
||||||
|
operationId: retrieveTransactions
|
||||||
|
parameters:
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
description: number of transactions to return
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
default: 100
|
||||||
|
minimum: 0
|
||||||
|
- name: offset
|
||||||
|
in: query
|
||||||
|
description: offset from where to retrieve transactions
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
default: -1
|
||||||
|
- name: bank
|
||||||
|
in: query
|
||||||
|
description: ID of the bank
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: sort
|
||||||
|
in: query
|
||||||
|
description: field name by which to sort
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Transactions"
|
||||||
|
"404":
|
||||||
|
description: No transactions
|
||||||
|
|
||||||
|
/transactions/{transactionId}:
|
||||||
|
get:
|
||||||
|
summary: Find transaction by ID
|
||||||
|
operationId: retrieveTransactions
|
||||||
|
parameters:
|
||||||
|
- name: transactionId
|
||||||
|
in: path
|
||||||
|
description: ID of transaction to return
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Transaction"
|
||||||
|
"400":
|
||||||
|
description: Invalid ID supplied
|
||||||
|
"404":
|
||||||
|
description: Transaction not found
|
||||||
|
|
||||||
|
/banks:
|
||||||
|
get:
|
||||||
|
summary: Retrieve existing banks
|
||||||
|
operationId: retrieveBanks
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Banks"
|
||||||
|
"404":
|
||||||
|
description: No banks
|
||||||
|
|
||||||
|
/banks/{bankId}:
|
||||||
|
get:
|
||||||
|
summary: Find bank by ID
|
||||||
|
operationId: retrieveBank
|
||||||
|
parameters:
|
||||||
|
- name: bankId
|
||||||
|
in: path
|
||||||
|
description: ID of bank to return
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Bank"
|
||||||
|
"400":
|
||||||
|
description: Invalid ID supplied
|
||||||
|
"404":
|
||||||
|
description: Bank not found
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Transaction:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
minimum: 0
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: number
|
||||||
|
format: float
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- date
|
||||||
|
- description
|
||||||
|
- value
|
||||||
|
Transactions:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Transaction"
|
||||||
|
Bank:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
nordigenId:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
Banks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Bank"
|
||||||
37
go.mod
37
go.mod
@ -1,3 +1,40 @@
|
|||||||
module git.rosemyrtle.work/personal-finance/server
|
module git.rosemyrtle.work/personal-finance/server
|
||||||
|
|
||||||
go 1.21.1
|
go 1.21.1
|
||||||
|
|
||||||
|
require github.com/gin-gonic/gin v1.9.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
|
||||||
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.5.3 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
|
golang.org/x/net v0.10.0 // indirect
|
||||||
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
101
go.sum
Normal file
101
go.sum
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
|
||||||
|
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||||
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
60
internal/dal/dal.go
Normal file
60
internal/dal/dal.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package dal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.rosemyrtle.work/personal-finance/server/internal/entities"
|
||||||
|
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(credentials *entities.Credentials) (*Connection, error) {
|
||||||
|
if credentials == nil {
|
||||||
|
return nil, errors.New("missing credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("pgx", credentials.Database)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Connection{db}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connection) Close() {
|
||||||
|
c.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Connection) Transactions() []entities.Transaction {
|
||||||
|
q := "SELECT t.date, t.description, t.amount FROM transactions t;"
|
||||||
|
|
||||||
|
rows, err := conn.db.Query(q)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []entities.Transaction
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var t entities.Transaction
|
||||||
|
if err := rows.Scan(&t.Date, &t.Description, &t.Value); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
result = append(result, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
43
internal/dal/dal_test.go
Normal file
43
internal/dal/dal_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package dal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AnyTime struct{}
|
||||||
|
|
||||||
|
func (t AnyTime) Match(v driver.Value) bool {
|
||||||
|
_, ok := v.(time.Time)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// type AnyDecimal struct{}
|
||||||
|
|
||||||
|
func TestTransactions(t *testing.T) {
|
||||||
|
db, mock, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create a mock DB: %s", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
conn := Connection{db}
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"date", "description", "amount"}).
|
||||||
|
AddRow(time.Now(), "income", 1000).
|
||||||
|
AddRow(time.Now(), "expense", -500)
|
||||||
|
|
||||||
|
mock.ExpectQuery("SELECT t.date, t.description, t.amount").WithoutArgs().WillReturnRows(rows)
|
||||||
|
|
||||||
|
transactions := conn.Transactions()
|
||||||
|
if len(transactions) <= 0 {
|
||||||
|
t.Error("No transactions returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
internal/entities/credentials.go
Normal file
5
internal/entities/credentials.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
type Credentials struct {
|
||||||
|
Database string
|
||||||
|
}
|
||||||
15
internal/entities/entities.go
Normal file
15
internal/entities/entities.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Transaction struct {
|
||||||
|
Id int64
|
||||||
|
Date time.Time
|
||||||
|
Description string
|
||||||
|
Value decimal.Decimal
|
||||||
|
Category string
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user