diff --git a/.github/ISSUE_TEMPLATE/tests.md b/.github/ISSUE_TEMPLATE/tests.md new file mode 100644 index 0000000..f0e6f37 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tests.md @@ -0,0 +1,14 @@ +--- +name: Tests +about: Suggest the tests for the project +title: "[TESTS]" +labels: tests +--- +**Describe what parts of code requires tests** +List the package names/ functions / methods that requires to be tested. + +**Define the test name skeletons** +Point defined test name skeletons. + +**Additional Context** +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/simple.md b/.github/PULL_REQUEST_TEMPLATE/simple.md new file mode 100644 index 0000000..895983c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/simple.md @@ -0,0 +1,15 @@ +--- +name: Simple PR with Checklist +about: Create a pull request with the default Checklist +--- +### Description +Please explain the changes you made here. + +### Checklist +- [ ] Code compiles correctly +- [ ] Created tests which fail without the change (if possible) +- [ ] All tests passing +- [ ] Run `golangci-lint run ./... --build-tags integrate` or `golangci-lint run ./...` without failure +- [ ] Updated Go modules, if necessary +- [ ] Extended the README / documentation, if necessary +- [ ] Added myself / the copyright holder to the AUTHORS file \ No newline at end of file diff --git a/config/processor.go b/config/processor.go index e5ed5d5..b5a0a0f 100644 --- a/config/processor.go +++ b/config/processor.go @@ -2,10 +2,11 @@ package config import ( "errors" - "github.com/spf13/viper" "strings" "time" + "github.com/spf13/viper" + "github.com/neuronlabs/neuron-core/internal" ) @@ -75,6 +76,7 @@ func defaultThreadsafeProcessorConfig() map[string]interface{} { internal.ProcessTxBegin, internal.ProcessHookBeforeCreate, internal.ProcessSetBelongsToRelations, + internal.ProcessSetCreatedAt, internal.ProcessCreate, internal.ProcessStoreScopePrimaries, internal.ProcessPatchForeignRelationsSafe, @@ -114,6 +116,7 @@ func defaultThreadsafeProcessorConfig() map[string]interface{} { internal.ProcessDeletedAtFilter, internal.ProcessReducePrimaryFilters, internal.ProcessPatchBelongsToRelations, + internal.ProcessSetUpdatedAt, internal.ProcessPatch, internal.ProcessPatchForeignRelationsSafe, internal.ProcessHookAfterPatch, @@ -124,6 +127,7 @@ func defaultThreadsafeProcessorConfig() map[string]interface{} { internal.ProcessReducePrimaryFilters, internal.ProcessHookBeforeDelete, internal.ProcessReducePrimaryFilters, + internal.ProcessSetDeletedAt, internal.ProcessDelete, internal.ProcessDeleteForeignRelationsSafe, internal.ProcessHookAfterDelete, @@ -179,6 +183,7 @@ func defaultConcurrentProcessorConfig() map[string]interface{} { internal.ProcessTxBegin, internal.ProcessHookBeforeCreate, internal.ProcessSetBelongsToRelations, + internal.ProcessSetCreatedAt, internal.ProcessCreate, internal.ProcessStoreScopePrimaries, internal.ProcessPatchForeignRelations, @@ -216,6 +221,7 @@ func defaultConcurrentProcessorConfig() map[string]interface{} { internal.ProcessDeletedAtFilter, internal.ProcessReducePrimaryFilters, internal.ProcessPatchBelongsToRelations, + internal.ProcessSetUpdatedAt, internal.ProcessPatch, internal.ProcessPatchForeignRelations, internal.ProcessHookAfterPatch, @@ -226,6 +232,7 @@ func defaultConcurrentProcessorConfig() map[string]interface{} { internal.ProcessReducePrimaryFilters, internal.ProcessHookBeforeDelete, internal.ProcessReducePrimaryFilters, + internal.ProcessSetDeletedAt, internal.ProcessDelete, internal.ProcessDeleteForeignRelations, internal.ProcessHookAfterDelete, diff --git a/internal/flags.go b/internal/flags.go deleted file mode 100644 index 33fc403..0000000 --- a/internal/flags.go +++ /dev/null @@ -1,20 +0,0 @@ -package internal - -// TODO: clear the flags -// // ScopeCtxFlags - flags settable on scope, endpoint, model handler and controller -// var ScopeCtxFlags = []uint{ -// flags.AddMetaCountList, -// flags.UseLinks, -// flags.ReturnPatchContent, -// } - -// // ModelCtxFlags - flags settable for model handler and controller -// var ModelCtxFlags = []uint{ -// flags.AllowClientID, -// } - -// // ControllerCtxFlags - flags settable only for the needs of the controller -// var ControllerCtxFlags = []uint{ -// flags.AllowForeignKeyFilter, -// flags.UseFilterValueLimit, -// } diff --git a/internal/processes.go b/internal/processes.go index e512a77..b3b57c4 100644 --- a/internal/processes.go +++ b/internal/processes.go @@ -4,6 +4,7 @@ package internal var Processes = map[string]struct{}{ ProcessHookBeforeCreate: struct{}{}, ProcessSetBelongsToRelations: struct{}{}, + ProcessSetCreatedAt: struct{}{}, ProcessCreate: struct{}{}, ProcessStoreScopePrimaries: struct{}{}, ProcessPatchForeignRelations: struct{}{}, @@ -24,12 +25,14 @@ var Processes = map[string]struct{}{ ProcessHookAfterList: struct{}{}, ProcessGetIncluded: struct{}{}, ProcessGetIncludedSafe: struct{}{}, + ProcessSetUpdatedAt: struct{}{}, ProcessHookBeforePatch: struct{}{}, ProcessPatch: struct{}{}, ProcessHookAfterPatch: struct{}{}, ProcessPatchBelongsToRelations: struct{}{}, ProcessReducePrimaryFilters: struct{}{}, ProcessHookBeforeDelete: struct{}{}, + ProcessSetDeletedAt: struct{}{}, ProcessDelete: struct{}{}, ProcessHookAfterDelete: struct{}{}, ProcessDeleteForeignRelations: struct{}{}, @@ -46,6 +49,7 @@ const ( // Create processes ProcessHookBeforeCreate = "hook_before_create" ProcessSetBelongsToRelations = "set_belongs_to_relations" + ProcessSetCreatedAt = "set_created_at" ProcessCreate = "create" ProcessStoreScopePrimaries = "store_scope_primaries" ProcessPatchForeignRelations = "patch_foreign_relations" @@ -73,6 +77,7 @@ const ( // Patch processes ProcessHookBeforePatch = "hook_before_patch" + ProcessSetUpdatedAt = "set_updated_at" ProcessPatch = "patch" ProcessHookAfterPatch = "hook_after_patch" ProcessPatchBelongsToRelations = "patch_belongs_to_relations" @@ -80,6 +85,7 @@ const ( // Delete processes ProcessReducePrimaryFilters = "reduce_primary_filters" ProcessHookBeforeDelete = "hook_before_delete" + ProcessSetDeletedAt = "set_deleted_at" ProcessDelete = "delete" ProcessHookAfterDelete = "hook_after_delete" ProcessDeleteForeignRelations = "delete_foreign_relations" diff --git a/query/const.go b/query/const.go index 461d129..6854674 100644 --- a/query/const.go +++ b/query/const.go @@ -41,6 +41,7 @@ const ( const ( ProcessHookBeforeCreate = internal.ProcessHookBeforeCreate ProcessSetBelongsToRelations = internal.ProcessSetBelongsToRelations + ProcessSetCreatedAt = internal.ProcessSetCreatedAt ProcessCreate = internal.ProcessCreate ProcessStoreScopePrimaries = internal.ProcessStoreScopePrimaries ProcessPatchForeignRelations = internal.ProcessPatchForeignRelations @@ -65,12 +66,14 @@ const ( ProcessGetIncludedSafe = internal.ProcessGetIncludedSafe ProcessHookBeforePatch = internal.ProcessHookBeforePatch + ProcessSetUpdatedAt = internal.ProcessSetUpdatedAt ProcessPatch = internal.ProcessPatch ProcessHookAfterPatch = internal.ProcessHookAfterPatch ProcessPatchBelongsToRelations = internal.ProcessPatchBelongsToRelations ProcessReducePrimaryFilters = internal.ProcessReducePrimaryFilters ProcessHookBeforeDelete = internal.ProcessHookBeforeDelete + ProcessSetDeletedAt = internal.ProcessSetDeletedAt ProcessDelete = internal.ProcessDelete ProcessHookAfterDelete = internal.ProcessHookAfterDelete ProcessDeleteForeignRelations = internal.ProcessDeleteForeignRelations diff --git a/query/process-create.go b/query/process-create.go index a18cb61..f50a571 100644 --- a/query/process-create.go +++ b/query/process-create.go @@ -29,40 +29,6 @@ func createFunc(ctx context.Context, s *Scope) error { return errors.NewDet(class.RepositoryNotImplementsCreator, "repository doesn't implement Creator interface") } - // Set created at field if possible - createdAtField, ok := s.Struct().CreatedAt() - if ok { - // by default scope has auto selected fields setCreatedAt should be true - setCreatedAt := s.autosetFields - if !setCreatedAt { - _, found := s.Fieldset[createdAtField.NeuronName()] - // if the fields were not auto selected check if the field is selected by user - setCreatedAt = !found - } - - if setCreatedAt { - // Check if the value of the created at field is not already set by the user. - v := reflect.ValueOf(s.Value).Elem().FieldByIndex(createdAtField.ReflectField().Index) - - if s.autosetFields { - setCreatedAt = reflect.DeepEqual(v.Interface(), reflect.Zero(createdAtField.ReflectField().Type).Interface()) - } - - if setCreatedAt { - switch { - case createdAtField.IsTimePointer(): - tv := time.Now() - v.Set(reflect.ValueOf(&tv)) - case createdAtField.IsTime(): - v.Set(reflect.ValueOf(time.Now())) - } - - s.Fieldset[createdAtField.NeuronName()] = createdAtField - - } - } - } - if err := creator.Create(ctx, s); err != nil { return err } @@ -123,3 +89,45 @@ func storeScopePrimaries(ctx context.Context, s *Scope) error { return nil } + +func setCreatedAtField(ctx context.Context, s *Scope) error { + if s.Error != nil { + return nil + } + + // Set created at field if possible + createdAtField, ok := s.Struct().CreatedAt() + if !ok { + return nil + } + // by default scope has auto selected fields setCreatedAt should be true + setCreatedAt := s.autosetFields + if !setCreatedAt { + _, found := s.Fieldset[createdAtField.NeuronName()] + // if the fields were not auto selected check if the field is selected by user + setCreatedAt = !found + } + + if !setCreatedAt { + return nil + } + // Check if the value of the created at field is not already set by the user. + v := reflect.ValueOf(s.Value).Elem().FieldByIndex(createdAtField.ReflectField().Index) + + if s.autosetFields { + setCreatedAt = reflect.DeepEqual(v.Interface(), reflect.Zero(createdAtField.ReflectField().Type).Interface()) + } + + if setCreatedAt { + switch { + case createdAtField.IsTimePointer(): + tv := time.Now() + v.Set(reflect.ValueOf(&tv)) + case createdAtField.IsTime(): + v.Set(reflect.ValueOf(time.Now())) + } + + s.Fieldset[createdAtField.NeuronName()] = createdAtField + } + return nil +} diff --git a/query/process-delete.go b/query/process-delete.go index 37c1a50..07f8467 100644 --- a/query/process-delete.go +++ b/query/process-delete.go @@ -25,14 +25,8 @@ func deleteFunc(ctx context.Context, s *Scope) error { return err } - deletedAt, hasDeletedAt := s.Struct().DeletedAt() + _, hasDeletedAt := s.Struct().DeletedAt() if hasDeletedAt { - s.Fieldset[deletedAt.NeuronName()] = deletedAt - - v := reflect.ValueOf(s.Value).Elem().FieldByIndex(deletedAt.ReflectField().Index) - t := time.Now() - v.Set(reflect.ValueOf(&t)) - patcher, ok := repo.(Patcher) if !ok { log.Warningf("Repository for model: '%s' doesn't implement Patcher interface", s.Struct().Type()) @@ -506,3 +500,20 @@ func reducePrimaryFilters(ctx context.Context, s *Scope) error { return nil } + +func setDeletedAtField(ctx context.Context, s *Scope) error { + if s.Error != nil { + return nil + } + deletedAt, hasDeletedAt := s.Struct().DeletedAt() + if !hasDeletedAt { + return nil + } + + v := reflect.ValueOf(s.Value).Elem().FieldByIndex(deletedAt.ReflectField().Index) + t := time.Now() + v.Set(reflect.ValueOf(&t)) + + s.Fieldset[deletedAt.NeuronName()] = deletedAt + return nil +} diff --git a/query/process-patch.go b/query/process-patch.go index 9feae63..ebbc155 100644 --- a/query/process-patch.go +++ b/query/process-patch.go @@ -31,15 +31,8 @@ func patchFunc(ctx context.Context, s *Scope) error { return errors.NewDetf(class.RepositoryNotImplementsPatcher, "repository: '%T' doesn't implement Patcher interface", repo) } - updatedAt, hasUpdatedAt := s.Struct().UpdatedAt() - // if there are any selected fields that are not a foreign relationships - // (attributes, foreign keys etc, relationship-belongs-to...) do the patch process - if !hasUpdatedAt && len(s.Fieldset) == 0 { - return errors.NewDet(class.QuerySelectedFieldsNotSelected, "no fields selected for patch process") - } - + _, hasUpdatedAt := s.Struct().UpdatedAt() onlyForeignRelationships := true - var updatedAtSelected bool for _, selected := range s.Fieldset { if selected.IsPrimary() { if len(s.Fieldset) == 1 { @@ -56,35 +49,9 @@ func patchFunc(ctx context.Context, s *Scope) error { } else if selected.Relationship().Kind() == mapping.RelBelongsTo { onlyForeignRelationships = false } - - if hasUpdatedAt && selected == updatedAt { - updatedAtSelected = true - } } if !onlyForeignRelationships || hasUpdatedAt { - if hasUpdatedAt { - v := reflect.ValueOf(s.Value).Elem().FieldByIndex(updatedAt.ReflectField().Index) - - var setUpdatedAt bool - if s.autosetFields { - setUpdatedAt = reflect.DeepEqual(v.Interface(), reflect.Zero(updatedAt.ReflectField().Type).Interface()) - } else { - setUpdatedAt = !updatedAtSelected - } - if setUpdatedAt { - t := time.Now() - switch { - case updatedAt.IsTimePointer(): - v.Set(reflect.ValueOf(&t)) - case updatedAt.IsTime(): - v.Set(reflect.ValueOf(t)) - } - if !updatedAtSelected { - s.Fieldset[updatedAt.NeuronName()] = updatedAt - } - } - } if log.Level().IsAllowed(log.LDEBUG3) { log.Debug3f("SCOPE[%s][%s] patching: %s", s.ID().String(), s.Struct().Collection(), s.String()) } @@ -925,3 +892,34 @@ func patchClearRelationshipWithForeignKey(ctx context.Context, s *Scope, relFiel } return nil } + +func setUpdatedAtField(ctx context.Context, s *Scope) error { + if s.Error != nil { + return nil + } + + updatedAt, hasUpdatedAt := s.Struct().UpdatedAt() + // if there are any selected fields that are not a foreign relationships + // (attributes, foreign keys etc, relationship-belongs-to...) do the patch process + if !hasUpdatedAt && len(s.Fieldset) == 0 { + return errors.NewDet(class.QuerySelectedFieldsNotSelected, "no fields selected for patch process") + } + if !hasUpdatedAt { + return nil + } + v := reflect.ValueOf(s.Value).Elem().FieldByIndex(updatedAt.ReflectField().Index) + + if !reflect.DeepEqual(v.Interface(), reflect.Zero(updatedAt.ReflectField().Type).Interface()) { + return nil + } + + t := time.Now() + switch { + case updatedAt.IsTimePointer(): + v.Set(reflect.ValueOf(&t)) + case updatedAt.IsTime(): + v.Set(reflect.ValueOf(t)) + } + s.Fieldset[updatedAt.NeuronName()] = updatedAt + return nil +} diff --git a/query/processor.go b/query/processor.go index 585d8ef..cc346fd 100644 --- a/query/processor.go +++ b/query/processor.go @@ -34,6 +34,7 @@ func init() { // create processes registerQueryProcess(ProcessHookBeforeCreate, beforeCreateFunc) registerQueryProcess(ProcessSetBelongsToRelations, setBelongsToRelationshipsFunc) + registerQueryProcess(ProcessSetCreatedAt, setCreatedAtField) registerQueryProcess(ProcessCreate, createFunc) registerQueryProcess(ProcessStoreScopePrimaries, storeScopePrimaries) registerQueryProcess(ProcessPatchForeignRelations, patchForeignRelationshipsFunc) @@ -61,6 +62,7 @@ func init() { // Patch registerQueryProcess(ProcessHookBeforePatch, beforePatchFunc) + registerQueryProcess(ProcessSetUpdatedAt, setUpdatedAtField) registerQueryProcess(ProcessPatch, patchFunc) registerQueryProcess(ProcessHookAfterPatch, afterPatchFunc) registerQueryProcess(ProcessPatchBelongsToRelations, patchBelongsToRelationshipsFunc) @@ -68,6 +70,7 @@ func init() { // Delete registerQueryProcess(ProcessReducePrimaryFilters, reducePrimaryFilters) registerQueryProcess(ProcessHookBeforeDelete, beforeDeleteFunc) + registerQueryProcess(ProcessSetDeletedAt, setDeletedAtField) registerQueryProcess(ProcessDelete, deleteFunc) registerQueryProcess(ProcessHookAfterDelete, afterDeleteFunc) registerQueryProcess(ProcessDeleteForeignRelations, deleteForeignRelationshipsFunc)