aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Lemon <y@yulqen.org>2024-04-10 20:54:12 +0100
committerMatthew Lemon <y@yulqen.org>2024-04-10 20:54:12 +0100
commit24d51e78c462fdb829b10747eac470fc80e6fdce (patch)
treee225dc9d3bb3c9ae6f8efe0b9fd880cba4146770
parente69391d4f309f6268440632585bbddf3a2a5bd60 (diff)
Adds ability to save a single datamap to the database
-rw-r--r--Makefile7
-rw-r--r--cmd/dbasik-api/datamaps.go98
-rw-r--r--cmd/dbasik-api/main.go2
-rw-r--r--cmd/dbasik-api/routes.go3
-rw-r--r--resources/curl1
5 files changed, 102 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index fdd13c8..1821068 100644
--- a/Makefile
+++ b/Makefile
@@ -6,3 +6,10 @@ run-container:
run:
@docker compose up -d
+
+migrate:
+ docker exec -it dbasik-db-1 migrate -path=${DBASIK_DB_DSN} -database= up
+
+migrate-down:
+ docker exec -it dbasik-db-1 migrate -path=${DBASIK_DB_DSN} -database= down
+
diff --git a/cmd/dbasik-api/datamaps.go b/cmd/dbasik-api/datamaps.go
index 1e668ef..6a3231a 100644
--- a/cmd/dbasik-api/datamaps.go
+++ b/cmd/dbasik-api/datamaps.go
@@ -18,14 +18,29 @@
package main
import (
+ "database/sql"
"encoding/csv"
"encoding/json"
+ "errors"
"fmt"
"net/http"
"strconv"
"time"
)
+// A custom err to return from our Get() method when looking up a datamap
+// that doesn't exist
+var (
+ ErrRecordNotFound = errors.New("record not found")
+)
+
+// A Models struct wraps the DatmapModel. We can add other models to this as
+// we progress
+type Models struct {
+ Datamaps datamapModel
+ DatamapLines datamapLineModel
+}
+
// datamapLine holds the data parsed from each line of a submitted datamap CSV file.
// The fields need to be exported otherwise they won't be included when encoding
// the struct to json.
@@ -37,6 +52,10 @@ type datamapLine struct {
Cellref string `json:"cellref"`
}
+type datamapLineModel struct {
+ DB *sql.DB
+}
+
// datamap includes a slice of datamapLine objects alongside header metadata
type datamap struct {
ID int64 `json:"id"`
@@ -46,13 +65,61 @@ type datamap struct {
DMLs []datamapLine `json:"datamap_lines"`
}
+type datamapModel struct {
+ DB *sql.DB
+}
+
+func NewModels(db *sql.DB) Models {
+ return Models{
+ Datamaps: datamapModel{DB: db},
+ DatamapLines: datamapLineModel{DB: db},
+ }
+}
+
+func (m *datamapLineModel) Insert(dm datamap, dmls []datamapLine) (int, error) {
+ var datamapID int64
+ tx, err := m.DB.Begin()
+ if err != nil {
+ return 0, err
+ }
+ err = m.DB.QueryRow(`INSERT INTO datamaps (name, description, created)
+ VALUES ($1, $2, CURRENT_TIMESTAMP)
+ RETURNING id`, dm.Name, dm.Description).Scan(&datamapID)
+ if err != nil {
+ return 0, err
+ }
+
+ stmt, err := tx.Prepare(`INSERT INTO datamap_lines
+ (datamap_id, key, sheet, data_type, cellref)
+ VALUES ($1, $2, $3, $4, $5)`)
+ if err != nil {
+ return 0, err
+ }
+
+ defer stmt.Close()
+
+ for _, line := range dmls {
+ _, err := stmt.Exec(
+ int64(datamapID),
+ line.Key,
+ line.Sheet,
+ line.DataType,
+ line.Cellref)
+ if err != nil {
+ tx.Rollback()
+ return 0, err
+ }
+ }
+ tx.Commit()
+ return int(datamapID), nil
+}
+
func (app *application) createDatamapHandler(w http.ResponseWriter, r *http.Request) {
// Parse the multipart form
err := r.ParseMultipartForm(10 << 20) // 10Mb max
if err != nil {
app.serverErrorResponse(w, r, err)
}
-
// Get form values
dmName := r.FormValue("name")
app.logger.Info("obtain value from form", "name", dmName)
@@ -87,20 +154,35 @@ func (app *application) createDatamapHandler(w http.ResponseWriter, r *http.Requ
}
dmls = append(dmls, datamapLine{
- Key: line[1],
- Sheet: line[2],
- DataType: line[3],
- Cellref: line[4],
+ ID: 0,
+ Key: line[0],
+ Sheet: line[1],
+ DataType: line[2],
+ Cellref: line[3],
})
+
}
dm = datamap{Name: dmName, Description: dmDesc, Created: time.Now(), DMLs: dmls}
- err = app.writeJSONPretty(w, http.StatusOK, envelope{"datamap": dm}, nil)
+ // save to the database
+ _, err = app.models.DatamapLines.Insert(dm, dmls)
if err != nil {
- app.logger.Debug("writing out csv", "err", err)
- app.serverErrorResponse(w, r, err)
+ http.Error(w, "Cannot save to database", http.StatusBadRequest)
+ return
}
+}
+
+func (app *application) getJSONForDatamap(w http.ResponseWriter, r *http.Request) {
+ // Get the DM out of the database
+ // dm = datamap{Name: dmName, Description: dmDesc, Created: time.Now(), DMLs: dmls}
+
+ // err = app.writeJSONPretty(w, http.StatusOK, envelope{"datamap": dm}, nil)
+ // if err != nil {
+ // app.logger.Debug("writing out csv", "err", err)
+ // app.serverErrorResponse(w, r, err)
+ // }
+
// fmt.Fprintf(w, "file successfully uploaded")
}
diff --git a/cmd/dbasik-api/main.go b/cmd/dbasik-api/main.go
index f132c50..b4850f8 100644
--- a/cmd/dbasik-api/main.go
+++ b/cmd/dbasik-api/main.go
@@ -55,6 +55,7 @@ type config struct {
type application struct {
config config
logger *slog.Logger
+ models Models
}
func main() {
@@ -94,6 +95,7 @@ func main() {
app := &application{
config: cfg,
logger: logger,
+ models: NewModels(db),
}
// Declare an http server which listens provided in the config struct and has
diff --git a/cmd/dbasik-api/routes.go b/cmd/dbasik-api/routes.go
index f884855..0cc78a9 100644
--- a/cmd/dbasik-api/routes.go
+++ b/cmd/dbasik-api/routes.go
@@ -24,7 +24,8 @@ func (app *application) routes() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("GET /v1/healthcheck", app.healthcheckHandler)
- mux.HandleFunc("POST /v1/datamaps", app.createDatamapHandler)
+ mux.HandleFunc("GET /v1/getdatamap/{id}", app.getJSONForDatamap) // TODO: not yet implemented
+ mux.HandleFunc("POST /v1/datamap", app.createDatamapHandler)
mux.HandleFunc("POST /v1/datamapline", app.createDatamapLine)
mux.HandleFunc("GET /v1/datamaps/{id}", app.showDatamapHandler)
return mux
diff --git a/resources/curl b/resources/curl
index ac6668b..4a9c949 100644
--- a/resources/curl
+++ b/resources/curl
@@ -1 +1,2 @@
curl -X POST -F "file=@./resources/datamap.csv" -F "name=bobbins" -F "description=This is a long description of the datamap." http://localhost:4000/v1/datamaps|jq > /tmp/dm.json
+curl -X POST -F "file=@./resources/datamap.csv" -F "name=bobbins" -F "description=This is a long description of the datamap." http://localhost:5000/v1/datamaps