Compare commits

...

3 Commits

Author SHA1 Message Date
6f1081589e Merge branch 'master' into itsscb/issue85 2023-10-23 23:12:32 +02:00
a31f8de478
Merge branch 'master' into itsscb/issue85 2023-10-23 22:42:35 +02:00
6b1f30f0f7 ft/adds table queries
TODO: Add support for multi table requests - currently the existing fields are being merged.
2023-10-17 23:16:31 +02:00
11 changed files with 253 additions and 0 deletions

@ -0,0 +1 @@
DROP TABLE IF EXISTS "queries";

@ -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'
);

@ -643,6 +643,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()
@ -898,6 +913,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...)
}
// UpdateAccountInfo mocks base method.
func (m *MockStore) UpdateAccountInfo(arg0 context.Context, arg1 db.UpdateAccountInfoParams) (db.AccountInfo, error) {
m.ctrl.T.Helper()

3
bff/db/query/query.sql Normal file

@ -0,0 +1,3 @@
-- name: GetQueryByName :one
SELECT * FROM queries
WHERE "name" = sqlc.arg(name);

@ -115,6 +115,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"`

@ -61,6 +61,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

@ -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

@ -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)
UpdateAccountTx(ctx context.Context, arg UpdateAccountTxParams) (Account, error)
}

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
}

@ -143,6 +143,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)