From 6e63bc12119a8c4e94325e2102265dc276eb7877 Mon Sep 17 00:00:00 2001 From: Harsh Patel Date: Thu, 21 Dec 2023 13:06:51 +0530 Subject: [PATCH] chore: add ci workflow to test and lint (#13) * feat: add wrapper function to support MongoDB transactions * doc: mark transactions feature as done in future scope section * fix: support multi tenancy * doc: fix mgod usage example in README * doc: multi tenancy advanced guide * chore: add ci workflow to test and lint * fix: remove slog usage * chore: bump monogdb requirement from 3.6 -> 4.4 * fix: add support from mongodb 3.6 with warning note * doc: refactor docs -> limitations * fix: remove slog usage * style: escape less than in installtion markdown file * chore: limit test workflow trigger for commits against main branch only --- .github/workflows/test-module.yml | 77 ++++++++++++++++++++++++++++ README.md | 4 ++ bsondoc/build_bson_doc.go | 29 ++++++----- docs/installation.md | 5 ++ errors/errors.go | 10 ++++ schema/entity_model_schema_cache.go | 5 +- schema/fieldopt/default_value_opt.go | 7 ++- transaction.go | 2 - 8 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/test-module.yml diff --git a/.github/workflows/test-module.yml b/.github/workflows/test-module.yml new file mode 100644 index 0000000..ab154f6 --- /dev/null +++ b/.github/workflows/test-module.yml @@ -0,0 +1,77 @@ +name: Lint & Test + +on: + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + - 'website/**' + push: + branches: + - main + paths-ignore: + - 'docs/**' + - 'website/**' + +jobs: + lint-module: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: 1.18.x + + - name: Setup cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go-${{ matrix.go-version }}- + + - name: Run linter + uses: golangci/golangci-lint-action@v3 + with: + version: v1.54 + + test-module: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: ['1.18', '1.19', '1.20', '1.21'] + mongodb-version: ['4.4', '5.0', '6.0', '7.0'] # add 3.6 as well after fixing the tests (collection creation) + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Start MongoDB Replica Set + uses: supercharge/mongodb-github-action@v1.10.0 + with: + mongodb-version: ${{ matrix.mongodb-version }} + mongodb-replica-set: replset + mongodb-db: mgoddb + + - name: Setup cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go-${{ matrix.go-version }}- + + - name: Run tests + run: go test ./... \ No newline at end of file diff --git a/README.md b/README.md index 5b2230b..b8209be 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ - Go 1.18 or higher. - MongoDB 3.6 and higher. +> [!WARNING] +> For MongoDB version **<4.4**, please create the collection in MongoDB before creating an `EntityMongoModel` using `mgod` for the same. +> Refer to [this MongoDB limitations](https://www.mongodb.com/docs/manual/reference/limits/#operations) for more information. + ## Installation ``` go get github.com/Lyearn/mgod diff --git a/bsondoc/build_bson_doc.go b/bsondoc/build_bson_doc.go index d1da554..5926217 100644 --- a/bsondoc/build_bson_doc.go +++ b/bsondoc/build_bson_doc.go @@ -4,7 +4,6 @@ package bsondoc import ( "context" "fmt" - "log/slog" "github.com/Lyearn/mgod/errors" "github.com/Lyearn/mgod/schema" @@ -33,7 +32,6 @@ func Build( } if bsonDoc == nil && len(entityModelSchema.Root.Children) != 0 { - slog.ErrorContext(ctx, "BSON doc is nil but entity model schema is not empty") return errors.NewBadRequestError(errors.BadRequestError{ Underlying: "bson doc", Got: "nil", @@ -64,7 +62,7 @@ func build( return nil } - schemaNode, err := getSchemaNodeForPath(ctx, parent, schemaNodes, translateTo) + schemaNode, err := getSchemaNodeForPath(parent, schemaNodes, translateTo) if err != nil { return err } else if schemaNode == nil { @@ -100,7 +98,7 @@ func build( uniqVisitedSchemaNodes := lo.Uniq(visitedSchemaNodes) if len(uniqVisitedSchemaNodes) != len(immediateChildren) { - err := addMissingNodes(ctx, bsonElem, immediateChildren, uniqVisitedSchemaNodes, schemaNodes, translateTo) + err := addMissingNodes(bsonElem, immediateChildren, uniqVisitedSchemaNodes, schemaNodes, translateTo) if err != nil { return err } @@ -157,7 +155,10 @@ func build( case TranslateToEnumEntityModel: modifiedBSONNodeVal, err = transformer.TransformForEntityModelDoc(elemVal) default: - err = fmt.Errorf("unknown translateTo enum value %s", translateTo) + err = errors.NewBadRequestError(errors.BadRequestError{ + Underlying: "translateTo enum", + Got: string(translateTo), + }) } if err != nil { @@ -220,7 +221,6 @@ func getConvertedValueForNode( // addMissingNodes appends missing nodes in bson doc which have default value. func addMissingNodes( - ctx context.Context, bsonElem *bson.D, immediateChildren []string, uniqVisitedSchemaNodes []string, @@ -229,7 +229,7 @@ func addMissingNodes( ) error { missingSchemaPaths, _ := lo.Difference(immediateChildren, uniqVisitedSchemaNodes) for _, missingSchemaPath := range missingSchemaPaths { - missingSchemaNode, err := getSchemaNodeForPath(ctx, missingSchemaPath, schemaNodes, translateTo) + missingSchemaNode, err := getSchemaNodeForPath(missingSchemaPath, schemaNodes, translateTo) if err != nil { return err } else if missingSchemaNode == nil { @@ -245,7 +245,11 @@ func addMissingNodes( // throw error if schema node is not _id field (special field) and is required but has no default value. if !isIDField && missingSchemaNode.Props.Options.Default == nil { - return fmt.Errorf("required field at path %s is missing in bson doc", missingSchemaPath) + return errors.NewBadRequestError(errors.BadRequestError{ + Underlying: "bson doc", + Got: "nil", + Expected: fmt.Sprintf("field at path - %s", missingSchemaPath), + }) } var bsonNodeToAppend bson.E @@ -278,7 +282,6 @@ func addMissingNodes( } func getSchemaNodeForPath( - ctx context.Context, path string, schemaNodes map[string]*schema.TreeNode, translateTo TranslateToEnum, @@ -291,10 +294,10 @@ func getSchemaNodeForPath( return nil, nil } - slog.ErrorContext(ctx, fmt.Sprintf( - "schema doesn't contains any node at path %s found in bsonDoc", path)) - - return nil, fmt.Errorf("unknown path %s found in bson doc", path) + return nil, errors.NewNotFoundError(errors.NotFoundError{ + Underlying: "bson doc", + Value: fmt.Sprintf("path - %s", path), + }) } return schemaNode, nil diff --git a/docs/installation.md b/docs/installation.md index fcbc528..fabf9de 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -7,6 +7,11 @@ title: Installation - Go 1.18 or higher - MongoDB 3.6 and higher +:::warning +For MongoDB version **\<4.4**, please create the collection in MongoDB before creating an `EntityMongoModel` using `mgod` for the same. +Refer to [this MongoDB limitations](https://www.mongodb.com/docs/manual/reference/limits/#operations) for more information. +::: + ## Installation ```bash diff --git a/errors/errors.go b/errors/errors.go index cea1f4f..97cdca2 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -19,6 +19,16 @@ func NewBadRequestError(e BadRequestError) Error { return Error(fmt.Sprintf("%s: expected %s, got %s", e.Underlying, e.Expected, e.Got)) } +type NotFoundError struct { + Value string + Underlying string +} + +func NewNotFoundError(e NotFoundError) Error { + return Error(fmt.Sprintf("%s not found for %s", e.Value, e.Underlying)) +} + const ( ErrNoDatabaseConnection = Error("no database connection") + ErrSchemaNotCached = Error("schema not cached") ) diff --git a/schema/entity_model_schema_cache.go b/schema/entity_model_schema_cache.go index 43768ab..8afd2a7 100644 --- a/schema/entity_model_schema_cache.go +++ b/schema/entity_model_schema_cache.go @@ -1,8 +1,9 @@ package schema import ( - "fmt" "sync" + + "github.com/Lyearn/mgod/errors" ) // EntityModelSchemaCache is the cache implementation than can hold [EntityModelSchema]. @@ -31,7 +32,7 @@ func (c *entityModelSchemaCache) GetSchema(schemaName string) (*EntityModelSchem return schema, nil } - return nil, fmt.Errorf("%s schema not found in cache", schemaName) + return nil, errors.ErrSchemaNotCached } func (c *entityModelSchemaCache) SetSchema(schemaName string, schema *EntityModelSchema) { diff --git a/schema/fieldopt/default_value_opt.go b/schema/fieldopt/default_value_opt.go index 73f2766..1f3aa33 100644 --- a/schema/fieldopt/default_value_opt.go +++ b/schema/fieldopt/default_value_opt.go @@ -5,6 +5,7 @@ import ( "reflect" "strconv" + "github.com/Lyearn/mgod/errors" "go.mongodb.org/mongo-driver/bson" ) @@ -73,6 +74,10 @@ func (o defaultValueOption) GetValue(field reflect.StructField) (interface{}, er return bson.A{}, nil default: - return nil, fmt.Errorf("unsupported type %v", fieldType) + return nil, errors.NewBadRequestError(errors.BadRequestError{ + Underlying: "default value option", + Got: fmt.Sprintf("%v", fieldType), + Expected: "string, int, float32, float64, bool, slice or array", + }) } } diff --git a/transaction.go b/transaction.go index bd5c248..812bc61 100644 --- a/transaction.go +++ b/transaction.go @@ -2,7 +2,6 @@ package mgod import ( "context" - "log/slog" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -18,7 +17,6 @@ type TransactionFunc func(sc mongo.SessionContext) (interface{}, error) func WithTransaction(ctx context.Context, transactionFunc TransactionFunc) (interface{}, error) { session, err := mClient.StartSession() if err != nil { - slog.ErrorContext(ctx, "Error occurred during WithTransaction", err) return nil, err } defer session.EndSession(ctx)