ft/adds table queries
TODO: Add support for multi table requests - currently the existing fields are being merged.
This commit is contained in:
parent
03c452e748
commit
6b1f30f0f7
4
Makefile
4
Makefile
@ -52,10 +52,10 @@ migratedown:
|
||||
docker run --name migratedown --privileged=true --rm -v $(PWD)/bff/db/migration:/migrations --network host migrate/migrate -path=/migrations/ -database $(DB_URL) down
|
||||
|
||||
createdb:
|
||||
docker exec -it postgres createdb --username=root --owner=root df
|
||||
docker exec -it df-bff_postgres_1 createdb --username=root --owner=root df
|
||||
|
||||
dropdb:
|
||||
docker exec -it postgres dropdb df
|
||||
docker exec -it df-bff_postgres_1 dropdb df
|
||||
|
||||
sqlc:
|
||||
cd bff && \
|
||||
|
1
bff/db/migration/000003_add_query_table.down.sql
Normal file
1
bff/db/migration/000003_add_query_table.down.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS "queries";
|
17
bff/db/migration/000003_add_query_table.up.sql
Normal file
17
bff/db/migration/000003_add_query_table.up.sql
Normal file
@ -0,0 +1,17 @@
|
||||
CREATE TABLE "queries" (
|
||||
"id" bigserial UNIQUE PRIMARY KEY NOT NULL,
|
||||
"name" varchar NOT NULL,
|
||||
"query" varchar NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "queries" (name, query)
|
||||
VALUES (
|
||||
'all_accounts',
|
||||
'SELECT * FROM accounts'
|
||||
);
|
||||
|
||||
INSERT INTO "queries" (name, query)
|
||||
VALUES (
|
||||
'all_persons',
|
||||
'SELECT * FROM persons'
|
||||
);
|
@ -614,6 +614,21 @@ func (mr *MockStoreMockRecorder) GetProvider(arg0, arg1 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProvider", reflect.TypeOf((*MockStore)(nil).GetProvider), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetQueryByName mocks base method.
|
||||
func (m *MockStore) GetQueryByName(arg0 context.Context, arg1 string) (db.Query, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetQueryByName", arg0, arg1)
|
||||
ret0, _ := ret[0].(db.Query)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetQueryByName indicates an expected call of GetQueryByName.
|
||||
func (mr *MockStoreMockRecorder) GetQueryByName(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueryByName", reflect.TypeOf((*MockStore)(nil).GetQueryByName), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetReturn mocks base method.
|
||||
func (m *MockStore) GetReturn(arg0 context.Context, arg1 uint64) (db.Return, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -854,6 +869,26 @@ func (mr *MockStoreMockRecorder) ListSessions(arg0, arg1 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSessions", reflect.TypeOf((*MockStore)(nil).ListSessions), arg0, arg1)
|
||||
}
|
||||
|
||||
// Query mocks base method.
|
||||
func (m *MockStore) Query(arg0 context.Context, arg1 string, arg2 ...any) ([]map[string]any, error) {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{arg0, arg1}
|
||||
for _, a := range arg2 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Query", varargs...)
|
||||
ret0, _ := ret[0].([]map[string]any)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Query indicates an expected call of Query.
|
||||
func (mr *MockStoreMockRecorder) Query(arg0, arg1 any, arg2 ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
varargs := append([]any{arg0, arg1}, arg2...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockStore)(nil).Query), varargs...)
|
||||
}
|
||||
|
||||
// UpdateAccount mocks base method.
|
||||
func (m *MockStore) UpdateAccount(arg0 context.Context, arg1 db.UpdateAccountParams) (db.Account, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
3
bff/db/query/query.sql
Normal file
3
bff/db/query/query.sql
Normal file
@ -0,0 +1,3 @@
|
||||
-- name: GetQueryByName :one
|
||||
SELECT * FROM queries
|
||||
WHERE "name" = sqlc.arg(name);
|
@ -108,6 +108,12 @@ type Provider struct {
|
||||
Changed time.Time `json:"changed"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type Return struct {
|
||||
ID uint64 `json:"id"`
|
||||
PersonID uint64 `json:"person_id"`
|
||||
|
@ -59,6 +59,7 @@ type Querier interface {
|
||||
GetPayment(ctx context.Context, id uint64) (Payment, error)
|
||||
GetPerson(ctx context.Context, id uint64) (Person, error)
|
||||
GetProvider(ctx context.Context, id uint64) (Provider, error)
|
||||
GetQueryByName(ctx context.Context, name string) (Query, error)
|
||||
GetReturn(ctx context.Context, id uint64) (Return, error)
|
||||
GetReturnIDsByPersonID(ctx context.Context, personID uint64) ([]uint64, error)
|
||||
GetReturns(ctx context.Context, id uint64) ([]Return, error)
|
||||
|
57
bff/db/sqlc/query.go
Normal file
57
bff/db/sqlc/query.go
Normal file
@ -0,0 +1,57 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
var (
|
||||
restrictedFields = map[string]bool{
|
||||
"passwordhash": true,
|
||||
}
|
||||
)
|
||||
|
||||
func (store *SQLStore) Query(ctx context.Context, statement string, args ...interface{}) (result []map[string]interface{}, err error) {
|
||||
var rows *sql.Rows
|
||||
// var err error
|
||||
|
||||
if len(args) > 0 {
|
||||
rows, err = store.db.QueryContext(ctx, statement, args)
|
||||
} else {
|
||||
rows, err = store.db.QueryContext(ctx, statement)
|
||||
}
|
||||
if err != nil {
|
||||
slog.Error("db_query", slog.String("statement", statement), slog.String("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cols, err := rows.Columns()
|
||||
if err != nil {
|
||||
slog.Error("db_query: getting columns", slog.String("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
values := make([]interface{}, len(cols))
|
||||
res := make(map[string]interface{})
|
||||
for i := range values {
|
||||
values[i] = new(interface{})
|
||||
}
|
||||
err = rows.Scan(values...)
|
||||
if err != nil {
|
||||
slog.Error("db_query: scanning rows", slog.String("error", err.Error()))
|
||||
return nil, err
|
||||
}
|
||||
for i, col := range cols {
|
||||
if restrictedFields[col] {
|
||||
continue
|
||||
}
|
||||
res[col] = *(values[i].(*interface{}))
|
||||
}
|
||||
|
||||
result = append(result, res)
|
||||
}
|
||||
|
||||
return result, rows.Err()
|
||||
}
|
22
bff/db/sqlc/query.sql.go
Normal file
22
bff/db/sqlc/query.sql.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.22.0
|
||||
// source: query.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getQueryByName = `-- name: GetQueryByName :one
|
||||
SELECT id, name, query FROM queries
|
||||
WHERE "name" = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetQueryByName(ctx context.Context, name string) (Query, error) {
|
||||
row := q.db.QueryRowContext(ctx, getQueryByName, name)
|
||||
var i Query
|
||||
err := row.Scan(&i.ID, &i.Name, &i.Query)
|
||||
return i, err
|
||||
}
|
@ -17,6 +17,7 @@ type Store interface {
|
||||
DeletePersonTx(ctx context.Context, id uint64) error
|
||||
CreateDocumentTx(ctx context.Context, arg CreateDocumentTxParams) (doc Document, code int, err error)
|
||||
DeleteDocumentTx(ctx context.Context, id uint64) (code codes.Code, err error)
|
||||
Query(ctx context.Context, statement string, args ...interface{}) (result []map[string]interface{}, err error)
|
||||
}
|
||||
|
||||
// Store provides all functions to execute db queries and transactions
|
||||
|
109
bff/gw/query.go
Normal file
109
bff/gw/query.go
Normal file
@ -0,0 +1,109 @@
|
||||
package gw
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (server *Server) Query(ctx *gin.Context) {
|
||||
// authHeader := ctx.GetHeader("authorization")
|
||||
|
||||
// authFields := strings.Fields(authHeader)
|
||||
|
||||
// if len(authFields) != 2 {
|
||||
// ctx.JSON(http.StatusUnauthorized, errorResponse(errors.New("invalid or missing authorization header")))
|
||||
// return
|
||||
// }
|
||||
|
||||
// token := authFields[1]
|
||||
|
||||
// authPayload, err := server.tokenMaker.VerifyToken(token)
|
||||
// if err != nil {
|
||||
// ctx.JSON(http.StatusUnauthorized, errorResponse(errors.New("invalid authorization header")))
|
||||
// return
|
||||
// }
|
||||
|
||||
// account, err := server.store.GetAccount(ctx, authPayload.AccountID)
|
||||
// if err != nil {
|
||||
// ctx.JSON(http.StatusNotFound, errorResponse(errors.New("account not found")))
|
||||
// return
|
||||
// }
|
||||
|
||||
var jsonData map[string]interface{}
|
||||
data, err := io.ReadAll(ctx.Request.Body)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(errors.New("failed to parse request body")))
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(data))
|
||||
|
||||
if err = json.Unmarshal(data, &jsonData); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(errors.New("failed to unmarshal request body json")))
|
||||
return
|
||||
}
|
||||
|
||||
name, ok := jsonData["name"]
|
||||
if !ok {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(errors.New("required query name is missing")))
|
||||
return
|
||||
}
|
||||
|
||||
q, err := server.store.GetQueryByName(ctx, fmt.Sprintf("%s", name))
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(errors.New("failed to get query")))
|
||||
return
|
||||
}
|
||||
|
||||
res, err := server.store.Query(ctx, q.Query)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, errorResponse(fmt.Errorf("failed to run query: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
return
|
||||
|
||||
// var req *uploadDocumentRequest
|
||||
// err = ctx.ShouldBind(&req)
|
||||
// if err != nil {
|
||||
// ctx.JSON(http.StatusBadRequest, errorResponse(errors.New("failed to parse request")))
|
||||
// return
|
||||
// }
|
||||
|
||||
// r := db.CreateDocumentTxParams{
|
||||
// AccountID: account.ID,
|
||||
// PersonID: req.PersonID,
|
||||
// MailID: req.MailID,
|
||||
// File: req.File,
|
||||
// Creator: account.Email,
|
||||
// }
|
||||
|
||||
// doc, code, err := server.store.CreateDocumentTx(ctx, r)
|
||||
// if err != nil {
|
||||
// if code == http.StatusInternalServerError {
|
||||
// slog.Error("create_document", slog.Int64("invoked_by", int64(authPayload.AccountID)), slog.String("document_name", req.File.Filename), slog.String("error", err.Error()))
|
||||
// }
|
||||
// ctx.JSON(code, errorResponse(err))
|
||||
// return
|
||||
// }
|
||||
|
||||
// ctx.JSON(http.StatusOK, doc)
|
||||
}
|
||||
|
||||
func clearStatement(statement string) string {
|
||||
statement = strings.ReplaceAll(statement, ";", "")
|
||||
statement = strings.ReplaceAll(statement, "'", "")
|
||||
statement = strings.ReplaceAll(statement, "\"", "")
|
||||
statement = strings.ReplaceAll(statement, "`", "")
|
||||
statement = strings.ReplaceAll(statement, "DROP", "")
|
||||
statement = strings.ReplaceAll(statement, "DELETE", "")
|
||||
|
||||
return statement
|
||||
}
|
@ -144,6 +144,7 @@ func runGatewayServer(config util.Config, store db.Store, swaggerFS http.FileSys
|
||||
mux := gin.New()
|
||||
mux.Group("v1/*{grpc_gateway}").Any("", gin.WrapH(grpcMux))
|
||||
mux.POST("documents/upload", server.UploadDocument)
|
||||
mux.POST("query", server.Query)
|
||||
mux.StaticFS("/swagger/", swaggerFS)
|
||||
|
||||
listener, err := net.Listen("tcp", config.HTTPServerAddress)
|
||||
|
Loading…
x
Reference in New Issue
Block a user