Merged in feature/update (pull request #27)

Query Update

* baselineupdate

* baseupdateplusmodelupdates

* passtests

* somemoresubmittesting

* testinnerfunctions

* readmeandinstall

* cleanerstartup

* readmeplusdeps

* tidyatrighttime

* validatetests

* normalizedontvalidate

* abitofzenormalizationcleanup

* addunitstestforhelperfuns

* normalizeactiveversiontestas
This commit is contained in:
Michael McGuinness
2025-01-20 13:31:48 +00:00
parent 897fd1c6dd
commit 518927c395
52 changed files with 1705 additions and 461 deletions
+52 -21
View File
@@ -14,12 +14,7 @@ import (
)
func (s *Service) Create(ctx context.Context, entity *queryprocessor.Create) (uuid.UUID, error) {
validator, err := s.getCreator(entity.Type)
if err != nil {
return uuid.Nil, err
}
err = validator.Validate(ctx, entity)
err := s.normalizeCreate(ctx, entity)
if err != nil {
return uuid.Nil, err
}
@@ -32,6 +27,30 @@ func (s *Service) Create(ctx context.Context, entity *queryprocessor.Create) (uu
return id, err
}
func (s *Service) normalizeCreate(ctx context.Context, entity *queryprocessor.Create) error {
err := s.normalizeQueryIDs(ctx, entity)
if err != nil {
return err
}
err = s.normalizeConfig(entity)
if err != nil {
return err
}
validator, err := s.getCreator(entity.Type)
if err != nil {
return err
}
err = validator.Validate(ctx, entity)
if err != nil {
return err
}
return nil
}
func (s *Service) submitCreate(ctx context.Context, entity *queryprocessor.Create) (uuid.UUID, error) {
query, err := parseCreateQuery(entity)
if err != nil {
@@ -53,21 +72,23 @@ func (s *Service) submitCreate(ctx context.Context, entity *queryprocessor.Creat
return uuid.Nil, err
}
for _, reqQuery := range query.RequiredQueryIDs {
err = qtx.CreateRequiredQuery(ctx, &repository.CreateRequiredQueryParams{
Queryid: dbID,
Requiredqueryid: reqQuery,
Addedversion: 1,
})
if err != nil {
return uuid.Nil, err
if query.RequiredQueryIDs != nil {
for _, reqQuery := range *query.RequiredQueryIDs {
err = qtx.AddRequiredQuery(ctx, &repository.AddRequiredQueryParams{
Queryid: dbID,
Requiredqueryid: reqQuery,
Addedversion: 1,
})
if err != nil {
return uuid.Nil, err
}
}
}
if query.Config != nil && string(query.Config) != "" {
err = qtx.CreateQueryConfig(ctx, &repository.CreateQueryConfigParams{
if query.Config != nil && string(*query.Config) != "" {
err = qtx.AddQueryConfig(ctx, &repository.AddQueryConfigParams{
Queryid: dbID,
Config: query.Config,
Config: *query.Config,
Addedversion: 1,
})
if err != nil {
@@ -99,8 +120,8 @@ func (s *Service) getCreator(qType queryprocessor.Type) (queryprocessor.Creator,
type createQuery struct {
Type repository.Querytype
RequiredQueryIDs []pgtype.UUID
Config []byte
RequiredQueryIDs *[]pgtype.UUID
Config *[]byte
}
func parseCreateQuery(q *queryprocessor.Create) (*createQuery, error) {
@@ -109,11 +130,21 @@ func parseCreateQuery(q *queryprocessor.Create) (*createQuery, error) {
return nil, err
}
reqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
var reqIDs *[]pgtype.UUID
if q.RequiredQueryIDs != nil {
tIDs := database.MustToDBUUIDArray(*q.RequiredQueryIDs)
reqIDs = &tIDs
}
var cfg *[]byte
if q.Config != nil {
tC := []byte(*q.Config)
cfg = &tC
}
return &createQuery{
Type: t,
RequiredQueryIDs: reqIDs,
Config: []byte(q.Config),
Config: cfg,
}, nil
}
+81 -5
View File
@@ -2,6 +2,7 @@ package query_test
import (
"context"
"errors"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/query"
@@ -32,10 +33,10 @@ func TestCreate(t *testing.T) {
q := query.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
}
create := &queryprocessor.Create{
Type: q.Type,
@@ -46,16 +47,20 @@ func TestCreate(t *testing.T) {
dbType, err := queryprocessor.ToDBQueryType(create.Type)
assert.Nil(t, err)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(database.MustToDBUUIDArray(*create.RequiredQueryIDs)).WillReturnRows(
pgxmock.NewRows([]string{"all_exist"}).AddRow(true),
)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectQuery("name: CreateQuery :one").WithArgs(dbType).WillReturnRows(
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(q.ID)),
)
for _, req := range create.RequiredQueryIDs {
pool.ExpectExec("name: CreateRequiredQuery :exec").WithArgs(database.MustToDBUUID(q.ID), database.MustToDBUUID(req), int32(1)).
for _, req := range *create.RequiredQueryIDs {
pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(q.ID), database.MustToDBUUID(req), int32(1)).
WillReturnResult(pgxmock.NewResult("", 1))
}
pool.ExpectExec("name: CreateQueryConfig :exec").WithArgs(database.MustToDBUUID(q.ID), []byte(create.Config), int32(1)).
pool.ExpectExec("name: AddQueryConfig :exec").WithArgs(database.MustToDBUUID(q.ID), []byte(*create.Config), int32(1)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
@@ -63,3 +68,74 @@ func TestCreate(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, q.ID, id)
}
func TestCreateMinimal(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := query.New(db)
q := query.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
}
create := &queryprocessor.Create{
Type: q.Type,
}
dbType, err := queryprocessor.ToDBQueryType(create.Type)
assert.Nil(t, err)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectQuery("name: CreateQuery :one").WithArgs(dbType).WillReturnRows(
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(q.ID)),
)
pool.ExpectCommit()
id, err := svc.Create(ctx, create)
assert.Nil(t, err)
assert.Equal(t, q.ID, id)
}
func TestCreateRollback(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := query.New(db)
q := query.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
}
create := &queryprocessor.Create{
Type: q.Type,
}
dbType, err := queryprocessor.ToDBQueryType(create.Type)
assert.Nil(t, err)
pool.ExpectBeginTx(pgx.TxOptions{})
msg := "database failure"
pool.ExpectQuery("name: CreateQuery :one").WithArgs(dbType).WillReturnError(errors.New(msg))
pool.ExpectRollback()
_, err = svc.Create(ctx, create)
assert.EqualError(t, err, msg)
}
+89 -11
View File
@@ -41,30 +41,34 @@ func TestGetCreator(t *testing.T) {
}
func TestParseCreateQuery(t *testing.T) {
cfg := "{\"key\":\"value\"}"
cQuery := &queryprocessor.Create{
Type: queryprocessor.TypeContextFull,
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: "{\"key\":\"value\"}",
Config: &cfg,
}
resultQuery, err := parseCreateQuery(cQuery)
assert.Nil(t, err)
rQIDs := database.MustToDBUUIDArray(*cQuery.RequiredQueryIDs)
qcfg := []byte(*cQuery.Config)
assert.EqualExportedValues(t, createQuery{
Type: repository.QuerytypeContextFull,
RequiredQueryIDs: database.MustToDBUUIDArray(cQuery.RequiredQueryIDs),
Config: []byte(cQuery.Config),
RequiredQueryIDs: &rQIDs,
Config: &qcfg,
}, *resultQuery)
}
func TestParseCreateQueryInvalidType(t *testing.T) {
cfg := "{\"key\":\"value\"}"
cQuery := &queryprocessor.Create{
Type: queryprocessor.Type(-1),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: "{\"key\":\"value\"}",
Config: &cfg,
}
_, err := parseCreateQuery(cQuery)
@@ -89,10 +93,10 @@ func TestSubmitCreate(t *testing.T) {
q := Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
}
create := &queryprocessor.Create{
Type: q.Type,
@@ -108,11 +112,11 @@ func TestSubmitCreate(t *testing.T) {
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(q.ID)),
)
for _, req := range create.RequiredQueryIDs {
pool.ExpectExec("name: CreateRequiredQuery :exec").WithArgs(database.MustToDBUUID(q.ID), database.MustToDBUUID(req), int32(1)).
for _, req := range *create.RequiredQueryIDs {
pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(q.ID), database.MustToDBUUID(req), int32(1)).
WillReturnResult(pgxmock.NewResult("", 1))
}
pool.ExpectExec("name: CreateQueryConfig :exec").WithArgs(database.MustToDBUUID(q.ID), []byte(create.Config), int32(1)).
pool.ExpectExec("name: AddQueryConfig :exec").WithArgs(database.MustToDBUUID(q.ID), []byte(*create.Config), int32(1)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
@@ -120,3 +124,77 @@ func TestSubmitCreate(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, q.ID, id)
}
func TestSubmitCreateNoReqsOrConfig(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
q := Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
}
create := &queryprocessor.Create{
Type: q.Type,
}
dbType, err := queryprocessor.ToDBQueryType(create.Type)
assert.Nil(t, err)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectQuery("name: CreateQuery :one").WithArgs(dbType).WillReturnRows(
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(q.ID)),
)
pool.ExpectCommit()
id, err := svc.submitCreate(ctx, create)
assert.Nil(t, err)
assert.Equal(t, q.ID, id)
}
func TestNormalizeCreate(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
cfg := "{}"
create := &queryprocessor.Create{
Type: queryprocessor.TypeJsonExtractor,
Config: &cfg,
RequiredQueryIDs: &[]uuid.UUID{},
}
dbids := database.MustToDBUUIDArray(*create.RequiredQueryIDs)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).WillReturnRows(
pgxmock.NewRows([]string{"all_exist"}).
AddRow(true),
)
err = svc.normalizeCreate(ctx, create)
assert.Nil(t, err)
assert.EqualExportedValues(t, queryprocessor.Create{
Type: queryprocessor.TypeJsonExtractor,
Config: nil,
RequiredQueryIDs: nil,
}, *create)
}
+8 -4
View File
@@ -129,18 +129,22 @@ func TestSyncDBFail(t *testing.T) {
pgxmock.NewRows([]string{"id", "jobId", "minCleanVersion", "minTextVersion"}).
AddRow(dbCollectorId, dbJobID, minCleanVersion, minTextVersion),
)
qV := int32(1)
reqID := database.MustToDBUUID(uuid.New())
resID := database.MustToDBUUID(uuid.New())
pool.ExpectQuery("name: ListResultsByDocumentID :many").WithArgs(database.MustToDBUUID(doc.ID), minCleanVersion, minTextVersion).
WillReturnRows(
pgxmock.NewRows([]string{"id", "queryId", "queryVersion"}),
pgxmock.NewRows([]string{"id", "queryId", "queryVersion"}).
AddRow(resID, reqID, qV),
)
dbErr = "database failure"
qV := int32(1)
pool.ExpectQuery("name: GetCollectorQueries :many").WithArgs(dbCollectorId).
WillReturnRows(
pgxmock.NewRows([]string{"collectorId", "queryId", "type", "queryVersion", "requiredIds"}).
AddRow(dbCollectorId, dbQueryID, repository.NullQuerytype{Querytype: repository.QuerytypeJsonExtractor, Valid: true}, &qV, []pgtype.UUID{}),
AddRow(dbCollectorId, dbQueryID, repository.NullQuerytype{Querytype: repository.QuerytypeJsonExtractor, Valid: true}, &qV, []pgtype.UUID{reqID}).
AddRow(dbCollectorId, reqID, repository.NullQuerytype{Querytype: repository.QuerytypeContextFull, Valid: true}, &qV, []pgtype.UUID{}),
)
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{}).
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{resID}).
WillReturnError(errors.New(dbErr))
docSvc = document.New(db)
+16 -6
View File
@@ -14,8 +14,8 @@ type Query struct {
Type queryprocessor.Type
ActiveVersion int32
LatestVersion int32
RequiredQueryIDs []uuid.UUID
Config string
RequiredQueryIDs *[]uuid.UUID
Config *string
}
func (s *Service) Get(ctx context.Context, id uuid.UUID) (*Query, error) {
@@ -28,9 +28,13 @@ func (s *Service) Get(ctx context.Context, id uuid.UUID) (*Query, error) {
}
func ParseFullActiveQuery(q *repository.Fullactivequery) (*Query, error) {
reqQueryIDs := []uuid.UUID{}
if !(len(q.Requiredids) == 0 || len(q.Requiredids) == 1 && database.MustToUUID(q.Requiredids[0]) == uuid.Nil) {
reqQueryIDs = database.MustToUUIDArray(q.Requiredids)
var reqQueryIDs *[]uuid.UUID
if len(q.Requiredids) > 0 {
rQ, err := database.ToUUIDArray(q.Requiredids)
if err != nil {
return nil, err
}
reqQueryIDs = &rQ
}
qType, err := queryprocessor.ParseDBType(q.Type)
@@ -38,13 +42,19 @@ func ParseFullActiveQuery(q *repository.Fullactivequery) (*Query, error) {
return nil, err
}
var scfg *string
if q.Config != nil && string(q.Config) != "" {
s := string(q.Config)
scfg = &s
}
return &Query{
ID: database.MustToUUID(q.ID),
ActiveVersion: q.Activeversion,
LatestVersion: q.Latestversion,
Type: qType,
RequiredQueryIDs: reqQueryIDs,
Config: string(q.Config),
Config: scfg,
}, nil
}
+15 -28
View File
@@ -9,7 +9,6 @@ import (
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -34,13 +33,13 @@ func TestGet(t *testing.T) {
Type: queryprocessor.TypeJsonExtractor,
ActiveVersion: int32(1),
LatestVersion: int32(1),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
}
dbReqIDs := database.MustToDBUUIDArray(query.RequiredQueryIDs)
dbReqIDs := database.MustToDBUUIDArray(*query.RequiredQueryIDs)
pool.ExpectQuery("name: GetQuery :one").WithArgs(database.MustToDBUUID(query.ID)).WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
@@ -59,19 +58,15 @@ func TestFullActiveQueryEmpty(t *testing.T) {
Type: repository.QuerytypeContextFull,
Activeversion: int32(1),
Latestversion: int32(2),
Config: []byte(""),
Requiredids: []pgtype.UUID{},
}
out, err := query.ParseFullActiveQuery(dbQuery)
assert.Nil(t, err)
assert.EqualExportedValues(t, query.Query{
ID: database.MustToUUID(dbQuery.ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
Config: "",
RequiredQueryIDs: []uuid.UUID{},
ID: database.MustToUUID(dbQuery.ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
}, *out)
}
@@ -81,19 +76,15 @@ func TestFullActiveQueryWithNullUUID(t *testing.T) {
Type: repository.QuerytypeContextFull,
Activeversion: int32(1),
Latestversion: int32(2),
Config: []byte(""),
Requiredids: []pgtype.UUID{database.MustToDBUUID(uuid.Nil)},
}
out, err := query.ParseFullActiveQuery(dbQuery)
assert.Nil(t, err)
assert.EqualExportedValues(t, query.Query{
ID: database.MustToUUID(dbQuery.ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
Config: "",
RequiredQueryIDs: []uuid.UUID{},
ID: database.MustToUUID(dbQuery.ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
}, *out)
}
@@ -104,8 +95,6 @@ func TestFullActiveQueryArray(t *testing.T) {
Type: repository.QuerytypeContextFull,
Activeversion: int32(1),
Latestversion: int32(2),
Config: []byte(""),
Requiredids: []pgtype.UUID{},
},
}
@@ -113,12 +102,10 @@ func TestFullActiveQueryArray(t *testing.T) {
assert.Nil(t, err)
assert.EqualExportedValues(t, []*query.Query{
{
ID: database.MustToUUID(dbQueries[0].ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
Config: "",
RequiredQueryIDs: []uuid.UUID{},
ID: database.MustToUUID(dbQueries[0].ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: int32(1),
LatestVersion: int32(2),
},
}, out)
}
+6 -6
View File
@@ -33,13 +33,13 @@ func TestList(t *testing.T) {
Type: queryprocessor.TypeJsonExtractor,
ActiveVersion: int32(1),
LatestVersion: int32(1),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
}
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
dbReqIDs := database.MustToDBUUIDArray(*q.RequiredQueryIDs)
filters := query.ListFilters{}
@@ -74,13 +74,13 @@ func TestListFilterType(t *testing.T) {
Type: queryprocessor.TypeJsonExtractor,
ActiveVersion: int32(1),
LatestVersion: int32(1),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
}
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
dbReqIDs := database.MustToDBUUIDArray(*q.RequiredQueryIDs)
filters := query.ListFilters{}
+105
View File
@@ -0,0 +1,105 @@
package query
import (
"context"
"encoding/json"
"errors"
"fmt"
"queryorchestration/internal/database"
queryprocessor "queryorchestration/internal/query/processor"
"strings"
"github.com/google/uuid"
)
type Config interface {
GetConfig() *string
SetConfig(*string)
}
func (s *Service) normalizeConfig(config Config) error {
if config == nil || config.GetConfig() == nil {
return nil
}
trim := strings.TrimSpace(*config.GetConfig())
if trim == "" {
config.SetConfig(nil)
return nil
}
if !json.Valid([]byte(trim)) {
return errors.New("invalid config JSON")
}
var data map[string]interface{}
if err := json.Unmarshal([]byte(trim), &data); err != nil {
return fmt.Errorf("error unmarshalling JSON: %s", err)
}
prettyJSON, err := json.Marshal(data)
if err != nil {
return fmt.Errorf("error marshalling JSON: %s", err)
}
strJSON := string(prettyJSON)
if strJSON == "{}" {
config.SetConfig(nil)
} else {
config.SetConfig(&strJSON)
}
return nil
}
type RequiredQueryIDs interface {
GetRequiredQueryIDs() *[]uuid.UUID
SetRequiredQueryIDs(*[]uuid.UUID)
}
func (s *Service) normalizeQueryIDs(ctx context.Context, ids RequiredQueryIDs) error {
if ids == nil || ids.GetRequiredQueryIDs() == nil {
return nil
}
ide := ids.GetRequiredQueryIDs()
if len(*ide) == 0 {
ids.SetRequiredQueryIDs(nil)
return nil
}
dbids := database.MustToDBUUIDArray(*ide)
exist, err := s.db.Queries.AllQueriesExist(ctx, dbids)
if err != nil {
return err
} else if !exist {
return errors.New("not all required ids are present")
}
return nil
}
func (s *Service) normalizeActiveVersion(current *Query, entity *queryprocessor.Update) error {
if current == nil {
return errors.New("current query required")
}
if entity == nil || entity.ActiveVersion == nil {
return nil
}
if entity.ActiveVersion == &current.ActiveVersion {
entity.ActiveVersion = nil
return nil
}
if *entity.ActiveVersion < 1 || *entity.ActiveVersion > current.LatestVersion+1 {
return fmt.Errorf("active version must be in the range: 1 <= activeVersion <= %d", current.LatestVersion+1)
}
return nil
}
+170
View File
@@ -0,0 +1,170 @@
package query
import (
"context"
"errors"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
queryprocessor "queryorchestration/internal/query/processor"
"testing"
"github.com/google/uuid"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
func TestNormalizeConfig(t *testing.T) {
s := Service{}
err := s.normalizeConfig(nil)
assert.Nil(t, err)
entity := queryprocessor.Create{}
entity.Config = nil
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Nil(t, entity.Config)
cfg := ""
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Nil(t, entity.Config)
cfg = " "
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Nil(t, entity.Config)
cfg = "{}"
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Nil(t, entity.Config)
cfg = "{\"hello\":\"bye\"}"
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Equal(t, "{\"hello\":\"bye\"}", *(entity.Config))
cfg = " { \"hello\" : \"bye\" } "
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Nil(t, err)
assert.Equal(t, "{\"hello\":\"bye\"}", *(entity.Config))
cfg = "{'hello':'bye'}"
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Error(t, err)
cfg = "{\"hello\":\"}"
entity.Config = &cfg
err = s.normalizeConfig(&entity)
assert.Error(t, err)
}
func TestNormalizeQueryIDs(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
s := Service{db: db}
err = s.normalizeQueryIDs(ctx, nil)
assert.Nil(t, err)
entity := queryprocessor.Create{}
entity.RequiredQueryIDs = nil
err = s.normalizeQueryIDs(ctx, &entity)
assert.Nil(t, err)
assert.Nil(t, entity.RequiredQueryIDs)
entity.RequiredQueryIDs = &[]uuid.UUID{}
err = s.normalizeQueryIDs(ctx, &entity)
assert.Nil(t, err)
assert.Nil(t, entity.RequiredQueryIDs)
ids := []uuid.UUID{uuid.New()}
entity.RequiredQueryIDs = &ids
dbids := database.MustToDBUUIDArray(*entity.RequiredQueryIDs)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).WillReturnRows(
pgxmock.NewRows([]string{"all_exist"}).
AddRow(true),
)
err = s.normalizeQueryIDs(ctx, &entity)
assert.Nil(t, err)
assert.ElementsMatch(t, ids, *entity.RequiredQueryIDs)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).WillReturnRows(
pgxmock.NewRows([]string{"all_exist"}).
AddRow(false),
)
err = s.normalizeQueryIDs(ctx, &entity)
assert.Error(t, err)
assert.ElementsMatch(t, ids, *entity.RequiredQueryIDs)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).
WillReturnError(errors.New("database failure"))
err = s.normalizeQueryIDs(ctx, &entity)
assert.Error(t, err)
assert.ElementsMatch(t, ids, *entity.RequiredQueryIDs)
}
func TestNormalizeActiveVersion(t *testing.T) {
s := Service{}
err := s.normalizeActiveVersion(nil, nil)
assert.Error(t, err)
current := Query{
ActiveVersion: 2,
LatestVersion: 4,
}
entity := queryprocessor.Update{}
err = s.normalizeActiveVersion(nil, &entity)
assert.Error(t, err)
err = s.normalizeActiveVersion(&current, nil)
assert.Nil(t, err)
err = s.normalizeActiveVersion(&current, &entity)
assert.Nil(t, err)
entity.ActiveVersion = &current.ActiveVersion
err = s.normalizeActiveVersion(&current, &entity)
assert.Nil(t, err)
assert.Nil(t, entity.ActiveVersion)
aV := current.ActiveVersion + 1
entity.ActiveVersion = &aV
err = s.normalizeActiveVersion(&current, &entity)
assert.Nil(t, err)
assert.Equal(t, aV, *entity.ActiveVersion)
aV = current.LatestVersion + 2
entity.ActiveVersion = &aV
err = s.normalizeActiveVersion(&current, &entity)
assert.Error(t, err)
aV = 0
entity.ActiveVersion = &aV
err = s.normalizeActiveVersion(&current, &entity)
assert.Error(t, err)
}
+6 -4
View File
@@ -13,15 +13,16 @@ import (
)
func TestParseQuery(t *testing.T) {
cfg := "example"
q := &query.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
ActiveVersion: int32(1),
LatestVersion: int32(2),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: "example",
Config: &cfg,
}
out := query.ParseQuery(q)
@@ -48,14 +49,15 @@ func TestParseFullActiveQuery(t *testing.T) {
out, err := query.ParseFullActiveQuery(q)
assert.Nil(t, err)
bcfg := string(q.Config)
assert.EqualExportedValues(t, query.Query{
ID: database.MustToUUID(q.ID),
Type: queryprocessor.TypeContextFull,
ActiveVersion: q.Activeversion,
LatestVersion: q.Latestversion,
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
database.MustToUUID(q.Requiredids[0]),
},
Config: string(q.Config),
Config: &bcfg,
}, *out)
}
+7 -1
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"github.com/google/uuid"
)
func ParseDBNullType(qType repository.NullQuerytype) (Type, error) {
@@ -64,7 +66,11 @@ func ToDBNullQueryType(t Type) (repository.NullQuerytype, error) {
}
func ParseDBCollectorQuery(q *repository.GetCollectorQueriesRow) (*Query, error) {
reqQueryIDs := database.MustToUUIDArray(q.Requiredids)
var reqQueryIDs *[]uuid.UUID
if len(q.Requiredids) > 0 {
ids := database.MustToUUIDArray(q.Requiredids)
reqQueryIDs = &ids
}
qType, err := ParseDBNullType(q.Type)
if err != nil {
+1 -1
View File
@@ -22,7 +22,7 @@ func TestParseDBCollectorQuery(t *testing.T) {
value, err := queryprocessor.ParseDBCollectorQuery(&dbResult)
assert.Nil(t, err)
assert.Equal(t, uuid.Nil, value.ID)
assert.Equal(t, []uuid.UUID{}, value.RequiredQueryIDs)
assert.Nil(t, value.RequiredQueryIDs)
assert.Equal(t, int32(0), value.Version)
assert.Equal(t, queryprocessor.Type(queryprocessor.TypeJsonExtractor), value.Type)
+40 -7
View File
@@ -16,22 +16,55 @@ const (
type Create struct {
Type Type
RequiredQueryIDs []uuid.UUID
Config string
RequiredQueryIDs *[]uuid.UUID
Config *string
}
func (s *Create) GetConfig() *string {
return s.Config
}
func (s *Create) SetConfig(cfg *string) {
s.Config = cfg
}
func (s *Create) GetRequiredQueryIDs() *[]uuid.UUID {
return s.RequiredQueryIDs
}
func (s *Create) SetRequiredQueryIDs(ids *[]uuid.UUID) {
s.RequiredQueryIDs = ids
}
type Update struct {
ID uuid.UUID
RequiredQueryIDs []uuid.UUID
Config string
ActiveVersion *int32
RequiredQueryIDs *[]uuid.UUID
Config *string
}
func (s *Update) GetConfig() *string {
return s.Config
}
func (s *Update) SetConfig(cfg *string) {
s.Config = cfg
}
func (s *Update) GetRequiredQueryIDs() *[]uuid.UUID {
return s.RequiredQueryIDs
}
func (s *Update) SetRequiredQueryIDs(ids *[]uuid.UUID) {
s.RequiredQueryIDs = ids
}
type Query struct {
ID uuid.UUID
Type Type
Version int32
RequiredQueryIDs []uuid.UUID
Config string
RequiredQueryIDs *[]uuid.UUID
Config *string
}
type Creator interface {
@@ -43,5 +76,5 @@ type Updator interface {
}
type Processor interface {
Process(ctx context.Context, query *Query, values []result.Value) (string, error)
Process(ctx context.Context, query *Query, values *[]result.Value) (string, error)
}
+93
View File
@@ -0,0 +1,93 @@
package queryprocessor_test
import (
queryprocessor "queryorchestration/internal/query/processor"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestCreateGetConfig(t *testing.T) {
entity := queryprocessor.Create{}
assert.Nil(t, entity.GetConfig())
cfg := "example_config"
entity.Config = &cfg
assert.NotNil(t, entity.GetConfig())
assert.Equal(t, cfg, *entity.GetConfig())
}
func TestCreateSetConfig(t *testing.T) {
entity := queryprocessor.Create{}
assert.Nil(t, entity.Config)
cfg := "example_config"
entity.SetConfig(&cfg)
assert.Equal(t, cfg, *entity.Config)
}
func TestUpdateGetConfig(t *testing.T) {
entity := queryprocessor.Update{}
assert.Nil(t, entity.GetConfig())
cfg := "example_config"
entity.Config = &cfg
assert.NotNil(t, entity.GetConfig())
assert.Equal(t, cfg, *entity.GetConfig())
}
func TestUpdateSetConfig(t *testing.T) {
entity := queryprocessor.Update{}
assert.Nil(t, entity.Config)
cfg := "example_config"
entity.SetConfig(&cfg)
assert.Equal(t, cfg, *entity.Config)
}
func TestCreateGetRequiredQueryIDs(t *testing.T) {
entity := queryprocessor.Create{}
assert.Nil(t, entity.GetRequiredQueryIDs())
ids := []uuid.UUID{uuid.New()}
entity.RequiredQueryIDs = &ids
assert.NotNil(t, entity.GetRequiredQueryIDs())
assert.Equal(t, ids, *entity.GetRequiredQueryIDs())
}
func TestCreateSetRequiredQueryIDs(t *testing.T) {
entity := queryprocessor.Create{}
assert.Nil(t, entity.RequiredQueryIDs)
ids := []uuid.UUID{uuid.New()}
entity.SetRequiredQueryIDs(&ids)
assert.Equal(t, ids, *entity.RequiredQueryIDs)
}
func TestUpdateGetRequiredQueryIDs(t *testing.T) {
entity := queryprocessor.Update{}
assert.Nil(t, entity.GetRequiredQueryIDs())
ids := []uuid.UUID{uuid.New()}
entity.RequiredQueryIDs = &ids
assert.NotNil(t, entity.GetRequiredQueryIDs())
assert.Equal(t, ids, *entity.GetRequiredQueryIDs())
}
func TestUpdateSetRequiredQueryIDs(t *testing.T) {
entity := queryprocessor.Update{}
assert.Nil(t, entity.RequiredQueryIDs)
ids := []uuid.UUID{uuid.New()}
entity.SetRequiredQueryIDs(&ids)
assert.Equal(t, ids, *entity.RequiredQueryIDs)
}
+12 -8
View File
@@ -64,20 +64,24 @@ func (q *Queue) Add(qu *queryprocessor.Query) {
if entry.ID == qu.ID {
return
}
for _, id := range qu.RequiredQueryIDs {
if entry.ID == id {
requiredIndex = index
break
if qu.RequiredQueryIDs != nil {
for _, id := range *qu.RequiredQueryIDs {
if entry.ID == id {
requiredIndex = index
break
}
}
}
}
}
for _, entry := range q.collectorQueries {
for _, id := range entry.RequiredQueryIDs {
if qu.ID == id {
dependentQueries = append(dependentQueries, entry)
break
if entry.RequiredQueryIDs != nil {
for _, id := range *entry.RequiredQueryIDs {
if qu.ID == id {
dependentQueries = append(dependentQueries, entry)
break
}
}
}
}
+6 -2
View File
@@ -10,6 +10,7 @@ import (
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -60,13 +61,16 @@ func TestGetCollectorQueries(t *testing.T) {
dbCollectorID := database.MustToDBUUID(svc.collector.ID)
collectorQueries := []*queryprocessor.Query{
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: int32(1)},
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, Version: int32(1)},
}
rows := pgxmock.NewRows([]string{"collectorId", "queryId", "type", "queryVersion", "requiredIds"})
for _, q := range collectorQueries {
dbID := database.MustToDBUUID(q.ID)
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
dbReqIDs := []pgtype.UUID{}
if q.RequiredQueryIDs != nil {
dbReqIDs = database.MustToDBUUIDArray(*q.RequiredQueryIDs)
}
ty, err := queryprocessor.ToDBNullQueryType(q.Type)
assert.Nil(t, err)
rows = rows.
+45 -30
View File
@@ -6,6 +6,7 @@ import (
queryprocessor "queryorchestration/internal/query/processor"
"queryorchestration/internal/query/result"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
@@ -27,43 +28,57 @@ func (q *Queue) Execute(ctx context.Context) error {
}
func (q *Queue) executeQuery(ctx context.Context, qu *queryprocessor.Query) error {
resultIDs := make([]pgtype.UUID, len(qu.RequiredQueryIDs))
for index, id := range qu.RequiredQueryIDs {
var queryVersion int32
for _, entry := range q.collectorQueries {
if entry.ID == id {
queryVersion = entry.Version
break
}
}
for _, entry := range q.results {
if entry.QueryID == id && entry.QueryVersion == queryVersion {
resultIDs[index] = database.MustToDBUUID(entry.ID)
break
}
}
}
values, err := q.db.Queries.ListResultValuesByID(ctx, resultIDs)
values, err := q.getRequiredResults(ctx, qu.RequiredQueryIDs)
if err != nil {
return err
}
cleanValues := make([]result.Value, len(values))
for index, r := range values {
cleanValue, err := q.getResultValue(r)
if err != nil {
return err
}
cleanValues[index] = cleanValue
}
err = q.setResult(ctx, qu, cleanValues)
err = q.setResult(ctx, qu, values)
if err != nil {
return err
}
return nil
}
func (q *Queue) getRequiredResults(ctx context.Context, requiredQueryIDs *[]uuid.UUID) (*[]result.Value, error) {
var values *[]result.Value
if requiredQueryIDs != nil {
resultIDs := make([]pgtype.UUID, len(*requiredQueryIDs))
for index, id := range *requiredQueryIDs {
var queryVersion int32
for _, entry := range q.collectorQueries {
if entry.ID == id {
queryVersion = entry.Version
break
}
}
for _, entry := range q.results {
if entry.QueryID == id && entry.QueryVersion == queryVersion {
resultIDs[index] = database.MustToDBUUID(entry.ID)
break
}
}
}
resultValues, err := q.db.Queries.ListResultValuesByID(ctx, resultIDs)
if err != nil {
return nil, err
}
rValues := make([]result.Value, len(resultValues))
for index, r := range resultValues {
cleanValue, err := q.getResultValue(r)
if err != nil {
return nil, err
}
rValues[index] = cleanValue
}
values = &rValues
}
return values, nil
}
+16 -13
View File
@@ -58,19 +58,22 @@ func TestExecute(t *testing.T) {
contextID := uuid.New()
contextVersion := int32(1)
collectorQueries := []queryprocessor.Query{
{ID: contextID, Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: contextVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryFourID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryFourVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: querySixVersion},
{ID: contextID, Type: queryprocessor.TypeContextFull, Version: contextVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryFourID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryFourVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: querySixVersion},
}
rows := pgxmock.NewRows([]string{"collectorId", "queryId", "type", "queryVersion", "requiredIds"})
for _, q := range collectorQueries {
dbID := database.MustToDBUUID(q.ID)
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
dbReqIDs := []pgtype.UUID{}
if q.RequiredQueryIDs != nil {
dbReqIDs = database.MustToDBUUIDArray(*q.RequiredQueryIDs)
}
ty, err := queryprocessor.ToDBNullQueryType(q.Type)
assert.Nil(t, err)
rows = rows.
@@ -88,11 +91,11 @@ func TestExecute(t *testing.T) {
}
expectedQueries := []*queryprocessor.Query{
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: querySixVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: querySixVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryTwoVersion},
}
docID := uuid.New()
+3 -12
View File
@@ -27,8 +27,8 @@ func TestExecute(t *testing.T) {
}
expectedQueries := []*queryprocessor.Query{
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: int32(1)},
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: int32(2)},
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, Version: int32(1)},
{ID: uuid.New(), Type: queryprocessor.TypeContextFull, Version: int32(2)},
}
q := &Queue{
@@ -39,17 +39,11 @@ func TestExecute(t *testing.T) {
textVersion: int32(2),
}
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{}).WillReturnRows(
pgxmock.NewRows([]string{"id", "queryId", "value"}),
)
pool.ExpectQuery("name: SetResult :one").WithArgs(database.MustToDBUUID(expectedQueries[0].ID), database.MustToDBUUID(q.documentId), pgxmock.AnyArg(), q.cleanVersion, q.textVersion, expectedQueries[0].Version).
WillReturnRows(
pgxmock.NewRows([]string{"id"}).AddRow(pgtype.UUID{}),
)
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{}).WillReturnRows(
pgxmock.NewRows([]string{"id", "queryId", "value"}),
)
pool.ExpectQuery("name: SetResult :one").WithArgs(database.MustToDBUUID(expectedQueries[1].ID), database.MustToDBUUID(q.documentId), pgxmock.AnyArg(), q.cleanVersion, q.textVersion, expectedQueries[1].Version).
WillReturnRows(
pgxmock.NewRows([]string{"id"}).AddRow(pgtype.UUID{}),
@@ -72,7 +66,7 @@ func TestExecuteQuery(t *testing.T) {
Pool: pool,
}
qu := &queryprocessor.Query{ID: uuid.New(), Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: int32(1)}
qu := &queryprocessor.Query{ID: uuid.New(), Type: queryprocessor.TypeContextFull, Version: int32(1)}
q := &Queue{
db: db,
@@ -81,9 +75,6 @@ func TestExecuteQuery(t *testing.T) {
textVersion: int32(2),
}
pool.ExpectQuery("name: ListResultValuesByID :many").WithArgs([]pgtype.UUID{}).WillReturnRows(
pgxmock.NewRows([]string{"id", "queryId", "value"}),
)
pool.ExpectQuery("name: SetResult :one").WithArgs(database.MustToDBUUID(qu.ID), database.MustToDBUUID(q.documentId), pgxmock.AnyArg(), q.cleanVersion, q.textVersion, qu.Version).
WillReturnRows(
pgxmock.NewRows([]string{"id"}).AddRow(pgtype.UUID{}),
+1 -1
View File
@@ -11,7 +11,7 @@ import (
jsonextractor "queryorchestration/internal/query/types/jsonExtractor"
)
func (q *Queue) setResult(ctx context.Context, qu *queryprocessor.Query, resultValues []result.Value) error {
func (q *Queue) setResult(ctx context.Context, qu *queryprocessor.Query, resultValues *[]result.Value) error {
processor, err := q.getProcessor(qu.Type)
if err != nil {
return err
+2 -2
View File
@@ -34,7 +34,7 @@ func TestSetResult(t *testing.T) {
textVersion: int32(2),
}
qu := &queryprocessor.Query{ID: uuid.New(), Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: int32(1)}
qu := &queryprocessor.Query{ID: uuid.New(), Type: queryprocessor.TypeContextFull, Version: int32(1)}
resultValues := []result.Value{}
pool.ExpectQuery("name: SetResult :one").WithArgs(database.MustToDBUUID(qu.ID), database.MustToDBUUID(q.documentId), pgxmock.AnyArg(), q.cleanVersion, q.textVersion, qu.Version).
@@ -42,7 +42,7 @@ func TestSetResult(t *testing.T) {
pgxmock.NewRows([]string{"id"}).AddRow(pgtype.UUID{}),
)
err = q.setResult(ctx, qu, resultValues)
err = q.setResult(ctx, qu, &resultValues)
assert.Nil(t, err)
}
+17 -13
View File
@@ -12,6 +12,7 @@ import (
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -57,19 +58,22 @@ func TestService(t *testing.T) {
contextID := uuid.New()
contextVersion := int32(1)
collectorQueries := []queryprocessor.Query{
{ID: contextID, Type: queryprocessor.TypeContextFull, RequiredQueryIDs: []uuid.UUID{}, Version: contextVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryFourID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryFourVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: querySixVersion},
{ID: contextID, Type: queryprocessor.TypeContextFull, Version: contextVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryFourID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryFourVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: querySixVersion},
}
rows := pgxmock.NewRows([]string{"collectorId", "queryId", "type", "queryVersion", "requiredIds"})
for _, q := range collectorQueries {
dbID := database.MustToDBUUID(q.ID)
dbReqIDs := database.MustToDBUUIDArray(q.RequiredQueryIDs)
dbReqIDs := []pgtype.UUID{}
if q.RequiredQueryIDs != nil {
dbReqIDs = database.MustToDBUUIDArray(*q.RequiredQueryIDs)
}
ty, err := queryprocessor.ToDBNullQueryType(q.Type)
assert.Nil(t, err)
rows = rows.
@@ -87,11 +91,11 @@ func TestService(t *testing.T) {
}
expectedQueries := []*queryprocessor.Query{
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: querySixVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: []uuid.UUID{queryOneID}, Version: queryTwoVersion},
{ID: querySixID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: querySixVersion},
{ID: queryFiveID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{querySixID}, Version: queryFiveVersion},
{ID: queryOneID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{contextID}, Version: queryOneVersion},
{ID: queryThreeID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryThreeVersion},
{ID: queryTwoID, Type: queryprocessor.TypeJsonExtractor, RequiredQueryIDs: &[]uuid.UUID{queryOneID}, Version: queryTwoVersion},
}
docID := uuid.New()
@@ -8,7 +8,6 @@ import (
contextfull "queryorchestration/internal/query/types/contextFull"
"testing"
"github.com/google/uuid"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -30,9 +29,7 @@ func TestCreatorValidate(t *testing.T) {
assert.NotNil(t, svc)
entity := &queryprocessor.Create{
Type: queryprocessor.TypeContextFull,
RequiredQueryIDs: []uuid.UUID{},
Config: "",
Type: queryprocessor.TypeContextFull,
}
err = svc.Validate(ctx, entity)
@@ -17,15 +17,14 @@ func TestContextFull(t *testing.T) {
extractor := contextfull.NewExtractor()
query := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{},
Version: int32(1),
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
}
values := []result.Value{}
value, err := extractor.Process(ctx, query, values)
value, err := extractor.Process(ctx, query, &values)
assert.Nil(t, err)
assert.Equal(t, "", value)
@@ -33,6 +32,6 @@ func TestContextFull(t *testing.T) {
contextfull.NewResult("example_result"),
}
_, err = extractor.Process(ctx, query, values)
_, err = extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "no requirements expected")
}
+2 -2
View File
@@ -14,8 +14,8 @@ func NewExtractor() Extractor {
return Extractor{}
}
func (e Extractor) Process(ctx context.Context, query *queryprocessor.Query, values []result.Value) (string, error) {
if len(values) > 0 {
func (e Extractor) Process(ctx context.Context, query *queryprocessor.Query, values *[]result.Value) (string, error) {
if values != nil && len(*values) > 0 {
return "", errors.New("no requirements expected")
}
// TODO
@@ -30,17 +30,13 @@ func TestUpdatorValidate(t *testing.T) {
assert.NotNil(t, svc)
current := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeContextFull,
Version: int32(1),
RequiredQueryIDs: []uuid.UUID{},
Config: "",
ID: uuid.New(),
Type: queryprocessor.TypeContextFull,
Version: int32(1),
}
entity := &queryprocessor.Update{
ID: current.ID,
RequiredQueryIDs: []uuid.UUID{},
Config: "",
ID: current.ID,
}
err = svc.Validate(ctx, current, entity)
@@ -8,7 +8,6 @@ import (
jsonextractor "queryorchestration/internal/query/types/jsonExtractor"
"testing"
"github.com/google/uuid"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -30,9 +29,7 @@ func TestCreatorValidate(t *testing.T) {
assert.NotNil(t, svc)
entity := &queryprocessor.Create{
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{},
Config: "",
Type: queryprocessor.TypeJsonExtractor,
}
err = svc.Validate(ctx, entity)
@@ -33,10 +33,9 @@ func TestJSONProcess(t *testing.T) {
extractor := jsonextractor.NewExtractor(db)
query := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{},
Version: int32(1),
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
}
entryValue := "value"
@@ -53,7 +52,7 @@ func TestJSONProcess(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err := extractor.Process(ctx, query, values)
value, err := extractor.Process(ctx, query, &values)
assert.Nil(t, err)
assert.Equal(t, entryValue, value)
@@ -68,7 +67,7 @@ func TestJSONProcess(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.Nil(t, err)
assert.Equal(t, entryValue, value)
@@ -83,7 +82,7 @@ func TestJSONProcess(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.Nil(t, err)
assert.Equal(t, entryValue, value)
}
@@ -104,10 +103,9 @@ func TestJSONProcessJSON(t *testing.T) {
extractor := jsonextractor.NewExtractor(db)
query := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{},
Version: int32(1),
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
}
entryValue := "value"
@@ -124,7 +122,7 @@ func TestJSONProcessJSON(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err := extractor.Process(ctx, query, values)
value, err := extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "JSON path does not exist: invalid_key")
assert.Empty(t, value)
@@ -136,7 +134,7 @@ func TestJSONProcessJSON(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "unexpected end of JSON input")
assert.Empty(t, value)
@@ -148,7 +146,7 @@ func TestJSONProcessJSON(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "JSON path does not exist: ")
assert.Empty(t, value)
@@ -160,7 +158,7 @@ func TestJSONProcessJSON(t *testing.T) {
AddRow(pgtype.UUID{}, []byte(config)),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "invalid character '}' looking for beginning of value")
assert.Empty(t, value)
@@ -169,7 +167,7 @@ func TestJSONProcessJSON(t *testing.T) {
pgxmock.NewRows([]string{"id", "config"}),
)
value, err = extractor.Process(ctx, query, values)
value, err = extractor.Process(ctx, query, &values)
assert.EqualError(t, err, "no rows in result set")
assert.Empty(t, value)
}
@@ -190,14 +188,13 @@ func TestJSONProcessResults(t *testing.T) {
extractor := jsonextractor.NewExtractor(db)
query := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
RequiredQueryIDs: []uuid.UUID{},
Version: int32(1),
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
}
results := []result.Value{}
value, err := extractor.Process(ctx, query, results)
value, err := extractor.Process(ctx, query, &results)
assert.EqualError(t, err, "JSON Extraction requires 1 result")
assert.Empty(t, value)
@@ -205,7 +202,7 @@ func TestJSONProcessResults(t *testing.T) {
contextfull.NewResult(""),
contextfull.NewResult(""),
}
value, err = extractor.Process(ctx, query, results)
value, err = extractor.Process(ctx, query, &results)
assert.EqualError(t, err, "JSON Extraction requires 1 result")
assert.Empty(t, value)
@@ -214,7 +211,7 @@ func TestJSONProcessResults(t *testing.T) {
contextfull.NewResult(""),
contextfull.NewResult(""),
}
value, err = extractor.Process(ctx, query, results)
value, err = extractor.Process(ctx, query, &results)
assert.EqualError(t, err, "JSON Extraction requires 1 result")
assert.Empty(t, value)
}
@@ -24,12 +24,12 @@ func NewExtractor(db *database.Connection) Extractor {
return Extractor{db}
}
func (e Extractor) Process(ctx context.Context, query *queryprocessor.Query, values []result.Value) (string, error) {
if len(values) != 1 {
func (e Extractor) Process(ctx context.Context, query *queryprocessor.Query, values *[]result.Value) (string, error) {
if values == nil || len(*values) != 1 {
return "", fmt.Errorf("JSON Extraction requires 1 result")
}
value, err := values[0].GetValue(ctx)
value, err := (*values)[0].GetValue(ctx)
if err != nil {
return "", err
}
@@ -30,17 +30,13 @@ func TestUpdatorValidate(t *testing.T) {
assert.NotNil(t, svc)
current := &queryprocessor.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
RequiredQueryIDs: []uuid.UUID{},
Config: "",
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
Version: int32(1),
}
entity := &queryprocessor.Update{
ID: current.ID,
RequiredQueryIDs: []uuid.UUID{},
Config: "",
ID: current.ID,
}
err = svc.Validate(ctx, current, entity)
+143 -5
View File
@@ -3,9 +3,13 @@ package query
import (
"context"
"fmt"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
queryprocessor "queryorchestration/internal/query/processor"
contextfull "queryorchestration/internal/query/types/contextFull"
jsonextractor "queryorchestration/internal/query/types/jsonExtractor"
"github.com/google/uuid"
)
func (s *Service) Update(ctx context.Context, entity *queryprocessor.Update) error {
@@ -14,6 +18,35 @@ func (s *Service) Update(ctx context.Context, entity *queryprocessor.Update) err
return err
}
err = s.normalizeUpdate(ctx, current, entity)
if err != nil {
return err
}
err = s.submitUpdate(ctx, current, entity)
if err != nil {
return err
}
return nil
}
func (s *Service) normalizeUpdate(ctx context.Context, current *Query, entity *queryprocessor.Update) error {
err := s.normalizeActiveVersion(current, entity)
if err != nil {
return err
}
err = s.normalizeQueryIDs(ctx, entity)
if err != nil {
return err
}
err = s.normalizeConfig(entity)
if err != nil {
return err
}
validator, err := s.getUpdator(current.Type)
if err != nil {
return err
@@ -24,18 +57,123 @@ func (s *Service) Update(ctx context.Context, entity *queryprocessor.Update) err
return err
}
err = s.submitUpdate(ctx, entity)
return nil
}
func (s *Service) submitUpdate(ctx context.Context, current *Query, entity *queryprocessor.Update) error {
tx, err := s.db.Pool.Begin(ctx)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback(ctx)
}()
qtx := s.db.Queries.WithTx(tx)
latestVersion := current.LatestVersion + 1
id := database.MustToDBUUID(entity.ID)
hasChanges := false
addIDs := getSetDifference(entity.RequiredQueryIDs, current.RequiredQueryIDs)
for _, qID := range addIDs {
err = qtx.AddRequiredQuery(ctx, &repository.AddRequiredQueryParams{
Queryid: id,
Requiredqueryid: database.MustToDBUUID(qID),
Addedversion: latestVersion,
})
if err != nil {
return err
}
hasChanges = true
}
removeIDs := getSetDifference(current.RequiredQueryIDs, entity.RequiredQueryIDs)
for _, qID := range removeIDs {
err = qtx.RemoveRequiredQuery(ctx, &repository.RemoveRequiredQueryParams{
Queryid: id,
Requiredqueryid: database.MustToDBUUID(qID),
Removedversion: &latestVersion,
})
if err != nil {
return err
}
hasChanges = true
}
if entity.Config != nil && *entity.Config != "" {
err = qtx.RemoveQueryConfig(ctx, &repository.RemoveQueryConfigParams{
Queryid: id,
Removedversion: &latestVersion,
})
if err != nil {
return err
}
err = qtx.AddQueryConfig(ctx, &repository.AddQueryConfigParams{
Queryid: id,
Config: []byte(*entity.Config),
Addedversion: latestVersion,
})
if err != nil {
return err
}
hasChanges = true
}
activeVersion := current.ActiveVersion
if entity.ActiveVersion != nil {
activeVersion = *entity.ActiveVersion
}
if activeVersion != current.ActiveVersion {
hasChanges = true
}
if hasChanges {
err = qtx.UpdateQuery(ctx, &repository.UpdateQueryParams{
Latestversion: latestVersion,
Activeversion: activeVersion,
ID: id,
})
if err != nil {
return err
}
err = tx.Commit(ctx)
if err != nil {
return err
}
}
return nil
}
func (s *Service) submitUpdate(ctx context.Context, entity *queryprocessor.Update) error {
// TODO - generate new entity
// TODO - submit update - id, type, activeversion, requiredQueryId, Config
return nil
func getSetDifference(setA *[]uuid.UUID, setB *[]uuid.UUID) []uuid.UUID {
if setA == nil {
return []uuid.UUID{}
} else if setB == nil {
return *setA
}
diff := []uuid.UUID{}
for _, q := range *setA {
isFound := false
for _, eq := range *setB {
if q == eq {
isFound = true
break
}
}
if !isFound {
diff = append(diff, q)
}
}
return diff
}
func (s *Service) getUpdator(qType queryprocessor.Type) (queryprocessor.Updator, error) {
+8 -12
View File
@@ -9,6 +9,8 @@ import (
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -30,29 +32,23 @@ func TestUpdate(t *testing.T) {
config := "{\"path\":\"example_path\"}"
existing := query.Query{
ID: uuid.New(),
Type: queryprocessor.TypeJsonExtractor,
ActiveVersion: int32(1),
LatestVersion: int32(1),
RequiredQueryIDs: []uuid.UUID{
uuid.New(),
},
Config: config,
}
update := &queryprocessor.Update{
ID: existing.ID,
RequiredQueryIDs: []uuid.UUID{
uuid.New(),
},
Config: config,
}
dbReqIDs := database.MustToDBUUIDArray(existing.RequiredQueryIDs)
pool.ExpectQuery("name: GetQuery :one").WithArgs(database.MustToDBUUID(update.ID)).WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
AddRow(database.MustToDBUUID(existing.ID), repository.QuerytypeJsonExtractor, existing.ActiveVersion, existing.LatestVersion, []byte(config), dbReqIDs),
AddRow(database.MustToDBUUID(existing.ID), repository.QuerytypeJsonExtractor, existing.ActiveVersion, existing.LatestVersion, []byte(config), []pgtype.UUID{}),
)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectExec("name: UpdateQuery :exec").WithArgs(int32(1), int32(2), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = svc.Update(ctx, update)
assert.Nil(t, err)
}
+237 -6
View File
@@ -2,12 +2,14 @@ package query
import (
"context"
"errors"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
queryprocessor "queryorchestration/internal/query/processor"
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
)
@@ -56,17 +58,246 @@ func TestSubmitUpdate(t *testing.T) {
config := "{\"path\":\"example_path\"}"
q := Query{
ID: uuid.New(),
RequiredQueryIDs: []uuid.UUID{
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: config,
Config: &config,
ActiveVersion: int32(1),
LatestVersion: int32(2),
}
aV := int32(10)
update := &queryprocessor.Update{
ID: q.ID,
RequiredQueryIDs: q.RequiredQueryIDs,
Config: q.Config,
ID: q.ID,
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: q.Config,
ActiveVersion: &aV,
}
err = svc.submitUpdate(ctx, update)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[0]), pgxmock.AnyArg()).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[0]), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: RemoveQueryConfig :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: AddQueryConfig :exec").WithArgs(database.MustToDBUUID(update.ID), []byte(*update.Config), int32(3)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = svc.submitUpdate(ctx, &q, update)
assert.Nil(t, err)
}
func TestSubmitUpdateRollback(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
q := Query{
ID: uuid.New(),
ActiveVersion: int32(1),
LatestVersion: int32(2),
}
aV := int32(10)
update := &queryprocessor.Update{
ID: q.ID,
ActiveVersion: &aV,
}
pool.ExpectBeginTx(pgx.TxOptions{})
msg := "database failure"
pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)).
WillReturnError(errors.New(msg))
pool.ExpectCommit()
err = svc.submitUpdate(ctx, &q, update)
assert.EqualError(t, err, msg)
}
func TestSubmitUpdateRequiredQueries(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
config := "{\"path\":\"example_path\"}"
q := Query{
ID: uuid.New(),
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
uuid.New(),
uuid.New(),
},
Config: &config,
ActiveVersion: int32(1),
LatestVersion: int32(2),
}
update := &queryprocessor.Update{
ID: q.ID,
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
uuid.New(),
(*q.RequiredQueryIDs)[0],
},
}
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[0]), pgxmock.AnyArg()).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[1]), pgxmock.AnyArg()).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[1]), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[2]), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: UpdateQuery :exec").WithArgs(q.ActiveVersion, int32(3), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = svc.submitUpdate(ctx, &q, update)
assert.Nil(t, err)
}
func TestSubmitUpdateActiveVersion(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
q := Query{
ID: uuid.New(),
ActiveVersion: int32(1),
LatestVersion: int32(2),
}
aV := int32(10)
update := &queryprocessor.Update{
ID: q.ID,
ActiveVersion: &aV,
}
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = svc.submitUpdate(ctx, &q, update)
assert.Nil(t, err)
}
func TestSubmitUpdateNoChange(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
q := Query{
ID: uuid.New(),
ActiveVersion: int32(1),
LatestVersion: int32(2),
}
update := &queryprocessor.Update{
ID: q.ID,
}
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectRollback()
err = svc.submitUpdate(ctx, &q, update)
assert.Nil(t, err)
}
func TestGetSetDifference(t *testing.T) {
commonUUID := uuid.New()
listA := []uuid.UUID{uuid.New(), commonUUID}
listB := []uuid.UUID{uuid.New(), commonUUID}
assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(nil, nil))
assert.ElementsMatch(t, listA, getSetDifference(&listA, nil))
assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(nil, &listB))
assert.ElementsMatch(t, []uuid.UUID{listA[0]}, getSetDifference(&listA, &listB))
assert.ElementsMatch(t, []uuid.UUID{listB[0]}, getSetDifference(&listB, &listA))
assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(&listA, &listA))
}
func TestNormalizeUpdate(t *testing.T) {
ctx := context.Background()
pool, err := pgxmock.NewPool()
if err != nil {
t.Fatalf("failed to open pgxmock database: %v", err)
}
queries := repository.New(pool)
db := &database.Connection{
Queries: queries,
Pool: pool,
}
svc := New(db)
current := &Query{
ID: uuid.New(),
ActiveVersion: int32(1),
LatestVersion: int32(2),
Type: queryprocessor.TypeJsonExtractor,
}
cfg := "{}"
aV := int32(2)
update := &queryprocessor.Update{
ID: current.ID,
Config: &cfg,
ActiveVersion: &aV,
RequiredQueryIDs: &[]uuid.UUID{},
}
dbids := database.MustToDBUUIDArray(*update.RequiredQueryIDs)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).WillReturnRows(
pgxmock.NewRows([]string{"all_exist"}).
AddRow(true),
)
err = svc.normalizeUpdate(ctx, current, update)
assert.Nil(t, err)
assert.EqualExportedValues(t, queryprocessor.Update{
ID: current.ID,
ActiveVersion: &aV,
Config: nil,
RequiredQueryIDs: nil,
}, *update)
}