Skip to content

Commit

Permalink
Feature/preloading (#4)
Browse files Browse the repository at this point in the history
* update mockery version

* fix typos in Authentication, resource and constructor

* fix route for auth unit tests

* remove error that is always nil

* add TODO to the changes to be done in the session service

* add error management to middlewareJSON

* add CommandInitializer to init configuration keys

* init database configuration keys

* init session configuration keys

* init initialization configuration keys

* init server configuration keys

* init logger configuration keys

* remove unused r.md file in commands

* move time to utils

* create super user when adding the auth controller

* update info controller and routes creation

* change the way badaas server is created

* move test e2e to test_e2e/ and docker to docker/

* do not run auto migration on test e2e execution + automigration refactor

* update docs and developers utils

* remove unnecesary init.sh for cockroach and add health check

* move db connection to orm module and make automigration possible by fx

* info and auth modules now also provide the corresponding services and middlewares

* use gotestsum to run tests + create tests report + tool to install dependencies

* add base models to orm

* add crud services and repositories in orm module

* adapt existing services and controllers to use orm module

* add integration tests

* update documentation

* update changelog

* add support for operators in conditions

* add noteq

* add lt

* add ltoreq

* add gt

* add gtoreq

* add isnull

* add isnotnull

* add is true

* add is not true

* add is false

* add is not false

* add is unknown

* add is not unknown

* add is distict

* add is not distict

* add array in

* add array not in

* add like

* add between

* add not between

* add and

* add or

* add not

* add unsafe conditions

* use all coverage files in sonar

* ignore all tests results

* update changelog

* create mocks for new types

* replace inverse join generation by inverse reference in models

* table class in place of strings for table name

* update to conditions generation that fix embedded names

* refactor: add field identifier

* add possibility to do preload

* support nested preloads

* add a way to know if a relation was loaded or not

* list and nested attributes preload

* update mocks

* update changelog

* cli: preload
  • Loading branch information
FrancoLiberali authored Dec 16, 2023
1 parent 19c9371 commit 826a8e5
Show file tree
Hide file tree
Showing 109 changed files with 4,436 additions and 978 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Transform BadAas into a library.
- Add badaas-orm with the compilable query system.
- Add operators support
- Add preloading

[unreleased]: https://github.com/ditrit/badaas/blob/main/changelog.md#unreleased
6 changes: 5 additions & 1 deletion cli/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
lint:
golangci-lint run

test_unit:
test_unit: clean_test_unit_results
go test ./... -v

clean_test_unit_results:
rm -f cmd/gen/conditions/*_conditions.go
rm -f cmd/gen/conditions/tests/**/badaas-orm.go

.PHONY: test_unit
1 change: 1 addition & 0 deletions cli/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add generation of docker and configuration files
- Add gen conditions to generate the conditions for the badaas' compilable query system.
- Add support for operators in condition generation.
- Add preload conditions generation.

[unreleased]: https://github.com/ditrit/badaas-orm/cli/blob/main/changelog.md#unreleased
6 changes: 6 additions & 0 deletions cli/cmd/gen/conditions/codeGenerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package conditions

type CodeGenerator[T any] interface {
Into(file *File) error
ForEachField(file *File, fields []Field) []T
}
198 changes: 147 additions & 51 deletions cli/cmd/gen/conditions/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,39 @@ import (

const (
// badaas/orm/condition.go
badaasORMCondition = "Condition"
badaasORMFieldCondition = "FieldCondition"
badaasORMWhereCondition = "WhereCondition"
badaasORMJoinCondition = "JoinCondition"
badaasORMCondition = "Condition"
badaasORMFieldCondition = "FieldCondition"
badaasORMWhereCondition = "WhereCondition"
badaasORMJoinCondition = "JoinCondition"
badaasORMIJoinCondition = "IJoinCondition"
badaasORMFieldIdentifier = "FieldIdentifier"
badaasORMNewCollectionPreload = "NewCollectionPreloadCondition"
IDFieldID = "IDFieldID"
CreatedAtFieldID = "CreatedAtFieldID"
UpdatedAtFieldID = "UpdatedAtFieldID"
DeletedAtFieldID = "DeletedAtFieldID"
// badaas/orm/operator.go
badaasORMOperator = "Operator"
// badaas/orm/baseModels.go
uIntID = "UIntID"
uuid = "UUID"
uuidModel = "UUIDModel"
uIntModel = "UIntModel"
)

var constantFieldIdentifiers = map[string]*jen.Statement{
"ID": jen.Qual(badaasORMPath, IDFieldID),
"CreatedAt": jen.Qual(badaasORMPath, CreatedAtFieldID),
"UpdatedAt": jen.Qual(badaasORMPath, UpdatedAtFieldID),
"DeletedAt": jen.Qual(badaasORMPath, DeletedAtFieldID),
}

type Condition struct {
codes []jen.Code
param *JenParam
destPkg string
codes []jen.Code
param *JenParam
destPkg string
fieldIdentifier string
preloadName string
}

func NewCondition(destPkg string, objectType Type, field Field) *Condition {
Expand Down Expand Up @@ -86,11 +107,8 @@ func (condition *Condition) generateForSlice(objectType Type, field Field) {
// slice of named types (user defined types)
_, err := field.Type.BadaasModelStruct()
if err == nil {
// slice of Badaas models -> hasMany relation
condition.generateInverseJoin(
objectType,
field,
)
// field is a Badaas Model
condition.generateCollectionPreload(objectType, field)
}
case *types.Pointer:
// slice of pointers, generate code for a slice of the pointed type
Expand All @@ -117,9 +135,10 @@ func (condition *Condition) generateForNamedType(objectType Type, field Field) {
objectType,
field,
)
} else if field.Type.IsGormCustomType() || field.TypeString() == "time.Time" {
} else if field.Type.IsGormCustomType() || field.TypeString() == "time.Time" || field.IsModelID() {
// field is a Gorm Custom type (implements Scanner and Valuer interfaces)
// or a named type supported by gorm (time.Time)
// or a badaas-orm id (uuid or uintid)
condition.param.ToCustomType(condition.destPkg, field.Type)
condition.generateWhere(
objectType,
Expand All @@ -132,24 +151,19 @@ func (condition *Condition) generateForNamedType(objectType Type, field Field) {

// Generate condition between object and field when the field is a Badaas Model
func (condition *Condition) generateForBadaasModel(objectType Type, field Field) {
hasFK, _ := objectType.HasFK(field)
if hasFK {
// belongsTo relation
_, err := objectType.GetFK(field)
if err == nil {
// has the fk -> belongsTo relation
condition.generateJoinWithFK(
objectType,
field,
)
} else {
// hasOne relation
// has not the fk -> hasOne relation
condition.generateJoinWithoutFK(
objectType,
field,
)

condition.generateInverseJoin(
objectType,
field,
)
}
}

Expand All @@ -176,20 +190,12 @@ func (condition *Condition) generateWhere(objectType Type, field Field) {
conditionName := getConditionName(objectType, field)
log.Logger.Debugf("Generated %q", conditionName)

conditionValues := jen.Dict{
jen.Id("Operator"): jen.Id("operator"),
}
columnName := field.getColumnName()
var fieldIdentifier *jen.Statement

if columnName != "" {
conditionValues[jen.Id("Column")] = jen.Lit(columnName)
if constantFieldIdentifier, ok := constantFieldIdentifiers[field.Name]; ok {
fieldIdentifier = constantFieldIdentifier
} else {
conditionValues[jen.Id("Field")] = jen.Lit(field.Name)
}

columnPrefix := field.ColumnPrefix
if columnPrefix != "" {
conditionValues[jen.Id("ColumnPrefix")] = jen.Lit(columnPrefix)
fieldIdentifier = condition.createFieldIdentifier(field, conditionName)
}

condition.codes = append(
Expand All @@ -202,22 +208,48 @@ func (condition *Condition) generateWhere(objectType Type, field Field) {
whereCondition,
).Block(
jen.Return(
fieldCondition.Clone().Values(conditionValues),
fieldCondition.Clone().Values(jen.Dict{
jen.Id("Operator"): jen.Id("operator"),
jen.Id("FieldIdentifier"): fieldIdentifier,
}),
),
),
)
}

// Generate a inverse JoinCondition, so from the field's object to the object
func (condition *Condition) generateInverseJoin(objectType Type, field Field) {
condition.generateJoinWithFK(
field.Type,
Field{
Name: objectType.Name(),
Type: objectType,
Tags: field.Tags,
},
// create a variable containing the definition of the field identifier
// to use it in the where condition and in the preload condition
func (condition *Condition) createFieldIdentifier(field Field, conditionName string) *jen.Statement {
fieldIdentifierValues := jen.Dict{}
columnName := field.getColumnName()

if columnName != "" {
fieldIdentifierValues[jen.Id("Column")] = jen.Lit(columnName)
} else {
fieldIdentifierValues[jen.Id("Field")] = jen.Lit(field.Name)
}

columnPrefix := field.ColumnPrefix
if columnPrefix != "" {
fieldIdentifierValues[jen.Id("ColumnPrefix")] = jen.Lit(columnPrefix)
}

fieldIdentifierVar := jen.Qual(
badaasORMPath, badaasORMFieldIdentifier,
).Values(fieldIdentifierValues)

fieldIdentifierName := strcase.ToCamel(conditionName) + "FieldID"

condition.codes = append(
condition.codes,
jen.Var().Id(
fieldIdentifierName,
).Op("=").Add(fieldIdentifierVar),
)

condition.fieldIdentifier = fieldIdentifierName

return jen.Qual("", fieldIdentifierName)
}

// Generate a JoinCondition between the object and field's object
Expand Down Expand Up @@ -258,8 +290,8 @@ func (condition *Condition) generateJoin(objectType Type, field Field, t1Field,
conditionName := getConditionName(objectType, field)
log.Logger.Debugf("Generated %q", conditionName)

ormT1Condition := jen.Qual(
badaasORMPath, badaasORMCondition,
ormT1IJoinCondition := jen.Qual(
badaasORMPath, badaasORMIJoinCondition,
).Types(t1)
ormT2Condition := jen.Qual(
badaasORMPath, badaasORMCondition,
Expand All @@ -277,22 +309,86 @@ func (condition *Condition) generateJoin(objectType Type, field Field, t1Field,
).Params(
jen.Id("conditions").Op("...").Add(ormT2Condition),
).Add(
ormT1Condition,
ormT1IJoinCondition,
).Block(
jen.Return(
ormJoinCondition.Values(jen.Dict{
jen.Id("T1Field"): jen.Lit(t1Field),
jen.Id("T2Field"): jen.Lit(t2Field),
jen.Id("Conditions"): jen.Id("conditions"),
jen.Id("T1Field"): jen.Lit(t1Field),
jen.Id("T2Field"): jen.Lit(t2Field),
jen.Id("RelationField"): jen.Lit(field.Name),
jen.Id("Conditions"): jen.Id("conditions"),
jen.Id("T1PreloadCondition"): jen.Id(getPreloadAttributesName(objectType.Name())),
}),
),
),
)

// preload for the relation
preloadName := getPreloadRelationName(objectType, field)
condition.codes = append(
condition.codes,
jen.Var().Id(
preloadName,
).Op("=").Add(jen.Id(conditionName)).Call(
jen.Id(getPreloadAttributesName(field.TypeName())),
),
)
condition.preloadName = preloadName
}

func getPreloadRelationName(objectType Type, field Field) string {
return objectType.Name() + "Preload" + field.Name
}

func (condition *Condition) generateCollectionPreload(objectType Type, field Field) {
t1 := jen.Qual(
getRelativePackagePath(condition.destPkg, objectType),
objectType.Name(),
)

t2 := jen.Qual(
getRelativePackagePath(condition.destPkg, field.Type),
field.TypeName(),
)

ormT1Condition := jen.Qual(
badaasORMPath, badaasORMCondition,
).Types(t1)
ormT2IJoinCondition := jen.Qual(
badaasORMPath, badaasORMIJoinCondition,
).Types(t2)
ormNewCollectionPreload := jen.Qual(
badaasORMPath, badaasORMNewCollectionPreload,
).Types(
t1, t2,
)

preloadName := getPreloadRelationName(objectType, field)

condition.codes = append(
condition.codes,
jen.Func().Id(
preloadName,
).Params(
jen.Id("nestedPreloads").Op("...").Add(ormT2IJoinCondition),
).Add(
ormT1Condition,
).Block(
jen.Return(
ormNewCollectionPreload.Call(
jen.Lit(field.Name),
jen.Id("nestedPreloads"),
),
),
),
)

condition.preloadName = preloadName + "()"
}

// Generate condition names
func getConditionName(typeV Type, field Field) string {
return typeV.Name() + strcase.ToPascal(field.ColumnPrefix) + strcase.ToPascal(field.Name)
return typeV.Name() + strcase.ToPascal(field.NamePrefix) + strcase.ToPascal(field.Name)
}

// Avoid importing the same package as the destination one
Expand Down
Loading

0 comments on commit 826a8e5

Please sign in to comment.