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
|
docker run --name migratedown --privileged=true --rm -v $(PWD)/bff/db/migration:/migrations --network host migrate/migrate -path=/migrations/ -database $(DB_URL) down
|
||||||
|
|
||||||
createdb:
|
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:
|
dropdb:
|
||||||
docker exec -it postgres dropdb df
|
docker exec -it df-bff_postgres_1 dropdb df
|
||||||
|
|
||||||
sqlc:
|
sqlc:
|
||||||
cd bff && \
|
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)
|
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.
|
// GetReturn mocks base method.
|
||||||
func (m *MockStore) GetReturn(arg0 context.Context, arg1 uint64) (db.Return, error) {
|
func (m *MockStore) GetReturn(arg0 context.Context, arg1 uint64) (db.Return, error) {
|
||||||
m.ctrl.T.Helper()
|
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)
|
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.
|
// UpdateAccount mocks base method.
|
||||||
func (m *MockStore) UpdateAccount(arg0 context.Context, arg1 db.UpdateAccountParams) (db.Account, error) {
|
func (m *MockStore) UpdateAccount(arg0 context.Context, arg1 db.UpdateAccountParams) (db.Account, error) {
|
||||||
m.ctrl.T.Helper()
|
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"`
|
Changed time.Time `json:"changed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Query struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Query string `json:"query"`
|
||||||
|
}
|
||||||
|
|
||||||
type Return struct {
|
type Return struct {
|
||||||
ID uint64 `json:"id"`
|
ID uint64 `json:"id"`
|
||||||
PersonID uint64 `json:"person_id"`
|
PersonID uint64 `json:"person_id"`
|
||||||
|
@ -59,6 +59,7 @@ type Querier interface {
|
|||||||
GetPayment(ctx context.Context, id uint64) (Payment, error)
|
GetPayment(ctx context.Context, id uint64) (Payment, error)
|
||||||
GetPerson(ctx context.Context, id uint64) (Person, error)
|
GetPerson(ctx context.Context, id uint64) (Person, error)
|
||||||
GetProvider(ctx context.Context, id uint64) (Provider, 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)
|
GetReturn(ctx context.Context, id uint64) (Return, error)
|
||||||
GetReturnIDsByPersonID(ctx context.Context, personID uint64) ([]uint64, error)
|
GetReturnIDsByPersonID(ctx context.Context, personID uint64) ([]uint64, error)
|
||||||
GetReturns(ctx context.Context, id uint64) ([]Return, 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
|
DeletePersonTx(ctx context.Context, id uint64) error
|
||||||
CreateDocumentTx(ctx context.Context, arg CreateDocumentTxParams) (doc Document, code int, err error)
|
CreateDocumentTx(ctx context.Context, arg CreateDocumentTxParams) (doc Document, code int, err error)
|
||||||
DeleteDocumentTx(ctx context.Context, id uint64) (code codes.Code, 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
|
// 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 := gin.New()
|
||||||
mux.Group("v1/*{grpc_gateway}").Any("", gin.WrapH(grpcMux))
|
mux.Group("v1/*{grpc_gateway}").Any("", gin.WrapH(grpcMux))
|
||||||
mux.POST("documents/upload", server.UploadDocument)
|
mux.POST("documents/upload", server.UploadDocument)
|
||||||
|
mux.POST("query", server.Query)
|
||||||
mux.StaticFS("/swagger/", swaggerFS)
|
mux.StaticFS("/swagger/", swaggerFS)
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", config.HTTPServerAddress)
|
listener, err := net.Listen("tcp", config.HTTPServerAddress)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user