diff options
author | Matthew Lemon <y@yulqen.org> | 2024-04-10 20:54:12 +0100 |
---|---|---|
committer | Matthew Lemon <y@yulqen.org> | 2024-04-10 20:54:12 +0100 |
commit | 24d51e78c462fdb829b10747eac470fc80e6fdce (patch) | |
tree | e225dc9d3bb3c9ae6f8efe0b9fd880cba4146770 | |
parent | e69391d4f309f6268440632585bbddf3a2a5bd60 (diff) |
Adds ability to save a single datamap to the database
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | cmd/dbasik-api/datamaps.go | 98 | ||||
-rw-r--r-- | cmd/dbasik-api/main.go | 2 | ||||
-rw-r--r-- | cmd/dbasik-api/routes.go | 3 | ||||
-rw-r--r-- | resources/curl | 1 |
5 files changed, 102 insertions, 9 deletions
@@ -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 |