Merge pull request #11 from sunboyy/operation-update

Add update operation
This commit is contained in:
sunboyy 2021-01-30 21:56:35 +07:00 committed by GitHub
commit 7f07de08af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 929 additions and 628 deletions

View file

@ -7,8 +7,12 @@
<img src="https://codecov.io/gh/sunboyy/repogen/branch/main/graph/badge.svg?token=9BD5Y8X7NO"/> <img src="https://codecov.io/gh/sunboyy/repogen/branch/main/graph/badge.svg?token=9BD5Y8X7NO"/>
</a> </a>
Repogen is a code generator for database repository in Golang. (WIP) Repogen is a code generator for database repository in Golang inspired by Spring Data JPA. (WIP)
## Features ## Features
Repogen is a library that generates MongoDB repository implementation from repository interface by using method name pattern. Repogen is a library that generates MongoDB repository implementation from repository interface by using method name pattern.
- Method signature validation
- Supports single-entity and multiple-entity operations
- Supports many comparison operators

View file

@ -6,4 +6,4 @@ coverage:
threshold: 4% threshold: 4%
patch: patch:
default: default:
target: 50% target: 60%

View file

@ -48,7 +48,7 @@ func TestGenerateMongoRepository(t *testing.T) {
Mode: spec.QueryModeOne, Mode: spec.QueryModeOne,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "ID", Comparator: spec.ComparatorEqual}, {Field: "ID", Comparator: spec.ComparatorEqual, ParamIndex: 1},
}, },
}, },
}, },
@ -70,8 +70,8 @@ func TestGenerateMongoRepository(t *testing.T) {
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorAnd, Operator: spec.OperatorAnd,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Gender", Comparator: spec.ComparatorNot}, {Field: "Gender", Comparator: spec.ComparatorNot, ParamIndex: 1},
{Field: "Age", Comparator: spec.ComparatorLessThan}, {Field: "Age", Comparator: spec.ComparatorLessThan, ParamIndex: 2},
}, },
}, },
}, },
@ -90,7 +90,7 @@ func TestGenerateMongoRepository(t *testing.T) {
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Age", Comparator: spec.ComparatorLessThanEqual}, {Field: "Age", Comparator: spec.ComparatorLessThanEqual, ParamIndex: 1},
}, },
}, },
}, },
@ -109,7 +109,7 @@ func TestGenerateMongoRepository(t *testing.T) {
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Age", Comparator: spec.ComparatorGreaterThan}, {Field: "Age", Comparator: spec.ComparatorGreaterThan, ParamIndex: 1},
}, },
}, },
}, },
@ -128,7 +128,7 @@ func TestGenerateMongoRepository(t *testing.T) {
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Age", Comparator: spec.ComparatorGreaterThanEqual}, {Field: "Age", Comparator: spec.ComparatorGreaterThanEqual, ParamIndex: 1},
}, },
}, },
}, },
@ -148,7 +148,7 @@ func TestGenerateMongoRepository(t *testing.T) {
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Age", Comparator: spec.ComparatorBetween}, {Field: "Age", Comparator: spec.ComparatorBetween, ParamIndex: 1},
}, },
}, },
}, },
@ -169,8 +169,8 @@ func TestGenerateMongoRepository(t *testing.T) {
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorOr, Operator: spec.OperatorOr,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "Gender", Comparator: spec.ComparatorEqual}, {Field: "Gender", Comparator: spec.ComparatorEqual, ParamIndex: 1},
{Field: "Age", Comparator: spec.ComparatorEqual}, {Field: "Age", Comparator: spec.ComparatorEqual, ParamIndex: 2},
}, },
}, },
}, },
@ -203,99 +203,99 @@ type UserRepositoryMongo struct {
collection *mongo.Collection collection *mongo.Collection
} }
func (r *UserRepositoryMongo) FindByID(ctx context.Context, arg0 primitive.ObjectID) (*UserModel, error) { func (r *UserRepositoryMongo) FindByID(arg0 context.Context, arg1 primitive.ObjectID) (*UserModel, error) {
var entity UserModel var entity UserModel
if err := r.collection.FindOne(ctx, bson.M{ if err := r.collection.FindOne(arg0, bson.M{
"_id": arg0, "_id": arg1,
}).Decode(&entity); err != nil { }).Decode(&entity); err != nil {
return nil, err return nil, err
} }
return &entity, nil return &entity, nil
} }
func (r *UserRepositoryMongo) FindByGenderNotAndAgeLessThan(ctx context.Context, arg0 Gender, arg1 int) (*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderNotAndAgeLessThan(arg0 context.Context, arg1 Gender, arg2 int) (*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"gender": bson.M{"$ne": arg0}, "gender": bson.M{"$ne": arg1},
"age": bson.M{"$lt": arg1}, "age": bson.M{"$lt": arg2},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
} }
func (r *UserRepositoryMongo) FindByAgeLessThanEqual(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeLessThanEqual(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$lte": arg0}, "age": bson.M{"$lte": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
} }
func (r *UserRepositoryMongo) FindByAgeGreaterThan(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeGreaterThan(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gt": arg0}, "age": bson.M{"$gt": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
} }
func (r *UserRepositoryMongo) FindByAgeGreaterThanEqual(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeGreaterThanEqual(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gte": arg0}, "age": bson.M{"$gte": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
} }
func (r *UserRepositoryMongo) FindByAgeBetween(ctx context.Context, arg0 int, arg1 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeBetween(arg0 context.Context, arg1 int, arg2 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gte": arg0, "$lte": arg1}, "age": bson.M{"$gte": arg1, "$lte": arg2},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
} }
func (r *UserRepositoryMongo) FindByGenderOrAge(ctx context.Context, arg0 Gender, arg1 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderOrAge(arg0 context.Context, arg1 Gender, arg2 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"$or": []bson.M{ "$or": []bson.M{
{"gender": arg0}, {"gender": arg1},
{"age": arg1}, {"age": arg2},
}, },
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil

View file

@ -56,7 +56,7 @@ func (g RepositoryGenerator) GenerateMethod(methodSpec spec.MethodSpec, buffer i
} }
var paramTypes []code.Type var paramTypes []code.Type
for _, param := range methodSpec.Params[1:] { for _, param := range methodSpec.Params {
paramTypes = append(paramTypes, param.Type) paramTypes = append(paramTypes, param.Type)
} }
@ -79,6 +79,8 @@ func (g RepositoryGenerator) generateMethodImplementation(methodSpec spec.Method
switch operation := methodSpec.Operation.(type) { switch operation := methodSpec.Operation.(type) {
case spec.FindOperation: case spec.FindOperation:
return g.generateFindImplementation(operation) return g.generateFindImplementation(operation)
case spec.UpdateOperation:
return g.generateUpdateImplementation(operation)
case spec.DeleteOperation: case spec.DeleteOperation:
return g.generateDeleteImplementation(operation) return g.generateDeleteImplementation(operation)
} }
@ -122,6 +124,51 @@ func (g RepositoryGenerator) generateFindImplementation(operation spec.FindOpera
return buffer.String(), nil return buffer.String(), nil
} }
func (g RepositoryGenerator) generateUpdateImplementation(operation spec.UpdateOperation) (string, error) {
buffer := new(bytes.Buffer)
var fields []updateField
for _, field := range operation.Fields {
bsonTag, err := g.bsonTagFromFieldName(field.Name)
if err != nil {
return "", err
}
fields = append(fields, updateField{BsonTag: bsonTag, ParamIndex: field.ParamIndex})
}
querySpec, err := g.mongoQuerySpec(operation.Query)
if err != nil {
return "", err
}
tmplData := mongoUpdateTemplateData{
UpdateFields: fields,
QuerySpec: querySpec,
}
if operation.Mode == spec.QueryModeOne {
tmpl, err := template.New("mongo_repository_updateone").Parse(updateOneTemplate)
if err != nil {
return "", err
}
if err := tmpl.Execute(buffer, tmplData); err != nil {
return "", err
}
} else {
tmpl, err := template.New("mongo_repository_updatemany").Parse(updateManyTemplate)
if err != nil {
return "", err
}
if err := tmpl.Execute(buffer, tmplData); err != nil {
return "", err
}
}
return buffer.String(), nil
}
func (g RepositoryGenerator) generateDeleteImplementation(operation spec.DeleteOperation) (string, error) { func (g RepositoryGenerator) generateDeleteImplementation(operation spec.DeleteOperation) (string, error) {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
@ -161,17 +208,16 @@ func (g RepositoryGenerator) mongoQuerySpec(query spec.QuerySpec) (querySpec, er
var predicates []predicate var predicates []predicate
for _, predicateSpec := range query.Predicates { for _, predicateSpec := range query.Predicates {
structField, ok := g.StructModel.Fields.ByName(predicateSpec.Field) bsonTag, err := g.bsonTagFromFieldName(predicateSpec.Field)
if !ok { if err != nil {
return querySpec{}, fmt.Errorf("struct field %s not found", predicateSpec.Field) return querySpec{}, err
} }
bsonTag, ok := structField.Tags["bson"] predicates = append(predicates, predicate{
if !ok { Field: bsonTag,
return querySpec{}, BsonTagNotFoundError Comparator: predicateSpec.Comparator,
} ParamIndex: predicateSpec.ParamIndex,
})
predicates = append(predicates, predicate{Field: bsonTag[0], Comparator: predicateSpec.Comparator})
} }
return querySpec{ return querySpec{
@ -180,6 +226,20 @@ func (g RepositoryGenerator) mongoQuerySpec(query spec.QuerySpec) (querySpec, er
}, nil }, nil
} }
func (g RepositoryGenerator) bsonTagFromFieldName(fieldName string) (string, error) {
structField, ok := g.StructModel.Fields.ByName(fieldName)
if !ok {
return "", fmt.Errorf("struct field %s not found", fieldName)
}
bsonTag, ok := structField.Tags["bson"]
if !ok {
return "", BsonTagNotFoundError
}
return bsonTag[0], nil
}
func (g RepositoryGenerator) structName() string { func (g RepositoryGenerator) structName() string {
return g.InterfaceName + "Mongo" return g.InterfaceName + "Mongo"
} }

View file

@ -95,16 +95,16 @@ func TestGenerateMethod_Find(t *testing.T) {
Mode: spec.QueryModeOne, Mode: spec.QueryModeOne,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "ID"}, {Comparator: spec.ComparatorEqual, Field: "ID", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByID(ctx context.Context, arg0 primitive.ObjectID) (*UserModel, error) { func (r *UserRepositoryMongo) FindByID(arg0 context.Context, arg1 primitive.ObjectID) (*UserModel, error) {
var entity UserModel var entity UserModel
if err := r.collection.FindOne(ctx, bson.M{ if err := r.collection.FindOne(arg0, bson.M{
"_id": arg0, "_id": arg1,
}).Decode(&entity); err != nil { }).Decode(&entity); err != nil {
return nil, err return nil, err
} }
@ -128,21 +128,21 @@ func (r *UserRepositoryMongo) FindByID(ctx context.Context, arg0 primitive.Objec
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByGender(ctx context.Context, arg0 Gender) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGender(arg0 context.Context, arg1 Gender) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"gender": arg0, "gender": arg1,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -167,23 +167,23 @@ func (r *UserRepositoryMongo) FindByGender(ctx context.Context, arg0 Gender) ([]
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorAnd, Operator: spec.OperatorAnd,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
{Comparator: spec.ComparatorEqual, Field: "Age"}, {Comparator: spec.ComparatorEqual, Field: "Age", ParamIndex: 2},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByGenderAndAge(ctx context.Context, arg0 Gender, arg1 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"gender": arg0, "gender": arg1,
"age": arg1, "age": arg2,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -208,25 +208,25 @@ func (r *UserRepositoryMongo) FindByGenderAndAge(ctx context.Context, arg0 Gende
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorOr, Operator: spec.OperatorOr,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
{Comparator: spec.ComparatorEqual, Field: "Age"}, {Comparator: spec.ComparatorEqual, Field: "Age", ParamIndex: 2},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByGenderOrAge(ctx context.Context, arg0 Gender, arg1 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderOrAge(arg0 context.Context, arg1 Gender, arg2 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"$or": []bson.M{ "$or": []bson.M{
{"gender": arg0}, {"gender": arg1},
{"age": arg1}, {"age": arg2},
}, },
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -249,21 +249,21 @@ func (r *UserRepositoryMongo) FindByGenderOrAge(ctx context.Context, arg0 Gender
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorNot, Field: "Gender"}, {Comparator: spec.ComparatorNot, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByGenderNot(ctx context.Context, arg0 Gender) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderNot(arg0 context.Context, arg1 Gender) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"gender": bson.M{"$ne": arg0}, "gender": bson.M{"$ne": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -286,21 +286,21 @@ func (r *UserRepositoryMongo) FindByGenderNot(ctx context.Context, arg0 Gender)
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorLessThan, Field: "Age"}, {Comparator: spec.ComparatorLessThan, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByAgeLessThan(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeLessThan(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$lt": arg0}, "age": bson.M{"$lt": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -323,21 +323,21 @@ func (r *UserRepositoryMongo) FindByAgeLessThan(ctx context.Context, arg0 int) (
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorLessThanEqual, Field: "Age"}, {Comparator: spec.ComparatorLessThanEqual, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByAgeLessThanEqual(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeLessThanEqual(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$lte": arg0}, "age": bson.M{"$lte": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -360,21 +360,21 @@ func (r *UserRepositoryMongo) FindByAgeLessThanEqual(ctx context.Context, arg0 i
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorGreaterThan, Field: "Age"}, {Comparator: spec.ComparatorGreaterThan, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByAgeGreaterThan(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeGreaterThan(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gt": arg0}, "age": bson.M{"$gt": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -397,21 +397,21 @@ func (r *UserRepositoryMongo) FindByAgeGreaterThan(ctx context.Context, arg0 int
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorGreaterThanEqual, Field: "Age"}, {Comparator: spec.ComparatorGreaterThanEqual, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByAgeGreaterThanEqual(ctx context.Context, arg0 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeGreaterThanEqual(arg0 context.Context, arg1 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gte": arg0}, "age": bson.M{"$gte": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -435,21 +435,21 @@ func (r *UserRepositoryMongo) FindByAgeGreaterThanEqual(ctx context.Context, arg
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorBetween, Field: "Age"}, {Comparator: spec.ComparatorBetween, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByAgeBetween(ctx context.Context, arg0 int, arg1 int) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByAgeBetween(arg0 context.Context, arg1 int, arg2 int) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"age": bson.M{"$gte": arg0, "$lte": arg1}, "age": bson.M{"$gte": arg1, "$lte": arg2},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -472,21 +472,21 @@ func (r *UserRepositoryMongo) FindByAgeBetween(ctx context.Context, arg0 int, ar
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorIn, Field: "Gender"}, {Comparator: spec.ComparatorIn, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) FindByGenderIn(ctx context.Context, arg0 []Gender) ([]*UserModel, error) { func (r *UserRepositoryMongo) FindByGenderIn(arg0 context.Context, arg1 []Gender) ([]*UserModel, error) {
cursor, err := r.collection.Find(ctx, bson.M{ cursor, err := r.collection.Find(arg0, bson.M{
"gender": bson.M{"$in": arg0}, "gender": bson.M{"$in": arg1},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*UserModel var entities []*UserModel
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil return entities, nil
@ -512,6 +512,109 @@ func (r *UserRepositoryMongo) FindByGenderIn(ctx context.Context, arg0 []Gender)
} }
} }
func TestGenerateMethod_Update(t *testing.T) {
testTable := []GenerateMethodTestCase{
{
Name: "simple update one method",
MethodSpec: spec.MethodSpec{
Name: "UpdateAgeByID",
Params: []code.Param{
{Name: "ctx", Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Name: "age", Type: code.SimpleType("int")},
{Name: "id", Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.SimpleType("bool"),
code.SimpleType("error"),
},
Operation: spec.UpdateOperation{
Fields: []spec.UpdateField{
{Name: "Age", ParamIndex: 1},
},
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{Field: "ID", Comparator: spec.ComparatorEqual, ParamIndex: 2},
},
},
},
},
ExpectedCode: `
func (r *UserRepositoryMongo) UpdateAgeByID(arg0 context.Context, arg1 int, arg2 primitive.ObjectID) (bool, error) {
result, err := r.collection.UpdateOne(arg0, bson.M{
"_id": arg2,
}, bson.M{
"$set": bson.M{
"age": arg1,
},
})
if err != nil {
return false, err
}
return result.MatchedCount > 0, err
}
`,
},
{
Name: "simple update many method",
MethodSpec: spec.MethodSpec{
Name: "UpdateAgeByGender",
Params: []code.Param{
{Name: "ctx", Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Name: "age", Type: code.SimpleType("int")},
{Name: "gender", Type: code.SimpleType("Gender")},
},
Returns: []code.Type{
code.SimpleType("int"),
code.SimpleType("error"),
},
Operation: spec.UpdateOperation{
Fields: []spec.UpdateField{
{Name: "Age", ParamIndex: 1},
},
Mode: spec.QueryModeMany,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{Field: "Gender", Comparator: spec.ComparatorEqual, ParamIndex: 2},
},
},
},
},
ExpectedCode: `
func (r *UserRepositoryMongo) UpdateAgeByGender(arg0 context.Context, arg1 int, arg2 Gender) (int, error) {
result, err := r.collection.UpdateMany(arg0, bson.M{
"gender": arg2,
}, bson.M{
"$set": bson.M{
"age": arg1,
},
})
if err != nil {
return 0, err
}
return int(result.MatchedCount), err
}
`,
},
}
for _, testCase := range testTable {
t.Run(testCase.Name, func(t *testing.T) {
generator := mongo.NewGenerator(userModel, "UserRepository")
buffer := new(bytes.Buffer)
err := generator.GenerateMethod(testCase.MethodSpec, buffer)
if err != nil {
t.Error(err)
}
if err := testutils.ExpectMultiLineString(testCase.ExpectedCode, buffer.String()); err != nil {
t.Error(err)
}
})
}
}
func TestGenerateMethod_Delete(t *testing.T) { func TestGenerateMethod_Delete(t *testing.T) {
testTable := []GenerateMethodTestCase{ testTable := []GenerateMethodTestCase{
{ {
@ -527,15 +630,15 @@ func TestGenerateMethod_Delete(t *testing.T) {
Mode: spec.QueryModeOne, Mode: spec.QueryModeOne,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "ID"}, {Comparator: spec.ComparatorEqual, Field: "ID", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByID(ctx context.Context, arg0 primitive.ObjectID) (bool, error) { func (r *UserRepositoryMongo) DeleteByID(arg0 context.Context, arg1 primitive.ObjectID) (bool, error) {
result, err := r.collection.DeleteOne(ctx, bson.M{ result, err := r.collection.DeleteOne(arg0, bson.M{
"_id": arg0, "_id": arg1,
}) })
if err != nil { if err != nil {
return false, err return false, err
@ -560,15 +663,15 @@ func (r *UserRepositoryMongo) DeleteByID(ctx context.Context, arg0 primitive.Obj
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByGender(ctx context.Context, arg0 Gender) (int, error) { func (r *UserRepositoryMongo) DeleteByGender(arg0 context.Context, arg1 Gender) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"gender": arg0, "gender": arg1,
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -595,17 +698,17 @@ func (r *UserRepositoryMongo) DeleteByGender(ctx context.Context, arg0 Gender) (
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorAnd, Operator: spec.OperatorAnd,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
{Comparator: spec.ComparatorEqual, Field: "Age"}, {Comparator: spec.ComparatorEqual, Field: "Age", ParamIndex: 2},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByGenderAndAge(ctx context.Context, arg0 Gender, arg1 int) (int, error) { func (r *UserRepositoryMongo) DeleteByGenderAndAge(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"gender": arg0, "gender": arg1,
"age": arg1, "age": arg2,
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -632,18 +735,18 @@ func (r *UserRepositoryMongo) DeleteByGenderAndAge(ctx context.Context, arg0 Gen
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Operator: spec.OperatorOr, Operator: spec.OperatorOr,
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorEqual, Field: "Gender"}, {Comparator: spec.ComparatorEqual, Field: "Gender", ParamIndex: 1},
{Comparator: spec.ComparatorEqual, Field: "Age"}, {Comparator: spec.ComparatorEqual, Field: "Age", ParamIndex: 2},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByGenderOrAge(ctx context.Context, arg0 Gender, arg1 int) (int, error) { func (r *UserRepositoryMongo) DeleteByGenderOrAge(arg0 context.Context, arg1 Gender, arg2 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"$or": []bson.M{ "$or": []bson.M{
{"gender": arg0}, {"gender": arg1},
{"age": arg1}, {"age": arg2},
}, },
}) })
if err != nil { if err != nil {
@ -669,15 +772,15 @@ func (r *UserRepositoryMongo) DeleteByGenderOrAge(ctx context.Context, arg0 Gend
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorNot, Field: "Gender"}, {Comparator: spec.ComparatorNot, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByGenderNot(ctx context.Context, arg0 Gender) (int, error) { func (r *UserRepositoryMongo) DeleteByGenderNot(arg0 context.Context, arg1 Gender) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"gender": bson.M{"$ne": arg0}, "gender": bson.M{"$ne": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -702,15 +805,15 @@ func (r *UserRepositoryMongo) DeleteByGenderNot(ctx context.Context, arg0 Gender
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorLessThan, Field: "Age"}, {Comparator: spec.ComparatorLessThan, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByAgeLessThan(ctx context.Context, arg0 int) (int, error) { func (r *UserRepositoryMongo) DeleteByAgeLessThan(arg0 context.Context, arg1 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"age": bson.M{"$lt": arg0}, "age": bson.M{"$lt": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -735,15 +838,15 @@ func (r *UserRepositoryMongo) DeleteByAgeLessThan(ctx context.Context, arg0 int)
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorLessThanEqual, Field: "Age"}, {Comparator: spec.ComparatorLessThanEqual, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByAgeLessThanEqual(ctx context.Context, arg0 int) (int, error) { func (r *UserRepositoryMongo) DeleteByAgeLessThanEqual(arg0 context.Context, arg1 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"age": bson.M{"$lte": arg0}, "age": bson.M{"$lte": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -768,15 +871,15 @@ func (r *UserRepositoryMongo) DeleteByAgeLessThanEqual(ctx context.Context, arg0
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorGreaterThan, Field: "Age"}, {Comparator: spec.ComparatorGreaterThan, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByAgeGreaterThan(ctx context.Context, arg0 int) (int, error) { func (r *UserRepositoryMongo) DeleteByAgeGreaterThan(arg0 context.Context, arg1 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"age": bson.M{"$gt": arg0}, "age": bson.M{"$gt": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -801,15 +904,15 @@ func (r *UserRepositoryMongo) DeleteByAgeGreaterThan(ctx context.Context, arg0 i
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorGreaterThanEqual, Field: "Age"}, {Comparator: spec.ComparatorGreaterThanEqual, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByAgeGreaterThanEqual(ctx context.Context, arg0 int) (int, error) { func (r *UserRepositoryMongo) DeleteByAgeGreaterThanEqual(arg0 context.Context, arg1 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"age": bson.M{"$gte": arg0}, "age": bson.M{"$gte": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -835,15 +938,15 @@ func (r *UserRepositoryMongo) DeleteByAgeGreaterThanEqual(ctx context.Context, a
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorBetween, Field: "Age"}, {Comparator: spec.ComparatorBetween, Field: "Age", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByAgeBetween(ctx context.Context, arg0 int, arg1 int) (int, error) { func (r *UserRepositoryMongo) DeleteByAgeBetween(arg0 context.Context, arg1 int, arg2 int) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"age": bson.M{"$gte": arg0, "$lte": arg1}, "age": bson.M{"$gte": arg1, "$lte": arg2},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -868,15 +971,15 @@ func (r *UserRepositoryMongo) DeleteByAgeBetween(ctx context.Context, arg0 int,
Mode: spec.QueryModeMany, Mode: spec.QueryModeMany,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Comparator: spec.ComparatorIn, Field: "Gender"}, {Comparator: spec.ComparatorIn, Field: "Gender", ParamIndex: 1},
}, },
}, },
}, },
}, },
ExpectedCode: ` ExpectedCode: `
func (r *UserRepositoryMongo) DeleteByGenderIn(ctx context.Context, arg0 []Gender) (int, error) { func (r *UserRepositoryMongo) DeleteByGenderIn(arg0 context.Context, arg1 []Gender) (int, error) {
result, err := r.collection.DeleteMany(ctx, bson.M{ result, err := r.collection.DeleteMany(arg0, bson.M{
"gender": bson.M{"$in": arg0}, "gender": bson.M{"$in": arg1},
}) })
if err != nil { if err != nil {
return 0, err return 0, err
@ -929,7 +1032,7 @@ func TestGenerateMethod_Invalid(t *testing.T) {
ExpectedError: mongo.OperationNotSupportedError, ExpectedError: mongo.OperationNotSupportedError,
}, },
{ {
Name: "bson tag not found", Name: "bson tag not found in query",
Method: spec.MethodSpec{ Method: spec.MethodSpec{
Name: "FindByAccessToken", Name: "FindByAccessToken",
Params: []code.Param{ Params: []code.Param{
@ -944,7 +1047,34 @@ func TestGenerateMethod_Invalid(t *testing.T) {
Mode: spec.QueryModeOne, Mode: spec.QueryModeOne,
Query: spec.QuerySpec{ Query: spec.QuerySpec{
Predicates: []spec.Predicate{ Predicates: []spec.Predicate{
{Field: "AccessToken", Comparator: spec.ComparatorEqual}, {Field: "AccessToken", Comparator: spec.ComparatorEqual, ParamIndex: 1},
},
},
},
},
ExpectedError: mongo.BsonTagNotFoundError,
},
{
Name: "bson tag not found in update field",
Method: spec.MethodSpec{
Name: "UpdateAccessTokenByID",
Params: []code.Param{
{Type: code.ExternalType{PackageAlias: "context", Name: "Context"}},
{Type: code.SimpleType("string")},
{Type: code.ExternalType{PackageAlias: "primitive", Name: "ObjectID"}},
},
Returns: []code.Type{
code.SimpleType("bool"),
code.SimpleType("error"),
},
Operation: spec.UpdateOperation{
Fields: []spec.UpdateField{
{Name: "AccessToken", ParamIndex: 1},
},
Mode: spec.QueryModeOne,
Query: spec.QuerySpec{
Predicates: []spec.Predicate{
{Field: "ID", Comparator: spec.ComparatorEqual, ParamIndex: 2},
}, },
}, },
}, },

View file

@ -7,6 +7,11 @@ import (
"github.com/sunboyy/repogen/internal/spec" "github.com/sunboyy/repogen/internal/spec"
) )
type updateField struct {
BsonTag string
ParamIndex int
}
type querySpec struct { type querySpec struct {
Operator spec.Operator Operator spec.Operator
Predicates []predicate Predicates []predicate
@ -14,10 +19,8 @@ type querySpec struct {
func (q querySpec) Code() string { func (q querySpec) Code() string {
var predicateCodes []string var predicateCodes []string
var argIndex int
for _, predicate := range q.Predicates { for _, predicate := range q.Predicates {
predicateCodes = append(predicateCodes, predicate.Code(argIndex)) predicateCodes = append(predicateCodes, predicate.Code())
argIndex += predicate.Comparator.NumberOfArguments()
} }
var lines []string var lines []string
@ -39,26 +42,27 @@ func (q querySpec) Code() string {
type predicate struct { type predicate struct {
Field string Field string
Comparator spec.Comparator Comparator spec.Comparator
ParamIndex int
} }
func (p predicate) Code(argIndex int) string { func (p predicate) Code() string {
switch p.Comparator { switch p.Comparator {
case spec.ComparatorEqual: case spec.ComparatorEqual:
return fmt.Sprintf(`"%s": arg%d`, p.Field, argIndex) return fmt.Sprintf(`"%s": arg%d`, p.Field, p.ParamIndex)
case spec.ComparatorNot: case spec.ComparatorNot:
return fmt.Sprintf(`"%s": bson.M{"$ne": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$ne": arg%d}`, p.Field, p.ParamIndex)
case spec.ComparatorLessThan: case spec.ComparatorLessThan:
return fmt.Sprintf(`"%s": bson.M{"$lt": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$lt": arg%d}`, p.Field, p.ParamIndex)
case spec.ComparatorLessThanEqual: case spec.ComparatorLessThanEqual:
return fmt.Sprintf(`"%s": bson.M{"$lte": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$lte": arg%d}`, p.Field, p.ParamIndex)
case spec.ComparatorGreaterThan: case spec.ComparatorGreaterThan:
return fmt.Sprintf(`"%s": bson.M{"$gt": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$gt": arg%d}`, p.Field, p.ParamIndex)
case spec.ComparatorGreaterThanEqual: case spec.ComparatorGreaterThanEqual:
return fmt.Sprintf(`"%s": bson.M{"$gte": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$gte": arg%d}`, p.Field, p.ParamIndex)
case spec.ComparatorBetween: case spec.ComparatorBetween:
return fmt.Sprintf(`"%s": bson.M{"$gte": arg%d, "$lte": arg%d}`, p.Field, argIndex, argIndex+1) return fmt.Sprintf(`"%s": bson.M{"$gte": arg%d, "$lte": arg%d}`, p.Field, p.ParamIndex, p.ParamIndex+1)
case spec.ComparatorIn: case spec.ComparatorIn:
return fmt.Sprintf(`"%s": bson.M{"$in": arg%d}`, p.Field, argIndex) return fmt.Sprintf(`"%s": bson.M{"$in": arg%d}`, p.Field, p.ParamIndex)
} }
return "" return ""
} }

View file

@ -33,7 +33,7 @@ type mongoConstructorTemplateData struct {
} }
const methodTemplate = ` const methodTemplate = `
func (r *{{.StructName}}) {{.MethodName}}(ctx context.Context, {{.Parameters}}){{.Returns}} { func (r *{{.StructName}}) {{.MethodName}}({{.Parameters}}){{.Returns}} {
{{.Implementation}} {{.Implementation}}
} }
` `
@ -76,30 +76,59 @@ type mongoFindTemplateData struct {
} }
const findOneTemplate = ` var entity {{.EntityType}} const findOneTemplate = ` var entity {{.EntityType}}
if err := r.collection.FindOne(ctx, bson.M{ if err := r.collection.FindOne(arg0, bson.M{
{{.QuerySpec.Code}} {{.QuerySpec.Code}}
}).Decode(&entity); err != nil { }).Decode(&entity); err != nil {
return nil, err return nil, err
} }
return &entity, nil` return &entity, nil`
const findManyTemplate = ` cursor, err := r.collection.Find(ctx, bson.M{ const findManyTemplate = ` cursor, err := r.collection.Find(arg0, bson.M{
{{.QuerySpec.Code}} {{.QuerySpec.Code}}
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
var entities []*{{.EntityType}} var entities []*{{.EntityType}}
if err := cursor.All(ctx, &entities); err != nil { if err := cursor.All(arg0, &entities); err != nil {
return nil, err return nil, err
} }
return entities, nil` return entities, nil`
type mongoUpdateTemplateData struct {
UpdateFields []updateField
QuerySpec querySpec
}
const updateOneTemplate = ` result, err := r.collection.UpdateOne(arg0, bson.M{
{{.QuerySpec.Code}}
}, bson.M{
"$set": bson.M{
{{range $index, $element := .UpdateFields}} "{{$element.BsonTag}}": arg{{$element.ParamIndex}},
{{end}} },
})
if err != nil {
return false, err
}
return result.MatchedCount > 0, err`
const updateManyTemplate = ` result, err := r.collection.UpdateMany(arg0, bson.M{
{{.QuerySpec.Code}}
}, bson.M{
"$set": bson.M{
{{range $index, $element := .UpdateFields}} "{{$element.BsonTag}}": arg{{$element.ParamIndex}},
{{end}} },
})
if err != nil {
return 0, err
}
return int(result.MatchedCount), err`
type mongoDeleteTemplateData struct { type mongoDeleteTemplateData struct {
QuerySpec querySpec QuerySpec querySpec
} }
const deleteOneTemplate = ` result, err := r.collection.DeleteOne(ctx, bson.M{ const deleteOneTemplate = ` result, err := r.collection.DeleteOne(arg0, bson.M{
{{.QuerySpec.Code}} {{.QuerySpec.Code}}
}) })
if err != nil { if err != nil {
@ -107,7 +136,7 @@ const deleteOneTemplate = ` result, err := r.collection.DeleteOne(ctx, bson.M{
} }
return result.DeletedCount > 0, nil` return result.DeletedCount > 0, nil`
const deleteManyTemplate = ` result, err := r.collection.DeleteMany(ctx, bson.M{ const deleteManyTemplate = ` result, err := r.collection.DeleteMany(arg0, bson.M{
{{.QuerySpec.Code}} {{.QuerySpec.Code}}
}) })
if err != nil { if err != nil {

View file

@ -13,6 +13,8 @@ func (err ParsingError) Error() string {
return "invalid query" return "invalid query"
case InvalidParamError: case InvalidParamError:
return "parameters do not match the query" return "parameters do not match the query"
case InvalidUpdateFieldsError:
return "update fields is invalid"
case UnsupportedReturnError: case UnsupportedReturnError:
return "this type of return is not supported" return "this type of return is not supported"
case ContextParamRequiredError: case ContextParamRequiredError:
@ -29,6 +31,7 @@ const (
UnsupportedNameError ParsingError = "ERROR_UNSUPPORTED" UnsupportedNameError ParsingError = "ERROR_UNSUPPORTED"
InvalidQueryError ParsingError = "ERROR_INVALID_QUERY" InvalidQueryError ParsingError = "ERROR_INVALID_QUERY"
InvalidParamError ParsingError = "ERROR_INVALID_PARAM" InvalidParamError ParsingError = "ERROR_INVALID_PARAM"
InvalidUpdateFieldsError ParsingError = "ERROR_INVALID_UPDATE_FIELDS"
UnsupportedReturnError ParsingError = "ERROR_INVALID_RETURN" UnsupportedReturnError ParsingError = "ERROR_INVALID_RETURN"
ContextParamRequiredError ParsingError = "ERROR_CONTEXT_PARAM_REQUIRED" ContextParamRequiredError ParsingError = "ERROR_CONTEXT_PARAM_REQUIRED"
StructFieldNotFoundError ParsingError = "ERROR_STRUCT_FIELD_NOT_FOUND" StructFieldNotFoundError ParsingError = "ERROR_STRUCT_FIELD_NOT_FOUND"

View file

@ -33,6 +33,19 @@ type FindOperation struct {
Query QuerySpec Query QuerySpec
} }
// UpdateOperation is a method specification for update operations
type UpdateOperation struct {
Fields []UpdateField
Mode QueryMode
Query QuerySpec
}
// UpdateField stores mapping between field name in the model and the parameter index
type UpdateField struct {
Name string
ParamIndex int
}
// DeleteOperation is a method specification for delete operations // DeleteOperation is a method specification for delete operations
type DeleteOperation struct { type DeleteOperation struct {
Mode QueryMode Mode QueryMode
@ -98,31 +111,32 @@ func (c Comparator) NumberOfArguments() int {
type Predicate struct { type Predicate struct {
Field string Field string
Comparator Comparator Comparator Comparator
ParamIndex int
} }
type predicateToken []string type predicateToken []string
func (t predicateToken) ToPredicate() Predicate { func (t predicateToken) ToPredicate(paramIndex int) Predicate {
if len(t) > 1 && t[len(t)-1] == "Not" { if len(t) > 1 && t[len(t)-1] == "Not" {
return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorNot} return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorNot, ParamIndex: paramIndex}
} }
if len(t) > 2 && t[len(t)-2] == "Less" && t[len(t)-1] == "Than" { if len(t) > 2 && t[len(t)-2] == "Less" && t[len(t)-1] == "Than" {
return Predicate{Field: strings.Join(t[:len(t)-2], ""), Comparator: ComparatorLessThan} return Predicate{Field: strings.Join(t[:len(t)-2], ""), Comparator: ComparatorLessThan, ParamIndex: paramIndex}
} }
if len(t) > 3 && t[len(t)-3] == "Less" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" { if len(t) > 3 && t[len(t)-3] == "Less" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" {
return Predicate{Field: strings.Join(t[:len(t)-3], ""), Comparator: ComparatorLessThanEqual} return Predicate{Field: strings.Join(t[:len(t)-3], ""), Comparator: ComparatorLessThanEqual, ParamIndex: paramIndex}
} }
if len(t) > 2 && t[len(t)-2] == "Greater" && t[len(t)-1] == "Than" { if len(t) > 2 && t[len(t)-2] == "Greater" && t[len(t)-1] == "Than" {
return Predicate{Field: strings.Join(t[:len(t)-2], ""), Comparator: ComparatorGreaterThan} return Predicate{Field: strings.Join(t[:len(t)-2], ""), Comparator: ComparatorGreaterThan, ParamIndex: paramIndex}
} }
if len(t) > 3 && t[len(t)-3] == "Greater" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" { if len(t) > 3 && t[len(t)-3] == "Greater" && t[len(t)-2] == "Than" && t[len(t)-1] == "Equal" {
return Predicate{Field: strings.Join(t[:len(t)-3], ""), Comparator: ComparatorGreaterThanEqual} return Predicate{Field: strings.Join(t[:len(t)-3], ""), Comparator: ComparatorGreaterThanEqual, ParamIndex: paramIndex}
} }
if len(t) > 1 && t[len(t)-1] == "In" { if len(t) > 1 && t[len(t)-1] == "In" {
return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorIn} return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorIn, ParamIndex: paramIndex}
} }
if len(t) > 1 && t[len(t)-1] == "Between" { if len(t) > 1 && t[len(t)-1] == "Between" {
return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorBetween} return Predicate{Field: strings.Join(t[:len(t)-1], ""), Comparator: ComparatorBetween, ParamIndex: paramIndex}
} }
return Predicate{Field: strings.Join(t, ""), Comparator: ComparatorEqual} return Predicate{Field: strings.Join(t, ""), Comparator: ComparatorEqual, ParamIndex: paramIndex}
} }

View file

@ -25,6 +25,8 @@ func (p interfaceMethodParser) Parse() (MethodSpec, error) {
switch methodNameTokens[0] { switch methodNameTokens[0] {
case "Find": case "Find":
return p.parseFindMethod(methodNameTokens[1:]) return p.parseFindMethod(methodNameTokens[1:])
case "Update":
return p.parseUpdateMethod(methodNameTokens[1:])
case "Delete": case "Delete":
return p.parseDeleteMethod(methodNameTokens[1:]) return p.parseDeleteMethod(methodNameTokens[1:])
} }
@ -41,12 +43,16 @@ func (p interfaceMethodParser) parseFindMethod(tokens []string) (MethodSpec, err
return MethodSpec{}, err return MethodSpec{}, err
} }
querySpec, err := p.parseQuery(tokens) querySpec, err := p.parseQuery(tokens, 1)
if err != nil { if err != nil {
return MethodSpec{}, err return MethodSpec{}, err
} }
if err := p.validateMethodSignature(querySpec); err != nil { if err := p.validateContextParam(); err != nil {
return MethodSpec{}, err
}
if err := p.validateQueryFromParams(p.Method.Params[1:], querySpec); err != nil {
return MethodSpec{}, err return MethodSpec{}, err
} }
@ -94,22 +100,94 @@ func (p interfaceMethodParser) extractFindReturns(returns []code.Type) (QueryMod
return "", UnsupportedReturnError return "", UnsupportedReturnError
} }
func (p interfaceMethodParser) parseUpdateMethod(tokens []string) (MethodSpec, error) {
if len(tokens) == 0 {
return MethodSpec{}, UnsupportedNameError
}
mode, err := p.extractCountReturns(p.Method.Returns)
if err != nil {
return MethodSpec{}, err
}
paramIndex := 1
var fields []UpdateField
var aggregatedToken string
for i, token := range tokens {
if token == "By" || token == "All" {
tokens = tokens[i:]
break
} else if token != "And" {
aggregatedToken += token
} else if len(aggregatedToken) == 0 {
return MethodSpec{}, InvalidUpdateFieldsError
} else {
fields = append(fields, UpdateField{Name: aggregatedToken, ParamIndex: paramIndex})
paramIndex++
aggregatedToken = ""
}
}
if len(aggregatedToken) == 0 {
return MethodSpec{}, InvalidUpdateFieldsError
}
fields = append(fields, UpdateField{Name: aggregatedToken, ParamIndex: paramIndex})
querySpec, err := p.parseQuery(tokens, 1+len(fields))
if err != nil {
return MethodSpec{}, err
}
if err := p.validateContextParam(); err != nil {
return MethodSpec{}, err
}
for _, field := range fields {
structField, ok := p.StructModel.Fields.ByName(field.Name)
if !ok {
return MethodSpec{}, StructFieldNotFoundError
}
if structField.Type != p.Method.Params[field.ParamIndex].Type {
return MethodSpec{}, InvalidParamError
}
}
if err := p.validateQueryFromParams(p.Method.Params[len(fields)+1:], querySpec); err != nil {
return MethodSpec{}, err
}
return MethodSpec{
Name: p.Method.Name,
Params: p.Method.Params,
Returns: p.Method.Returns,
Operation: UpdateOperation{
Fields: fields,
Mode: mode,
Query: querySpec,
},
}, nil
}
func (p interfaceMethodParser) parseDeleteMethod(tokens []string) (MethodSpec, error) { func (p interfaceMethodParser) parseDeleteMethod(tokens []string) (MethodSpec, error) {
if len(tokens) == 0 { if len(tokens) == 0 {
return MethodSpec{}, UnsupportedNameError return MethodSpec{}, UnsupportedNameError
} }
mode, err := p.extractDeleteReturns(p.Method.Returns) mode, err := p.extractCountReturns(p.Method.Returns)
if err != nil { if err != nil {
return MethodSpec{}, err return MethodSpec{}, err
} }
querySpec, err := p.parseQuery(tokens) querySpec, err := p.parseQuery(tokens, 1)
if err != nil { if err != nil {
return MethodSpec{}, err return MethodSpec{}, err
} }
if err := p.validateMethodSignature(querySpec); err != nil { if err := p.validateContextParam(); err != nil {
return MethodSpec{}, err
}
if err := p.validateQueryFromParams(p.Method.Params[1:], querySpec); err != nil {
return MethodSpec{}, err return MethodSpec{}, err
} }
@ -124,7 +202,7 @@ func (p interfaceMethodParser) parseDeleteMethod(tokens []string) (MethodSpec, e
}, nil }, nil
} }
func (p interfaceMethodParser) extractDeleteReturns(returns []code.Type) (QueryMode, error) { func (p interfaceMethodParser) extractCountReturns(returns []code.Type) (QueryMode, error) {
if len(returns) != 2 { if len(returns) != 2 {
return "", UnsupportedReturnError return "", UnsupportedReturnError
} }
@ -146,7 +224,7 @@ func (p interfaceMethodParser) extractDeleteReturns(returns []code.Type) (QueryM
return "", UnsupportedReturnError return "", UnsupportedReturnError
} }
func (p interfaceMethodParser) parseQuery(tokens []string) (QuerySpec, error) { func (p interfaceMethodParser) parseQuery(tokens []string, paramIndex int) (QuerySpec, error) {
if len(tokens) == 0 { if len(tokens) == 0 {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, InvalidQueryError
} }
@ -176,11 +254,15 @@ func (p interfaceMethodParser) parseQuery(tokens []string) (QuerySpec, error) {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, InvalidQueryError
} else if token == "And" && operator != OperatorOr { } else if token == "And" && operator != OperatorOr {
operator = OperatorAnd operator = OperatorAnd
predicates = append(predicates, aggregatedToken.ToPredicate()) predicate := aggregatedToken.ToPredicate(paramIndex)
predicates = append(predicates, predicate)
paramIndex += predicate.Comparator.NumberOfArguments()
aggregatedToken = predicateToken{} aggregatedToken = predicateToken{}
} else if token == "Or" && operator != OperatorAnd { } else if token == "Or" && operator != OperatorAnd {
operator = OperatorOr operator = OperatorOr
predicates = append(predicates, aggregatedToken.ToPredicate()) predicate := aggregatedToken.ToPredicate(paramIndex)
predicates = append(predicates, predicate)
paramIndex += predicate.Comparator.NumberOfArguments()
aggregatedToken = predicateToken{} aggregatedToken = predicateToken{}
} else { } else {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, InvalidQueryError
@ -189,22 +271,25 @@ func (p interfaceMethodParser) parseQuery(tokens []string) (QuerySpec, error) {
if len(aggregatedToken) == 0 { if len(aggregatedToken) == 0 {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, InvalidQueryError
} }
predicates = append(predicates, aggregatedToken.ToPredicate()) predicates = append(predicates, aggregatedToken.ToPredicate(paramIndex))
return QuerySpec{Operator: operator, Predicates: predicates}, nil return QuerySpec{Operator: operator, Predicates: predicates}, nil
} }
func (p interfaceMethodParser) validateMethodSignature(querySpec QuerySpec) error { func (p interfaceMethodParser) validateContextParam() error {
contextType := code.ExternalType{PackageAlias: "context", Name: "Context"} contextType := code.ExternalType{PackageAlias: "context", Name: "Context"}
if len(p.Method.Params) == 0 || p.Method.Params[0].Type != contextType { if len(p.Method.Params) == 0 || p.Method.Params[0].Type != contextType {
return ContextParamRequiredError return ContextParamRequiredError
} }
return nil
}
if querySpec.NumberOfArguments()+1 != len(p.Method.Params) { func (p interfaceMethodParser) validateQueryFromParams(params []code.Param, querySpec QuerySpec) error {
if querySpec.NumberOfArguments() != len(params) {
return InvalidParamError return InvalidParamError
} }
currentParamIndex := 1 var currentParamIndex int
for _, predicate := range querySpec.Predicates { for _, predicate := range querySpec.Predicates {
structField, ok := p.StructModel.Fields.ByName(predicate.Field) structField, ok := p.StructModel.Fields.ByName(predicate.Field)
if !ok { if !ok {
@ -212,7 +297,7 @@ func (p interfaceMethodParser) validateMethodSignature(querySpec QuerySpec) erro
} }
for i := 0; i < predicate.Comparator.NumberOfArguments(); i++ { for i := 0; i < predicate.Comparator.NumberOfArguments(); i++ {
if p.Method.Params[currentParamIndex].Type != predicate.Comparator.ArgumentTypeFromFieldType( if params[currentParamIndex].Type != predicate.Comparator.ArgumentTypeFromFieldType(
structField.Type) { structField.Type) {
return InvalidParamError return InvalidParamError
} }

File diff suppressed because it is too large Load diff