feat: project view (#90)

* init for views (spooler, handler)

* init for views (spooler, handler)

* start view in management

* granted project

* implement granted project view

* search granted projects

* fix search column

* update all projects on project change

* search roles

* filter org

* project members

* project grant members

* fix tests

* application view

* project grant search

* mock

* test appendevents

* test appendevents

* Update internal/view/query.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/eventstore/spooler/spooler.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/view/query.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* merge request changes

* Update internal/project/repository/view/model/application.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* merge request changes

* Project view sql (#92)

* sql and configs

* error handling

* sql start in eventstore

* on error handling, config

* read user on members

* Update internal/project/repository/view/application_view.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/application_query.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_grant_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_grant_member_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_grant_member_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_member_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_member_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/granted_project.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* return caos errors

* Update internal/project/repository/view/model/granted_project_query.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_grant_member.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_grant_member_query.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_member.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_member_query.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_role.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/project/repository/view/model/project_role_query.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/application_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/application_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update pkg/management/api/grpc/project_converter.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* converter fix

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
Fabi
2020-05-11 12:16:29 +02:00
committed by GitHub
parent 49d86fdabb
commit 6e105f662e
92 changed files with 8354 additions and 3423 deletions

View File

@@ -1,9 +1,25 @@
package view
import (
"database/sql"
"github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/errors"
"github.com/jinzhu/gorm"
)
type ViewConfig struct {
SQL *types.SQL
}
func Start(conf ViewConfig) (*sql.DB, *gorm.DB, error) {
sqlClient, err := sql.Open("postgres", conf.SQL.ConnectionString())
if err != nil {
return nil, nil, errors.ThrowPreconditionFailed(err, "SQL-9qBtr", "unable to open database connection")
}
client, err := gorm.Open("postgres", sqlClient)
if err != nil {
return nil, nil, err
}
return sqlClient, client, nil
}

View File

@@ -14,6 +14,8 @@ var (
expectedGetByQueryCaseSensitive = `SELECT \* FROM "%s" WHERE \(%s %s \$1\) LIMIT 1`
expectedSave = `UPDATE "%s" SET "test" = \$1 WHERE "%s"."%s" = \$2`
expectedRemove = `DELETE FROM "%s" WHERE \(%s = \$1\)`
expectedRemoveByObject = `DELETE FROM "%s" WHERE "%s"."%s" = \$1`
expectedRemoveByObjectMultiplePK = `DELETE FROM "%s" WHERE "%s"."%s" = \$1 AND "%s"."%s" = \$2`
expectedSearch = `SELECT \* FROM "%s" OFFSET 0`
expectedSearchCount = `SELECT count\(\*\) FROM "%s"`
expectedSearchLimit = `SELECT \* FROM "%s" LIMIT %v OFFSET 0`
@@ -94,10 +96,16 @@ func (key TestSearchKey) ToColumnName() string {
}
type Test struct {
ID string `json:"-" gorm:"column:id;primary_key"`
ID string `json:"-" gorm:"column:primary_id;primary_key"`
Test string `json:"test" gorm:"column:test"`
}
type TestMultiplePK struct {
TestID string `gorm:"column:testId;primary_key"`
HodorID string `gorm:"column:hodorId;primary_key"`
Test string `gorm:"column:test"`
}
type dbMock struct {
db *gorm.DB
mock sqlmock.Sqlmock
@@ -201,7 +209,7 @@ func (db *dbMock) expectGetByQueryErr(table, key, method, value string, err erro
}
func (db *dbMock) expectSave(table string, object Test) *dbMock {
query := fmt.Sprintf(expectedSave, table, table, "id")
query := fmt.Sprintf(expectedSave, table, table, "primary_id")
db.mock.ExpectExec(query).
WithArgs(object.Test, object.ID).
WillReturnResult(sqlmock.NewResult(1, 1))
@@ -227,6 +235,24 @@ func (db *dbMock) expectRemove(table, key, value string) *dbMock {
return db
}
func (db *dbMock) expectRemoveByObject(table string, object Test) *dbMock {
query := fmt.Sprintf(expectedRemoveByObject, table, table, "primary_id")
db.mock.ExpectExec(query).
WithArgs(object.ID).
WillReturnResult(sqlmock.NewResult(1, 1))
return db
}
func (db *dbMock) expectRemoveByObjectMultiplePKs(table string, object TestMultiplePK) *dbMock {
query := fmt.Sprintf(expectedRemoveByObjectMultiplePK, table, table, "testId", table, "hodorId")
db.mock.ExpectExec(query).
WithArgs(object.TestID, object.HodorID).
WillReturnResult(sqlmock.NewResult(1, 1))
return db
}
func (db *dbMock) expectRemoveErr(table, key, value string, err error) *dbMock {
query := fmt.Sprintf(expectedRemove, table, key)
db.mock.ExpectExec(query).

View File

@@ -12,10 +12,10 @@ const (
)
type FailedEvent struct {
ViewName string `gorm:"column:view_name;primary_key"`
FailedSequnce uint64 `gorm:"column:failed_sequence;primary_key`
FailureCount uint64 `gorm:"column:failure_count`
ErrMsg uint64 `gorm:"column:err_msg`
ViewName string `gorm:"column:view_name;primary_key"`
FailedSequence uint64 `gorm:"column:failed_sequence;primary_key`
FailureCount uint64 `gorm:"column:failure_count`
ErrMsg string `gorm:"column:err_msg`
}
type FailedEventSearchQuery struct {
@@ -71,18 +71,18 @@ func LatestFailedEvent(db *gorm.DB, table, viewName string, sequence uint64) (*F
failedEvent := new(FailedEvent)
queries := []SearchQuery{
FailedEventSearchQuery{Key: FAILEDEVENTKEY_VIEW_NAME, Method: model.SEARCHMETHOD_EQUALS_IGNORE_CASE, Value: viewName},
FailedEventSearchQuery{Key: FAILEDEVENTKEY_FAILED_SEQUENCE, Method: model.SEARCHMETHOD_EQUALS_IGNORE_CASE, Value: sequence},
FailedEventSearchQuery{Key: FAILEDEVENTKEY_FAILED_SEQUENCE, Method: model.SEARCHMETHOD_EQUALS, Value: sequence},
}
query := PrepareGetByQuery(table, queries...)
err := query(db, sequence)
err := query(db, failedEvent)
if err == nil {
return failedEvent, nil
}
if gorm.IsRecordNotFoundError(err) {
if errors.IsNotFound(err) {
failedEvent.ViewName = viewName
failedEvent.FailedSequnce = sequence
failedEvent.FailedSequence = sequence
failedEvent.FailureCount = 0
return failedEvent, nil
}

View File

@@ -69,7 +69,7 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
case model.SEARCHMETHOD_EQUALS_IGNORE_CASE:
valueText, ok := value.(string)
if !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-idu8e", "Starts with only possible for strings")
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-idu8e", "Equal ignore case only possible for strings")
}
query = query.Where("LOWER("+column+") = LOWER(?)", valueText)
case model.SEARCHMETHOD_STARTS_WITH:
@@ -81,7 +81,7 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
case model.SEARCHMETHOD_STARTS_WITH_IGNORE_CASE:
valueText, ok := value.(string)
if !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eidus", "Starts with only possible for strings")
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eidus", "Starts with ignore case only possible for strings")
}
query = query.Where("LOWER("+column+") LIKE LOWER(?)", valueText+"%")
case model.SEARCHMETHOD_CONTAINS:
@@ -93,10 +93,11 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
case model.SEARCHMETHOD_CONTAINS_IGNORE_CASE:
valueText, ok := value.(string)
if !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eid73", "Contains with only possible for strings")
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eid73", "Contains with ignore case only possible for strings")
}
query = query.Where("LOWER("+column+") LIKE LOWER(?)", "%"+valueText+"%")
case model.SEARCHMETHOD_NOT_EQUALS:
query = query.Where(""+column+" <> ?", value)
default:
return nil, nil
}

View File

@@ -58,7 +58,7 @@ func PrepareSave(table string) func(db *gorm.DB, object interface{}) error {
}
}
func PrepareDelete(table string, key ColumnKey, id string) func(db *gorm.DB) error {
func PrepareDeleteByKey(table string, key ColumnKey, id string) func(db *gorm.DB) error {
return func(db *gorm.DB) error {
err := db.Table(table).
Where(fmt.Sprintf("%s = ?", key.ToColumnName()), id).
@@ -70,3 +70,15 @@ func PrepareDelete(table string, key ColumnKey, id string) func(db *gorm.DB) err
return nil
}
}
func PrepareDeleteByObject(table string, object interface{}) func(db *gorm.DB) error {
return func(db *gorm.DB) error {
err := db.Table(table).
Delete(object).
Error
if err != nil {
return caos_errs.ThrowInternal(err, "VIEW-lso9w", "could not delete object")
}
return nil
}
}

View File

@@ -372,7 +372,90 @@ func TestPrepareDelete(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
getDelete := PrepareDelete(tt.args.table, tt.args.key, tt.args.value)
getDelete := PrepareDeleteByKey(tt.args.table, tt.args.key, tt.args.value)
err := getDelete(tt.db.db)
if !tt.res.wantErr && err != nil {
t.Errorf("got wrong err should be nil: %v ", err)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
if err := tt.db.mock.ExpectationsWereMet(); !tt.res.wantErr && err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
tt.db.close()
})
}
}
func TestPrepareDeleteByObject(t *testing.T) {
type args struct {
table string
object interface{}
}
type res struct {
result Test
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
db *dbMock
args args
res res
}{
{
"delete",
mockDB(t).
expectBegin(nil).
expectRemoveByObject("TESTTABLE", Test{ID: "VALUE", Test: "TEST"}).
expectCommit(nil),
args{
table: "TESTTABLE",
object: &Test{ID: "VALUE", Test: "TEST"},
},
res{
result: Test{ID: "VALUE"},
wantErr: false,
},
},
{
"delete multiple PK",
mockDB(t).
expectBegin(nil).
expectRemoveByObjectMultiplePKs("TESTTABLE", TestMultiplePK{TestID: "TESTID", HodorID: "HODORID", Test: "TEST"}).
expectCommit(nil),
args{
table: "TESTTABLE",
object: &TestMultiplePK{TestID: "TESTID", HodorID: "HODORID", Test: "TEST"},
},
res{
wantErr: false,
},
},
{
"db error",
mockDB(t).
expectBegin(nil).
expectRemoveErr("TESTTABLE", "id", "VALUE", gorm.ErrUnaddressable).
expectCommit(nil),
args{
table: "TESTTABLE",
object: &Test{ID: "VALUE", Test: "TEST"},
},
res{
result: Test{ID: "VALUE"},
wantErr: true,
errFunc: caos_errs.IsInternal,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
getDelete := PrepareDeleteByObject(tt.args.table, tt.args.object)
err := getDelete(tt.db.db)
if !tt.res.wantErr && err != nil {

View File

@@ -51,7 +51,7 @@ func LatestSequence(db *gorm.DB, table, viewName string) (uint64, error) {
return sequence.ActualSequence, nil
}
if gorm.IsRecordNotFoundError(err) {
if caos_errs.IsNotFound(err) {
return 0, nil
}
return 0, caos_errs.ThrowInternalf(err, "VIEW-9LyCB", "unable to get latest sequence of %s", viewName)