From f77aa3590169cd0bedb84f01c16f3a9383a63e17 Mon Sep 17 00:00:00 2001 From: olxandr Date: Sat, 5 Jul 2025 13:37:22 +0300 Subject: [PATCH 01/22] init couchbase integration with basic methods and mocks --- go.mod | 8 + go.sum | 16 + go.work.sum | 34 +- pkg/gofr/container/container.go | 1 + pkg/gofr/container/datasources.go | 37 ++ pkg/gofr/datasource/couchbase/couchbase.go | 340 +++++++++++++++ .../datasource/couchbase/couchbase_test.go | 56 +++ pkg/gofr/datasource/couchbase/interfaces.go | 34 ++ pkg/gofr/datasource/couchbase/logger.go | 42 ++ pkg/gofr/datasource/couchbase/metrics.go | 11 + .../datasource/couchbase/mock_interfaces.go | 392 ++++++++++++++++++ pkg/gofr/datasource/couchbase/mock_logger.go | 107 +++++ pkg/gofr/datasource/couchbase/mock_metrics.go | 75 ++++ pkg/gofr/external_db.go | 11 + 14 files changed, 1159 insertions(+), 5 deletions(-) create mode 100644 pkg/gofr/datasource/couchbase/couchbase.go create mode 100644 pkg/gofr/datasource/couchbase/couchbase_test.go create mode 100644 pkg/gofr/datasource/couchbase/interfaces.go create mode 100644 pkg/gofr/datasource/couchbase/logger.go create mode 100644 pkg/gofr/datasource/couchbase/metrics.go create mode 100644 pkg/gofr/datasource/couchbase/mock_interfaces.go create mode 100644 pkg/gofr/datasource/couchbase/mock_logger.go create mode 100644 pkg/gofr/datasource/couchbase/mock_metrics.go diff --git a/go.mod b/go.mod index 343603b2d..292742293 100644 --- a/go.mod +++ b/go.mod @@ -56,6 +56,11 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/couchbase/gocb/v2 v2.10.0 // indirect + github.com/couchbase/gocbcore/v10 v10.7.0 // indirect + github.com/couchbase/gocbcoreps v0.1.3 // indirect + github.com/couchbase/goprotostellar v1.0.2 // indirect + github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -63,6 +68,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect @@ -91,6 +97,8 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.39.0 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/net v0.41.0 // indirect diff --git a/go.sum b/go.sum index f0c17b009..c1b28a88b 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,16 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/couchbase/gocb/v2 v2.10.0 h1:NNxZ4okToU1Ylqp6F8tE41CEJQPhb2WjufryAkeubOk= +github.com/couchbase/gocb/v2 v2.10.0/go.mod h1:OSbMfQkP7ltbKiDZhsT2mGDhkQNmvGXxptKcxAUJQ2Y= +github.com/couchbase/gocbcore/v10 v10.7.0 h1:lAEi0PNeEGKOu8pWrPUdtLOT2oGr1J/UTdGHVPC3r/0= +github.com/couchbase/gocbcore/v10 v10.7.0/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI= +github.com/couchbase/gocbcoreps v0.1.3 h1:fILaKGCjxFIeCgAUG8FGmRDSpdrRggohOMKEgO9CUpg= +github.com/couchbase/gocbcoreps v0.1.3/go.mod h1:hBFpDNPnRno6HH5cRXExhqXYRmTsFJlFHQx7vztcXPk= +github.com/couchbase/goprotostellar v1.0.2 h1:yoPbAL9sCtcyZ5e/DcU5PRMOEFaJrF9awXYu3VPfGls= +github.com/couchbase/goprotostellar v1.0.2/go.mod h1:5/yqVnZlW2/NSbAWu1hPJCFBEwjxgpe0PFFOlRixnp4= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 h1:lhGOw8rNG6RAadmmaJAF3PJ7MNt7rFuWG7BHCYMgnGE= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28/go.mod h1:o7T431UOfFVHDNvMBUmUxpHnhivwv7BziUao/nMl81E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -90,6 +100,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -247,7 +259,11 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/go.work.sum b/go.work.sum index ec87d4cd5..10fdc6e15 100644 --- a/go.work.sum +++ b/go.work.sum @@ -9,7 +9,6 @@ cel.dev/expr v0.19.2 h1:V354PbqIXr9IQdwy4SYA4xa0HXaWq1BUPAGzugBY5V4= cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss= cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -719,7 +718,10 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= +<<<<<<< HEAD github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +======= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= @@ -784,6 +786,7 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +<<<<<<< HEAD github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= @@ -791,12 +794,19 @@ github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9 github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +======= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= @@ -818,7 +828,10 @@ github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrb github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +<<<<<<< HEAD github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +======= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= @@ -856,7 +869,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +<<<<<<< HEAD github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +======= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -1135,7 +1151,10 @@ go.opentelemetry.io/contrib/detectors/gcp v1.33.0 h1:FVPoXEoILwgbZUu4X7YSgsESsAm go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c9s2SOlgoSRyMpNVzUj2fZqI= go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +<<<<<<< HEAD go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +======= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= @@ -1182,10 +1201,6 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -1193,6 +1208,7 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -1284,6 +1300,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1473,6 +1490,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go. google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:W9ynFDP/shebLB1Hl/ESTOap2jHd6pmLXPNZC7SVDbA= google.golang.org/genproto/googleapis/api v0.0.0-20250227231956-55c901821b1e/go.mod h1:Xsh8gBVxGCcbV8ZeTB9wI5XPyZ5RvC6V3CTeeplHbiA= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= +google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250106144421-5f5ef82da422 h1:w6g+P/ZscmNlGxVVXGaPVQOLu1q19ubsTOZKwaDqm4k= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250106144421-5f5ef82da422/go.mod h1:s4mHJ3FfG8P6A3O+gZ8TVqB3ufjOl9UG3ANCMMwCHmo= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250124145028-65684f501c47 h1:zYSVZD88HgcYTPowSo35t8Gdxpz+SYJ1CM0Kd/yugGw= @@ -1487,7 +1506,10 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20250425173222-7b384671a google.golang.org/genproto/googleapis/bytestream v0.0.0-20250428153025-10db94c68c34/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9 h1:YI36gCL8AQMhzYN6+jH8PdV/iZ0On+Zd0rO/7lCH3k8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= +<<<<<<< HEAD google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ= +======= +>>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= @@ -1508,6 +1530,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1527,6 +1550,7 @@ google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe0 google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= diff --git a/pkg/gofr/container/container.go b/pkg/gofr/container/container.go index 89cf9917d..34bce2738 100644 --- a/pkg/gofr/container/container.go +++ b/pkg/gofr/container/container.go @@ -65,6 +65,7 @@ type Container struct { SurrealDB SurrealDB ArangoDB ArangoDB Elasticsearch Elasticsearch + Couchbase Couchbase KVStore KVStore diff --git a/pkg/gofr/container/datasources.go b/pkg/gofr/container/datasources.go index 4eac82395..96fbeaaa9 100644 --- a/pkg/gofr/container/datasources.go +++ b/pkg/gofr/container/datasources.go @@ -721,3 +721,40 @@ type ElasticsearchProvider interface { provider } + +// Couchbase defines the methods for interacting with a Couchbase database. +type Couchbase interface { + // Get retrieves a document by its key from the specified bucket. + // The result parameter should be a pointer to the struct where the document will be unmarshaled. + Get(ctx context.Context, bucket, key string, result any) error + + // Upsert inserts a new document or replaces an existing one in the specified bucket. + // The document parameter can be any Go type that can be marshaled into JSON. + Upsert(ctx context.Context, bucket, key string, document any, result any) error + + // Remove deletes a document by its key from the specified bucket. + Remove(ctx context.Context, bucket, key string) error + + // Query executes a N1QL query against the Couchbase cluster. + // The statement is the N1QL query string, and params are any query parameters. + // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. + Query(ctx context.Context, statement string, params map[string]any, result any) error + + // AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. + // The statement is the Analytics query string, and params are any query parameters. + // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. + AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error + + Close(opts any) error + + HealthChecker +} + +// CouchbaseProvider is an interface that extends Couchbase with additional methods +// for logging, metrics, tracing, and connection management, aligning with other +// data source providers in your package. +type CouchbaseProvider interface { + Couchbase + + provider +} diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go new file mode 100644 index 000000000..654291204 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -0,0 +1,340 @@ +package couchbase + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/couchbase/gocb/v2" + "go.opencensus.io/trace" +) + +// Error variables for the couchbase package. +var ( + errStatusDown = errors.New("status down") + errMissingField = errors.New("missing required field in config") + errWrongResultType = errors.New("result must be *gocb.MutationResult or **gocb.MutationResult") + errBucketNotInitialized = errors.New("couchbase bucket is not initialized") + errClustertNotInitialized = errors.New("couchbase cluster is not initialized") +) + +const defaultTimeout = 5 * time.Second + +// Client represents a Couchbase client that interacts with a Couchbase cluster. +type Client struct { + cluster clusterProvider + bucket bucketProvider + config *Config + logger Logger + metrics Metrics + tracer trace.Tracer +} + +// Config holds the configuration parameters for connecting to a Couchbase cluster. +type Config struct { + Host string + User string + Password string + Bucket string + URI string + ConnectionTimeout time.Duration +} + +// New creates a new Couchbase client with the provided configuration. +func New(c *Config) *Client { + return &Client{config: c} +} + +// UseLogger sets the logger for the Couchbase client. +func (c *Client) UseLogger(logger any) { + if l, ok := logger.(Logger); ok { + c.logger = l + } +} + +// UseMetrics sets the metrics collector for the Couchbase client. +func (c *Client) UseMetrics(metrics any) { + if m, ok := metrics.(Metrics); ok { + c.metrics = m + } +} + +// UseTracer sets the tracer for the Couchbase client. +func (c *Client) UseTracer(tracer any) { + if t, ok := tracer.(trace.Tracer); ok { + c.tracer = t + } +} + +// collectionWrapper is a wrapper around gocb.Collection to implement the collectionProvider interface. +type collectionWrapper struct { + *gocb.Collection +} + +// Upsert performs an upsert operation on the collection. +func (cw *collectionWrapper) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { + return cw.Collection.Upsert(key, value, opts) +} + +// bucketWrapper is a wrapper around gocb.Bucket to implement the bucketProvider interface. +type bucketWrapper struct { + *gocb.Bucket +} + +// Collection returns a collectionProvider for the specified collection name. +func (bw *bucketWrapper) Collection(name string) collectionProvider { + return &collectionWrapper{bw.Bucket.Collection(name)} +} + +// DefaultCollection returns the default collectionProvider for the bucket. +func (bw *bucketWrapper) DefaultCollection() collectionProvider { + return &collectionWrapper{bw.Bucket.DefaultCollection()} +} + +// Connect establishes a connection to the Couchbase cluster and bucket. +func (c *Client) Connect() { + uri, err := generateCouchbaseURI(c.config) + if err != nil { + c.logger.Errorf("error generating Couchbase URI: %v", err) + return + } + + c.logger.Debugf("connecting to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) + + timeout := c.config.ConnectionTimeout + if timeout == 0 { + timeout = defaultTimeout + } + + cluster, err := gocb.Connect(uri, gocb.ClusterOptions{ + Authenticator: gocb.PasswordAuthenticator{ + Username: c.config.User, + Password: c.config.Password, + }, + }) + if err != nil { + c.logger.Errorf("error while connecting to Couchbase, err:%v", err) + return + } + + c.cluster = cluster + + err = c.cluster.(*gocb.Cluster).WaitUntilReady(timeout, nil) + if err != nil { + c.logger.Errorf("could not connect to Couchbase at %v due to err: %v", c.config.Host, err) + return + } + + c.bucket = &bucketWrapper{c.cluster.Bucket(c.config.Bucket)} + + err = c.bucket.WaitUntilReady(timeout, nil) + if err != nil { + c.logger.Errorf("could not connect to bucket %v at %v due to err: %v", c.config.Bucket, c.config.Host, err) + return + } + + couchbaseBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} + c.metrics.NewHistogram("app_couchbase_stats", "Response time of Couchbase queries in milliseconds.", couchbaseBuckets...) + + c.logger.Logf("connected to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) +} + +// generateCouchbaseURI generates the Couchbase connection URI from the configuration. +func generateCouchbaseURI(config *Config) (string, error) { + if config.URI != "" { + return config.URI, nil + } + + if config.Host == "" { + return "", fmt.Errorf("%w: host is empty", errMissingField) + } + + return fmt.Sprintf("couchbase://%s", config.Host), nil +} + +// Health represents the health status of the Couchbase connection. +type Health struct { + Status string `json:"status,omitempty"` + Details map[string]any `json:"details,omitempty"` +} + +// HealthCheck performs a health check on the Couchbase cluster. +func (c *Client) HealthCheck() (any, error) { + h := Health{ + Details: make(map[string]any), + } + + h.Details["host"] = c.config.Host + h.Details["bucket"] = c.config.Bucket + + _, err := c.cluster.(*gocb.Cluster).Ping(nil) + if err != nil { + h.Status = "DOWN" + return &h, errStatusDown + } + + h.Status = "UP" + + return &h, nil +} + +// Upsert inserts a new document or replaces an existing one in the specified bucket. +func (c *Client) Upsert(ctx context.Context, key string, document, result any) error { + if c.bucket == nil { + return errBucketNotInitialized + } + + mr, err := c.bucket.DefaultCollection().Upsert(key, document, &gocb.UpsertOptions{Context: ctx}) + if err != nil { + return fmt.Errorf("failed to upsert document with key %s: %w", key, err) + } + + switch r := result.(type) { + case *gocb.MutationResult: + *r = *mr + case **gocb.MutationResult: + *r = mr + default: + return errWrongResultType + } + + return nil +} + +// Get retrieves a document by its key from the specified bucket. +func (c *Client) Get(ctx context.Context, key string, result any) error { + if c.bucket == nil { + return errBucketNotInitialized + } + + // For simplicity, using the default collection. In a more complex app, + // you might pass collection name or use scope/collection. + res, err := c.bucket.DefaultCollection().Get(key, &gocb.GetOptions{Context: ctx}) + if err != nil { + return fmt.Errorf("failed to get document with key %s: %w", key, err) + } + + if err = res.Content(result); err != nil { + return fmt.Errorf("failed to unmarshal document content for key %s: %w", key, err) + } + + return nil +} + +// Remove deletes a document by its key from a bucket. +func (c *Client) Remove(ctx context.Context, key string) error { + if c.bucket == nil { + return errBucketNotInitialized + } + + _, err := c.bucket.DefaultCollection().Remove(key, &gocb.RemoveOptions{Context: ctx}) + if err != nil { + return fmt.Errorf("failed to remove document with key %s: %w", key, err) + } + + return nil +} + +// Query executes a N1QL query against the Couchbase cluster. +func (c *Client) Query(ctx context.Context, statement string, params map[string]any, result any) error { + if c.cluster == nil { + return errClustertNotInitialized + } + + opts := &gocb.QueryOptions{Context: ctx} + if params != nil { + opts.NamedParameters = params + } + + rows, err := c.cluster.(*gocb.Cluster).Query(statement, opts) + if err != nil { + return fmt.Errorf("N1QL query failed: %w", err) + } + defer rows.Close() + + var tempResults []map[string]any + + for rows.Next() { + var row map[string]any + if err = rows.Row(&row); err != nil { + return fmt.Errorf("failed to unmarshal N1QL query row into map: %w", err) + } + + tempResults = append(tempResults, row) + } + + if err = rows.Err(); err != nil { + return fmt.Errorf("N1QL query iteration error: %w", err) + } + + data, err := json.Marshal(tempResults) + if err != nil { + return fmt.Errorf("failed to marshal N1QL results: %w", err) + } + + if err := json.Unmarshal(data, result); err != nil { + return fmt.Errorf("failed to unmarshal N1QL results into target: %w", err) + } + + return nil +} + +// AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. +func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { + if c.cluster == nil { + return errClustertNotInitialized + } + + opts := &gocb.AnalyticsOptions{Context: ctx} + if params != nil { + // gocb analytics options can take either positional or named parameters. + // For simplicity, we'll assume named parameters if map is provided. + opts.NamedParameters = params + } + + rows, err := c.cluster.(*gocb.Cluster).AnalyticsQuery(statement, opts) + if err != nil { + return fmt.Errorf("analytics query failed: %w", err) + } + defer rows.Close() + + // AnalyticsResult does not have ReadAll, so we iterate manually. + var tempResults []map[string]any + + for rows.Next() { + var row map[string]any + if err = rows.Row(&row); err != nil { + return fmt.Errorf("failed to unmarshal Analytics query row into map: %w", err) + } + + tempResults = append(tempResults, row) + } + + if err = rows.Err(); err != nil { + return fmt.Errorf("analytics query iteration error: %w", err) + } + + // Marshal tempResults to JSON and then unmarshal into the target `result` + // This allows `result` to be `*[]YourStruct`, `*[]map[string]any`, etc. + data, err := json.Marshal(tempResults) + if err != nil { + return fmt.Errorf("failed to marshal analytics results: %w", err) + } + + if err := json.Unmarshal(data, result); err != nil { + return fmt.Errorf("failed to unmarshal analytics results into target: %w", err) + } + + return nil +} + +// Close closes the connection to the Couchbase cluster. +func (c *Client) Close(opts *gocb.ClusterCloseOptions) error { + if c.cluster != nil { + return c.cluster.(*gocb.Cluster).Close(opts) + } + + return nil +} diff --git a/pkg/gofr/datasource/couchbase/couchbase_test.go b/pkg/gofr/datasource/couchbase/couchbase_test.go new file mode 100644 index 000000000..005eea8a9 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/couchbase_test.go @@ -0,0 +1,56 @@ +package couchbase + +import ( + "context" + "testing" + "time" + + "github.com/couchbase/gocb/v2" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestClient_New(t *testing.T) { + client := New(&Config{ + Host: "localhost", + User: "Administrator", + Password: "password", + Bucket: "gofr", + ConnectionTimeout: time.Second * 5, + }) + + assert.NotNil(t, client) +} + +func TestClient_Upsert(t *testing.T) { + var ( + ctrl = gomock.NewController(t) + cluster = NewMockclusterProvider(ctrl) + bucket = NewMockbucketProvider(ctrl) + collection = NewMockcollectionProvider(ctrl) + logger = NewMockLogger(ctrl) + metrics = NewMockMetrics(ctrl) + ) + + client := Client{ + cluster: cluster, + bucket: bucket, + config: &Config{}, + logger: logger, + metrics: metrics, + } + + t.Run("upsert", func(t *testing.T) { + wantResult := &gocb.MutationResult{Result: gocb.Result{}} + + bucket.EXPECT().DefaultCollection().Return(collection) + collection.EXPECT().Upsert("test bucket", map[string]string{"key": "value"}, gomock.Any()). + Return(wantResult, nil) + + var result *gocb.MutationResult + err := client.Upsert(context.Background(), "test bucket", map[string]string{"key": "value"}, &result) + + assert.Nil(t, err) + assert.Equal(t, wantResult, result) + }) +} diff --git a/pkg/gofr/datasource/couchbase/interfaces.go b/pkg/gofr/datasource/couchbase/interfaces.go new file mode 100644 index 000000000..0949462df --- /dev/null +++ b/pkg/gofr/datasource/couchbase/interfaces.go @@ -0,0 +1,34 @@ +package couchbase + +import ( + "time" + + "github.com/couchbase/gocb/v2" + "gofr.dev/pkg/gofr/container" +) + +// clusterProvider is an interface that abstracts the gocb.Cluster for easier testing. +type clusterProvider interface { + Bucket(bucketName string) *gocb.Bucket + AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error) + WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error +} + +// bucketProvider is an interface that abstracts the gocb.Bucket for easier testing. +type bucketProvider interface { + Collection(collectionName string) collectionProvider + DefaultCollection() collectionProvider + WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error +} + +// gocbProvider is an interface that embeds container.CouchbaseProvider for dependency injection. +type gocbProvider interface { + container.CouchbaseProvider +} + +// collectionProvider is an interface that abstracts the gocb.Collection for easier testing. +type collectionProvider interface { + Get(id string, opts *gocb.GetOptions) (docOut *gocb.GetResult, errOut error) + Upsert(id string, val any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) + Remove(id string, opts *gocb.RemoveOptions) (mutOut *gocb.MutationResult, errOut error) +} diff --git a/pkg/gofr/datasource/couchbase/logger.go b/pkg/gofr/datasource/couchbase/logger.go new file mode 100644 index 000000000..845f021c2 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/logger.go @@ -0,0 +1,42 @@ +package couchbase + +import ( + "fmt" + "io" + "regexp" + "strings" +) + +// Logger is an interface for logging messages. +type Logger interface { + Debug(args ...any) + Debugf(pattern string, args ...any) + Logf(pattern string, args ...any) + Errorf(pattern string, args ...any) +} + +// QueryLog represents a log entry for a Couchbase query. +type QueryLog struct { + Query string `json:"query"` + Duration int64 `json:"duration"` +} + +// PrettyPrint prints the query log in a human-readable format. +func (ql *QueryLog) PrettyPrint(writer io.Writer) { + fmt.Fprintf(writer, "\u001b[38;5;8m%-32s \u001b[38;5;207m%-6s\u001b[0m %8d\u001b[38;5;8mµs\u001b[0m %s\n", + clean(ql.Query), "COUCHBASE", ql.Duration, "") +} + +// clean takes a string query as input and performs two operations to clean it up: +// 1. It replaces multiple consecutive whitespace characters with a single space. +// 2. It trims leading and trailing whitespace from the string. +// The cleaned-up query string is then returned. +func clean(query string) string { + // Replace multiple consecutive whitespace characters with a single space + query = regexp.MustCompile(`\s+`).ReplaceAllString(query, " ") + + // Trim leading and trailing whitespace from the string + query = strings.TrimSpace(query) + + return query +} diff --git a/pkg/gofr/datasource/couchbase/metrics.go b/pkg/gofr/datasource/couchbase/metrics.go new file mode 100644 index 000000000..5ed2f6b72 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/metrics.go @@ -0,0 +1,11 @@ +package couchbase + +import "context" + +// Metrics is an interface for collecting metrics. +type Metrics interface { + // NewHistogram creates a new histogram metric. + NewHistogram(name, desc string, buckets ...float64) + // RecordHistogram records a value in a histogram metric. + RecordHistogram(ctx context.Context, name string, value float64, labels ...string) +} diff --git a/pkg/gofr/datasource/couchbase/mock_interfaces.go b/pkg/gofr/datasource/couchbase/mock_interfaces.go new file mode 100644 index 000000000..e0d583a77 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/mock_interfaces.go @@ -0,0 +1,392 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/gofr/datasource/couchbase/interfaces.go +// +// Generated by this command: +// +// mockgen -source=pkg/gofr/datasource/couchbase/interfaces.go -destination=pkg/gofr/datasource/couchbase/mock_interfaces.go -package=couchbase +// + +// Package couchbase is a generated GoMock package. +package couchbase + +import ( + context "context" + reflect "reflect" + time "time" + + gocb "github.com/couchbase/gocb/v2" + gomock "go.uber.org/mock/gomock" +) + +// MockclusterProvider is a mock of clusterProvider interface. +type MockclusterProvider struct { + ctrl *gomock.Controller + recorder *MockclusterProviderMockRecorder + isgomock struct{} +} + +// MockclusterProviderMockRecorder is the mock recorder for MockclusterProvider. +type MockclusterProviderMockRecorder struct { + mock *MockclusterProvider +} + +// NewMockclusterProvider creates a new mock instance. +func NewMockclusterProvider(ctrl *gomock.Controller) *MockclusterProvider { + mock := &MockclusterProvider{ctrl: ctrl} + mock.recorder = &MockclusterProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockclusterProvider) EXPECT() *MockclusterProviderMockRecorder { + return m.recorder +} + +// AnalyticsQuery mocks base method. +func (m *MockclusterProvider) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnalyticsQuery", statement, opts) + ret0, _ := ret[0].(*gocb.AnalyticsResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AnalyticsQuery indicates an expected call of AnalyticsQuery. +func (mr *MockclusterProviderMockRecorder) AnalyticsQuery(statement, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockclusterProvider)(nil).AnalyticsQuery), statement, opts) +} + +// Bucket mocks base method. +func (m *MockclusterProvider) Bucket(bucketName string) *gocb.Bucket { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Bucket", bucketName) + ret0, _ := ret[0].(*gocb.Bucket) + return ret0 +} + +// Bucket indicates an expected call of Bucket. +func (mr *MockclusterProviderMockRecorder) Bucket(bucketName any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bucket", reflect.TypeOf((*MockclusterProvider)(nil).Bucket), bucketName) +} + +// WaitUntilReady mocks base method. +func (m *MockclusterProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitUntilReady indicates an expected call of WaitUntilReady. +func (mr *MockclusterProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockclusterProvider)(nil).WaitUntilReady), timeout, opts) +} + +// MockbucketProvider is a mock of bucketProvider interface. +type MockbucketProvider struct { + ctrl *gomock.Controller + recorder *MockbucketProviderMockRecorder + isgomock struct{} +} + +// MockbucketProviderMockRecorder is the mock recorder for MockbucketProvider. +type MockbucketProviderMockRecorder struct { + mock *MockbucketProvider +} + +// NewMockbucketProvider creates a new mock instance. +func NewMockbucketProvider(ctrl *gomock.Controller) *MockbucketProvider { + mock := &MockbucketProvider{ctrl: ctrl} + mock.recorder = &MockbucketProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockbucketProvider) EXPECT() *MockbucketProviderMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockbucketProvider) Collection(collectionName string) collectionProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection", collectionName) + ret0, _ := ret[0].(collectionProvider) + return ret0 +} + +// Collection indicates an expected call of Collection. +func (mr *MockbucketProviderMockRecorder) Collection(collectionName any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockbucketProvider)(nil).Collection), collectionName) +} + +// DefaultCollection mocks base method. +func (m *MockbucketProvider) DefaultCollection() collectionProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DefaultCollection") + ret0, _ := ret[0].(collectionProvider) + return ret0 +} + +// DefaultCollection indicates an expected call of DefaultCollection. +func (mr *MockbucketProviderMockRecorder) DefaultCollection() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultCollection", reflect.TypeOf((*MockbucketProvider)(nil).DefaultCollection)) +} + +// WaitUntilReady mocks base method. +func (m *MockbucketProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitUntilReady indicates an expected call of WaitUntilReady. +func (mr *MockbucketProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockbucketProvider)(nil).WaitUntilReady), timeout, opts) +} + +// MockgocbProvider is a mock of gocbProvider interface. +type MockgocbProvider struct { + ctrl *gomock.Controller + recorder *MockgocbProviderMockRecorder + isgomock struct{} +} + +// MockgocbProviderMockRecorder is the mock recorder for MockgocbProvider. +type MockgocbProviderMockRecorder struct { + mock *MockgocbProvider +} + +// NewMockgocbProvider creates a new mock instance. +func NewMockgocbProvider(ctrl *gomock.Controller) *MockgocbProvider { + mock := &MockgocbProvider{ctrl: ctrl} + mock.recorder = &MockgocbProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockgocbProvider) EXPECT() *MockgocbProviderMockRecorder { + return m.recorder +} + +// AnalyticsQuery mocks base method. +func (m *MockgocbProvider) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnalyticsQuery", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// AnalyticsQuery indicates an expected call of AnalyticsQuery. +func (mr *MockgocbProviderMockRecorder) AnalyticsQuery(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockgocbProvider)(nil).AnalyticsQuery), ctx, statement, params, result) +} + +// Close mocks base method. +func (m *MockgocbProvider) Close(opts any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockgocbProviderMockRecorder) Close(opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockgocbProvider)(nil).Close), opts) +} + +// Connect mocks base method. +func (m *MockgocbProvider) Connect() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Connect") +} + +// Connect indicates an expected call of Connect. +func (mr *MockgocbProviderMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockgocbProvider)(nil).Connect)) +} + +// Get mocks base method. +func (m *MockgocbProvider) Get(ctx context.Context, bucket, key string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, bucket, key, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockgocbProviderMockRecorder) Get(ctx, bucket, key, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockgocbProvider)(nil).Get), ctx, bucket, key, result) +} + +// HealthCheck mocks base method. +func (m *MockgocbProvider) HealthCheck(arg0 context.Context) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthCheck", arg0) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HealthCheck indicates an expected call of HealthCheck. +func (mr *MockgocbProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockgocbProvider)(nil).HealthCheck), arg0) +} + +// Query mocks base method. +func (m *MockgocbProvider) Query(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockgocbProviderMockRecorder) Query(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockgocbProvider)(nil).Query), ctx, statement, params, result) +} + +// Remove mocks base method. +func (m *MockgocbProvider) Remove(ctx context.Context, bucket, key string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx, bucket, key) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockgocbProviderMockRecorder) Remove(ctx, bucket, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockgocbProvider)(nil).Remove), ctx, bucket, key) +} + +// Upsert mocks base method. +func (m *MockgocbProvider) Upsert(ctx context.Context, bucket, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upsert", ctx, bucket, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Upsert indicates an expected call of Upsert. +func (mr *MockgocbProviderMockRecorder) Upsert(ctx, bucket, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockgocbProvider)(nil).Upsert), ctx, bucket, key, document, result) +} + +// UseLogger mocks base method. +func (m *MockgocbProvider) UseLogger(logger any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseLogger", logger) +} + +// UseLogger indicates an expected call of UseLogger. +func (mr *MockgocbProviderMockRecorder) UseLogger(logger any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockgocbProvider)(nil).UseLogger), logger) +} + +// UseMetrics mocks base method. +func (m *MockgocbProvider) UseMetrics(metrics any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseMetrics", metrics) +} + +// UseMetrics indicates an expected call of UseMetrics. +func (mr *MockgocbProviderMockRecorder) UseMetrics(metrics any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockgocbProvider)(nil).UseMetrics), metrics) +} + +// UseTracer mocks base method. +func (m *MockgocbProvider) UseTracer(tracer any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseTracer", tracer) +} + +// UseTracer indicates an expected call of UseTracer. +func (mr *MockgocbProviderMockRecorder) UseTracer(tracer any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockgocbProvider)(nil).UseTracer), tracer) +} + +// MockcollectionProvider is a mock of collectionProvider interface. +type MockcollectionProvider struct { + ctrl *gomock.Controller + recorder *MockcollectionProviderMockRecorder + isgomock struct{} +} + +// MockcollectionProviderMockRecorder is the mock recorder for MockcollectionProvider. +type MockcollectionProviderMockRecorder struct { + mock *MockcollectionProvider +} + +// NewMockcollectionProvider creates a new mock instance. +func NewMockcollectionProvider(ctrl *gomock.Controller) *MockcollectionProvider { + mock := &MockcollectionProvider{ctrl: ctrl} + mock.recorder = &MockcollectionProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockcollectionProvider) EXPECT() *MockcollectionProviderMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockcollectionProvider) Get(id string, opts *gocb.GetOptions) (*gocb.GetResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", id, opts) + ret0, _ := ret[0].(*gocb.GetResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockcollectionProviderMockRecorder) Get(id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockcollectionProvider)(nil).Get), id, opts) +} + +// Remove mocks base method. +func (m *MockcollectionProvider) Remove(id string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", id, opts) + ret0, _ := ret[0].(*gocb.MutationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Remove indicates an expected call of Remove. +func (mr *MockcollectionProviderMockRecorder) Remove(id, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockcollectionProvider)(nil).Remove), id, opts) +} + +// Upsert mocks base method. +func (m *MockcollectionProvider) Upsert(id string, val any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upsert", id, val, opts) + ret0, _ := ret[0].(*gocb.MutationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Upsert indicates an expected call of Upsert. +func (mr *MockcollectionProviderMockRecorder) Upsert(id, val, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockcollectionProvider)(nil).Upsert), id, val, opts) +} diff --git a/pkg/gofr/datasource/couchbase/mock_logger.go b/pkg/gofr/datasource/couchbase/mock_logger.go new file mode 100644 index 000000000..aee5b4918 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/mock_logger.go @@ -0,0 +1,107 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/gofr/datasource/couchbase/logger.go +// +// Generated by this command: +// +// mockgen -source=pkg/gofr/datasource/couchbase/logger.go -destination=pkg/gofr/datasource/couchbase/mock_logger.go -package=couchbase +// + +// Package couchbase is a generated GoMock package. +package couchbase + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockLogger is a mock of Logger interface. +type MockLogger struct { + ctrl *gomock.Controller + recorder *MockLoggerMockRecorder + isgomock struct{} +} + +// MockLoggerMockRecorder is the mock recorder for MockLogger. +type MockLoggerMockRecorder struct { + mock *MockLogger +} + +// NewMockLogger creates a new mock instance. +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { + return m.recorder +} + +// Debug mocks base method. +func (m *MockLogger) Debug(args ...any) { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debug", varargs...) +} + +// Debug indicates an expected call of Debug. +func (mr *MockLoggerMockRecorder) Debug(args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), args...) +} + +// Debugf mocks base method. +func (m *MockLogger) Debugf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debugf", varargs...) +} + +// Debugf indicates an expected call of Debugf. +func (mr *MockLoggerMockRecorder) Debugf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) +} + +// Errorf mocks base method. +func (m *MockLogger) Errorf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Errorf", varargs...) +} + +// Errorf indicates an expected call of Errorf. +func (mr *MockLoggerMockRecorder) Errorf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) +} + +// Logf mocks base method. +func (m *MockLogger) Logf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Logf", varargs...) +} + +// Logf indicates an expected call of Logf. +func (mr *MockLoggerMockRecorder) Logf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logf", reflect.TypeOf((*MockLogger)(nil).Logf), varargs...) +} diff --git a/pkg/gofr/datasource/couchbase/mock_metrics.go b/pkg/gofr/datasource/couchbase/mock_metrics.go new file mode 100644 index 000000000..bbc09d574 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/mock_metrics.go @@ -0,0 +1,75 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/gofr/datasource/couchbase/metrics.go +// +// Generated by this command: +// +// mockgen -source=pkg/gofr/datasource/couchbase/metrics.go -destination=pkg/gofr/datasource/couchbase/mock_metrics.go -package=couchbase +// + +// Package couchbase is a generated GoMock package. +package couchbase + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockMetrics is a mock of Metrics interface. +type MockMetrics struct { + ctrl *gomock.Controller + recorder *MockMetricsMockRecorder + isgomock struct{} +} + +// MockMetricsMockRecorder is the mock recorder for MockMetrics. +type MockMetricsMockRecorder struct { + mock *MockMetrics +} + +// NewMockMetrics creates a new mock instance. +func NewMockMetrics(ctrl *gomock.Controller) *MockMetrics { + mock := &MockMetrics{ctrl: ctrl} + mock.recorder = &MockMetricsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockMetrics) EXPECT() *MockMetricsMockRecorder { + return m.recorder +} + +// NewHistogram mocks base method. +func (m *MockMetrics) NewHistogram(name, desc string, buckets ...float64) { + m.ctrl.T.Helper() + varargs := []any{name, desc} + for _, a := range buckets { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "NewHistogram", varargs...) +} + +// NewHistogram indicates an expected call of NewHistogram. +func (mr *MockMetricsMockRecorder) NewHistogram(name, desc any, buckets ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{name, desc}, buckets...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewHistogram", reflect.TypeOf((*MockMetrics)(nil).NewHistogram), varargs...) +} + +// RecordHistogram mocks base method. +func (m *MockMetrics) RecordHistogram(ctx context.Context, name string, value float64, labels ...string) { + m.ctrl.T.Helper() + varargs := []any{ctx, name, value} + for _, a := range labels { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "RecordHistogram", varargs...) +} + +// RecordHistogram indicates an expected call of RecordHistogram. +func (mr *MockMetricsMockRecorder) RecordHistogram(ctx, name, value any, labels ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, name, value}, labels...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordHistogram", reflect.TypeOf((*MockMetrics)(nil).RecordHistogram), varargs...) +} diff --git a/pkg/gofr/external_db.go b/pkg/gofr/external_db.go index 1afdad21c..2ca8ec29d 100644 --- a/pkg/gofr/external_db.go +++ b/pkg/gofr/external_db.go @@ -194,3 +194,14 @@ func (a *App) AddElasticsearch(db container.ElasticsearchProvider) { a.container.Elasticsearch = db } + +func (a *App) AddCouchbase(db container.CouchbaseProvider) { + db.UseLogger(a.Logger()) + db.UseMetrics(a.Metrics()) + + tracer := otel.GetTracerProvider().Tracer("gofr-couchbase") + db.UseTracer(tracer) + db.Connect() + + a.container.Couchbase = db +} From bc639f8028cd53a1a883e3329d57670864a58351 Mon Sep 17 00:00:00 2001 From: olxandr Date: Tue, 8 Jul 2025 22:35:24 +0300 Subject: [PATCH 02/22] added tests for DB methods --- pkg/gofr/datasource/couchbase/couchbase.go | 230 ++++--- .../datasource/couchbase/couchbase_test.go | 627 +++++++++++++++++- pkg/gofr/datasource/couchbase/interfaces.go | 59 +- .../datasource/couchbase/mock_interfaces.go | 435 +++++++----- 4 files changed, 1070 insertions(+), 281 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index 654291204..bb641b370 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -13,11 +13,13 @@ import ( // Error variables for the couchbase package. var ( - errStatusDown = errors.New("status down") - errMissingField = errors.New("missing required field in config") - errWrongResultType = errors.New("result must be *gocb.MutationResult or **gocb.MutationResult") - errBucketNotInitialized = errors.New("couchbase bucket is not initialized") - errClustertNotInitialized = errors.New("couchbase cluster is not initialized") + errStatusDown = errors.New("status down") + errMissingField = errors.New("missing required field in config") + errWrongResultType = errors.New("result must be *gocb.MutationResult or **gocb.MutationResult") + errBucketNotInitialized = errors.New("couchbase bucket is not initialized") + errClustertNotInitialized = errors.New("couchbase cluster is not initialized") + errFailedToUnmarshalN1QL = errors.New("failed to unmarshal N1QL results into target") + errFailedToUnmarshalAnalytics = errors.New("failed to unmarshal analytics results into target") ) const defaultTimeout = 5 * time.Second @@ -42,6 +44,37 @@ type Config struct { ConnectionTimeout time.Duration } +// analyticsResultWrapper is a wrapper around gocb.AnalyticsResult to implement the analyticsResultProvider interface. +type analyticsResultWrapper struct { + *gocb.AnalyticsResult +} + +// clusterWrapper is a wrapper around gocb.Cluster to implement the clusterProvider interface. +type clusterWrapper struct { + *gocb.Cluster +} + +// collectionWrapper is a wrapper around gocb.Collection to implement the collectionProvider interface. +type collectionWrapper struct { + *gocb.Collection +} + +// bucketWrapper is a wrapper around gocb.Bucket to implement the bucketProvider interface. +type bucketWrapper struct { + *gocb.Bucket +} + +// queryResultWrapper is a wrapper around gocb.QueryResult to implement the queryResultProvider interface. +type queryResultWrapper struct { + *gocb.QueryResult +} + +// Health represents the health status of the Couchbase connection. +type Health struct { + Status string `json:"status,omitempty"` + Details map[string]any `json:"details,omitempty"` +} + // New creates a new Couchbase client with the provided configuration. func New(c *Config) *Client { return &Client{config: c} @@ -62,25 +95,46 @@ func (c *Client) UseMetrics(metrics any) { } // UseTracer sets the tracer for the Couchbase client. -func (c *Client) UseTracer(tracer any) { - if t, ok := tracer.(trace.Tracer); ok { - c.tracer = t +func (c *Client) UseTracer(tracer trace.Tracer) { + c.tracer = tracer +} + +// Bucket returns a bucketProvider for the specified bucket name. +func (cw *clusterWrapper) Bucket(bucketName string) bucketProvider { + return &bucketWrapper{cw.Cluster.Bucket(bucketName)} +} + +// Query executes a N1QL query against the Couchbase cluster. +func (cw *clusterWrapper) Query(statement string, opts *gocb.QueryOptions) (resultProvider, error) { + res, err := cw.Cluster.Query(statement, opts) + if err != nil { + return nil, err } + + return &queryResultWrapper{res}, nil } -// collectionWrapper is a wrapper around gocb.Collection to implement the collectionProvider interface. -type collectionWrapper struct { - *gocb.Collection +// AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. +func (cw *clusterWrapper) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (resultProvider, error) { + res, err := cw.Cluster.AnalyticsQuery(statement, opts) + if err != nil { + return nil, err + } + + return &analyticsResultWrapper{res}, nil } -// Upsert performs an upsert operation on the collection. -func (cw *collectionWrapper) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { - return cw.Collection.Upsert(key, value, opts) +func (cw *clusterWrapper) Close(opts *gocb.ClusterCloseOptions) error { + return cw.Cluster.Close(opts) } -// bucketWrapper is a wrapper around gocb.Bucket to implement the bucketProvider interface. -type bucketWrapper struct { - *gocb.Bucket +func (cw *clusterWrapper) Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) { + return cw.Cluster.Ping(opts) +} + +// WaitUntilReady waits until the cluster is ready. +func (cw *clusterWrapper) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + return cw.Cluster.WaitUntilReady(timeout, opts) } // Collection returns a collectionProvider for the specified collection name. @@ -93,6 +147,46 @@ func (bw *bucketWrapper) DefaultCollection() collectionProvider { return &collectionWrapper{bw.Bucket.DefaultCollection()} } +// Next returns true if there are more rows to be retrieved. +func (qrw *queryResultWrapper) Next() bool { + return qrw.QueryResult.Next() +} + +// Row unmarshals the current row into the value pointed to by the result parameter. +func (qrw *queryResultWrapper) Row(value any) error { + return qrw.QueryResult.Row(value) +} + +// Err returns the error, if any, that occurred during the rows iteration. +func (qrw *queryResultWrapper) Err() error { + return qrw.QueryResult.Err() +} + +// Close closes the query result. +func (qrw *queryResultWrapper) Close() error { + return qrw.QueryResult.Close() +} + +// Next returns true if there are more rows to be retrieved. +func (arw *analyticsResultWrapper) Next() bool { + return arw.AnalyticsResult.Next() +} + +// Row unmarshals the current row into the value pointed to by the result parameter. +func (arw *analyticsResultWrapper) Row(value any) error { + return arw.AnalyticsResult.Row(value) +} + +// Err returns the error, if any, that occurred during the rows iteration. +func (arw *analyticsResultWrapper) Err() error { + return arw.AnalyticsResult.Err() +} + +// Close closes the analytics result. +func (arw *analyticsResultWrapper) Close() error { + return arw.AnalyticsResult.Close() +} + // Connect establishes a connection to the Couchbase cluster and bucket. func (c *Client) Connect() { uri, err := generateCouchbaseURI(c.config) @@ -119,15 +213,15 @@ func (c *Client) Connect() { return } - c.cluster = cluster + c.cluster = &clusterWrapper{cluster} - err = c.cluster.(*gocb.Cluster).WaitUntilReady(timeout, nil) + err = c.cluster.WaitUntilReady(timeout, nil) if err != nil { c.logger.Errorf("could not connect to Couchbase at %v due to err: %v", c.config.Host, err) return } - c.bucket = &bucketWrapper{c.cluster.Bucket(c.config.Bucket)} + c.bucket = c.cluster.Bucket(c.config.Bucket) err = c.bucket.WaitUntilReady(timeout, nil) if err != nil { @@ -154,12 +248,6 @@ func generateCouchbaseURI(config *Config) (string, error) { return fmt.Sprintf("couchbase://%s", config.Host), nil } -// Health represents the health status of the Couchbase connection. -type Health struct { - Status string `json:"status,omitempty"` - Details map[string]any `json:"details,omitempty"` -} - // HealthCheck performs a health check on the Couchbase cluster. func (c *Client) HealthCheck() (any, error) { h := Health{ @@ -169,7 +257,7 @@ func (c *Client) HealthCheck() (any, error) { h.Details["host"] = c.config.Host h.Details["bucket"] = c.config.Bucket - _, err := c.cluster.(*gocb.Cluster).Ping(nil) + _, err := c.cluster.Ping(nil) if err != nil { h.Status = "DOWN" return &h, errStatusDown @@ -203,14 +291,17 @@ func (c *Client) Upsert(ctx context.Context, key string, document, result any) e return nil } +// Upsert performs an upsert operation on the collection. +func (cw *collectionWrapper) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { + return cw.Collection.Upsert(key, value, opts) +} + // Get retrieves a document by its key from the specified bucket. func (c *Client) Get(ctx context.Context, key string, result any) error { if c.bucket == nil { return errBucketNotInitialized } - // For simplicity, using the default collection. In a more complex app, - // you might pass collection name or use scope/collection. res, err := c.bucket.DefaultCollection().Get(key, &gocb.GetOptions{Context: ctx}) if err != nil { return fmt.Errorf("failed to get document with key %s: %w", key, err) @@ -223,6 +314,11 @@ func (c *Client) Get(ctx context.Context, key string, result any) error { return nil } +// Get performs a get operation on the collection. +func (cw *collectionWrapper) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) { + return cw.Collection.Get(key, opts) +} + // Remove deletes a document by its key from a bucket. func (c *Client) Remove(ctx context.Context, key string) error { if c.bucket == nil { @@ -237,48 +333,27 @@ func (c *Client) Remove(ctx context.Context, key string) error { return nil } +// Remove performs a remove operation on the collection. +func (cw *collectionWrapper) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { + return cw.Collection.Remove(key, opts) +} + // Query executes a N1QL query against the Couchbase cluster. func (c *Client) Query(ctx context.Context, statement string, params map[string]any, result any) error { if c.cluster == nil { return errClustertNotInitialized } - opts := &gocb.QueryOptions{Context: ctx} - if params != nil { - opts.NamedParameters = params - } - - rows, err := c.cluster.(*gocb.Cluster).Query(statement, opts) - if err != nil { - return fmt.Errorf("N1QL query failed: %w", err) - } - defer rows.Close() - - var tempResults []map[string]any - - for rows.Next() { - var row map[string]any - if err = rows.Row(&row); err != nil { - return fmt.Errorf("failed to unmarshal N1QL query row into map: %w", err) + queryFn := func() (resultProvider, error) { + opts := &gocb.QueryOptions{Context: ctx} + if params != nil { + opts.NamedParameters = params } - tempResults = append(tempResults, row) - } - - if err = rows.Err(); err != nil { - return fmt.Errorf("N1QL query iteration error: %w", err) + return c.cluster.Query(statement, opts) } - data, err := json.Marshal(tempResults) - if err != nil { - return fmt.Errorf("failed to marshal N1QL results: %w", err) - } - - if err := json.Unmarshal(data, result); err != nil { - return fmt.Errorf("failed to unmarshal N1QL results into target: %w", err) - } - - return nil + return executeQuery(queryFn, "N1QL", result) } // AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. @@ -287,44 +362,47 @@ func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params ma return errClustertNotInitialized } - opts := &gocb.AnalyticsOptions{Context: ctx} - if params != nil { - // gocb analytics options can take either positional or named parameters. - // For simplicity, we'll assume named parameters if map is provided. - opts.NamedParameters = params + queryFn := func() (resultProvider, error) { + opts := &gocb.AnalyticsOptions{Context: ctx} + if params != nil { + opts.NamedParameters = params + } + + return c.cluster.AnalyticsQuery(statement, opts) } - rows, err := c.cluster.(*gocb.Cluster).AnalyticsQuery(statement, opts) + return executeQuery(queryFn, "Analytics", result) +} + +func executeQuery(queryFn func() (resultProvider, error), queryType string, result any) error { + rows, err := queryFn() if err != nil { - return fmt.Errorf("analytics query failed: %w", err) + return fmt.Errorf("%s query failed: %w", queryType, err) } defer rows.Close() - // AnalyticsResult does not have ReadAll, so we iterate manually. var tempResults []map[string]any for rows.Next() { var row map[string]any if err = rows.Row(&row); err != nil { - return fmt.Errorf("failed to unmarshal Analytics query row into map: %w", err) + return fmt.Errorf("failed to unmarshal %s query row into map: %w", queryType, err) } tempResults = append(tempResults, row) } if err = rows.Err(); err != nil { - return fmt.Errorf("analytics query iteration error: %w", err) + return fmt.Errorf("%s query iteration error: %w", queryType, err) } - // Marshal tempResults to JSON and then unmarshal into the target `result` - // This allows `result` to be `*[]YourStruct`, `*[]map[string]any`, etc. data, err := json.Marshal(tempResults) if err != nil { - return fmt.Errorf("failed to marshal analytics results: %w", err) + return fmt.Errorf("failed to marshal %s results: %w", queryType, err) } if err := json.Unmarshal(data, result); err != nil { - return fmt.Errorf("failed to unmarshal analytics results into target: %w", err) + return fmt.Errorf("failed to unmarshal %s results into target: %w", queryType, err) } return nil @@ -333,7 +411,7 @@ func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params ma // Close closes the connection to the Couchbase cluster. func (c *Client) Close(opts *gocb.ClusterCloseOptions) error { if c.cluster != nil { - return c.cluster.(*gocb.Cluster).Close(opts) + return c.cluster.Close(opts) } return nil diff --git a/pkg/gofr/datasource/couchbase/couchbase_test.go b/pkg/gofr/datasource/couchbase/couchbase_test.go index 005eea8a9..c3f3f0aa9 100644 --- a/pkg/gofr/datasource/couchbase/couchbase_test.go +++ b/pkg/gofr/datasource/couchbase/couchbase_test.go @@ -2,11 +2,14 @@ package couchbase import ( "context" + "encoding/json" "testing" "time" "github.com/couchbase/gocb/v2" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opencensus.io/trace" "go.uber.org/mock/gomock" ) @@ -19,38 +22,612 @@ func TestClient_New(t *testing.T) { ConnectionTimeout: time.Second * 5, }) - assert.NotNil(t, client) + require.NotNil(t, client) } func TestClient_Upsert(t *testing.T) { - var ( - ctrl = gomock.NewController(t) - cluster = NewMockclusterProvider(ctrl) - bucket = NewMockbucketProvider(ctrl) - collection = NewMockcollectionProvider(ctrl) - logger = NewMockLogger(ctrl) - metrics = NewMockMetrics(ctrl) - ) - - client := Client{ + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + metrics := NewMockMetrics(ctrl) + cluster := NewMockclusterProvider(ctrl) + + tests := []struct { + name string + key string + document any + result any + setupMocks func(*MockbucketProvider, *MockcollectionProvider) + client *Client + wantErr error + }{ + { + name: "success: upsert document with *gocb.MutationResult", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &gocb.MutationResult{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + }, + wantErr: nil, + }, + { + name: "success: upsert document with **gocb.MutationResult", + key: "test-key", + document: map[string]string{"key": "value"}, + result: new(*gocb.MutationResult), + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + }, + wantErr: nil, + }, + { + name: "error: from collection.Upsert", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &gocb.MutationResult{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(nil, assert.AnError)) + }, + wantErr: assert.AnError, + }, + { + name: "error: wrong result type", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &struct{}{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + }, + wantErr: errWrongResultType, + }, + { + name: "error: bucket not initialized", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &gocb.MutationResult{}, + setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider) {}, + client: &Client{ + bucket: nil, + }, + wantErr: errBucketNotInitialized, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bucket := NewMockbucketProvider(ctrl) + collection := NewMockcollectionProvider(ctrl) + + tt.setupMocks(bucket, collection) + + client := tt.client + if client == nil { + client = &Client{ + cluster: cluster, + bucket: bucket, + config: &Config{}, + logger: logger, + metrics: metrics, + } + } + + err := client.Upsert(context.Background(), tt.key, tt.document, tt.result) + + if tt.wantErr != nil { + assert.ErrorIs(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestClient_Get(t *testing.T) { + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + metrics := NewMockMetrics(ctrl) + cluster := NewMockclusterProvider(ctrl) + + tests := []struct { + name string + key string + result any + setupMocks func(*MockbucketProvider, *MockcollectionProvider, *MockgetResultProvider) + client *Client + wantErr error + }{ + { + name: "success: get document", + key: "test-key", + result: &struct{}{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, getResult *MockgetResultProvider) { + gomock.InOrder( + bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Get("test-key", gomock.Any()).Return(getResult, nil), + getResult.EXPECT().Content(gomock.Any()).Return(nil), + ) + }, + wantErr: nil, + }, + { + name: "error: from collection.Get", + key: "test-key", + result: &struct{}{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, _ *MockgetResultProvider) { + gomock.InOrder( + bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Get("test-key", gomock.Any()).Return(nil, assert.AnError), + ) + }, + wantErr: assert.AnError, + }, + { + name: "error: from getResult.Content", + key: "test-key", + result: &struct{}{}, + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, getResult *MockgetResultProvider) { + gomock.InOrder( + bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Get("test-key", gomock.Any()).Return(getResult, nil), + getResult.EXPECT().Content(gomock.Any()).Return(assert.AnError), + ) + }, + wantErr: assert.AnError, + }, + { + name: "error: bucket not initialized", + key: "test-key", + result: nil, + setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider, _ *MockgetResultProvider) {}, + client: &Client{ + bucket: nil, + }, + wantErr: errBucketNotInitialized, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bucket := NewMockbucketProvider(ctrl) + collection := NewMockcollectionProvider(ctrl) + getResult := NewMockgetResultProvider(ctrl) + + tt.setupMocks(bucket, collection, getResult) + + client := tt.client + if client == nil { + client = &Client{ + cluster: cluster, + bucket: bucket, + config: &Config{}, + logger: logger, + metrics: metrics, + } + } + + err := client.Get(context.Background(), tt.key, tt.result) + + if tt.wantErr != nil { + assert.ErrorIs(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestClient_Remove(t *testing.T) { + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + metrics := NewMockMetrics(ctrl) + cluster := NewMockclusterProvider(ctrl) + + tests := []struct { + name string + key string + setupMocks func(*MockbucketProvider, *MockcollectionProvider) + client *Client + wantErr error + }{ + { + name: "success: remove document", + key: "test-key", + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder( + bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Remove("test-key", gomock.Any()).Return(&gocb.MutationResult{}, nil), + ) + }, + wantErr: nil, + }, + { + name: "error: from collection.Remove", + key: "test-key", + setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + gomock.InOrder( + bucket.EXPECT().DefaultCollection().Return(collection), + collection.EXPECT().Remove("test-key", gomock.Any()).Return(nil, assert.AnError), + ) + }, + wantErr: assert.AnError, + }, + { + name: "error: bucket not initialized", + key: "test-key", + setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider) {}, + client: &Client{ + bucket: nil, + }, + wantErr: errBucketNotInitialized, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bucket := NewMockbucketProvider(ctrl) + collection := NewMockcollectionProvider(ctrl) + + tt.setupMocks(bucket, collection) + + client := tt.client + if client == nil { + client = &Client{ + cluster: cluster, + bucket: bucket, + config: &Config{}, + logger: logger, + metrics: metrics, + } + } + + err := client.Remove(context.Background(), tt.key) + + if tt.wantErr != nil { + assert.ErrorIs(t, err, tt.wantErr) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestClient_Query(t *testing.T) { + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + metrics := NewMockMetrics(ctrl) + + tests := []struct { + name string + statement string + params map[string]any + result any + setupMocks func(*MockclusterProvider, *MockresultProvider) + client *Client + wantErr error + }{ + { + name: "success: N1QL query", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), + queryResult.EXPECT().Next().Return(true), + queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + data := `{"id": "1", "name": "test"}` + return json.Unmarshal([]byte(data), arg) + }), + queryResult.EXPECT().Next().Return(false), + queryResult.EXPECT().Err().Return(nil), + queryResult.EXPECT().Close().Return(nil)) + }, + wantErr: nil, + }, + { + name: "error: from cluster.Query", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, _ *MockresultProvider) { + cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) + }, + wantErr: assert.AnError, + }, + { + name: "error: N1QL query iteration error", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), + queryResult.EXPECT().Next().Return(false), + queryResult.EXPECT().Err().Return(assert.AnError), + queryResult.EXPECT().Close().Return(nil)) + }, + wantErr: assert.AnError, + }, + { + name: "error: failed to unmarshal N1QL results into target", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &struct{}{}, // This will cause unmarshal error if tempResults is not compatible + setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), + queryResult.EXPECT().Next().Return(true), + queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + data := `{"id": "1", "name": "test"}` + return json.Unmarshal([]byte(data), arg) + }), + queryResult.EXPECT().Next().Return(false), + queryResult.EXPECT().Err().Return(nil), + queryResult.EXPECT().Close().Return(nil)) + }, + wantErr: errFailedToUnmarshalN1QL, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cluster := NewMockclusterProvider(ctrl) + queryResult := NewMockresultProvider(ctrl) + + tt.setupMocks(cluster, queryResult) + + client := tt.client + if client == nil { + client = &Client{ + cluster: cluster, + config: &Config{}, + logger: logger, + metrics: metrics, + } + } + + err := client.Query(context.Background(), tt.statement, tt.params, tt.result) + + if tt.wantErr != nil { + assert.ErrorContains(t, err, tt.wantErr.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestClient_UseLogger(t *testing.T) { + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + client := New(&Config{}) + client.UseLogger(logger) + assert.Equal(t, logger, client.logger) +} + +func TestClient_UseMetrics(t *testing.T) { + ctrl := gomock.NewController(t) + metrics := NewMockMetrics(ctrl) + client := New(&Config{}) + client.UseMetrics(metrics) + assert.Equal(t, metrics, client.metrics) +} + +func TestClient_UseTracer(t *testing.T) { + client := New(&Config{}) + client.UseTracer(trace.DefaultTracer) + assert.NotNil(t, client.tracer) +} + +func TestClient_Close(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + cluster := NewMockclusterProvider(ctrl) + cluster.EXPECT().Close(nil).Return(nil) + + client := &Client{ cluster: cluster, - bucket: bucket, - config: &Config{}, - logger: logger, - metrics: metrics, } - t.Run("upsert", func(t *testing.T) { - wantResult := &gocb.MutationResult{Result: gocb.Result{}} + err := client.Close(nil) + assert.NoError(t, err) +} - bucket.EXPECT().DefaultCollection().Return(collection) - collection.EXPECT().Upsert("test bucket", map[string]string{"key": "value"}, gomock.Any()). - Return(wantResult, nil) +func TestClient_HealthCheck(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() - var result *gocb.MutationResult - err := client.Upsert(context.Background(), "test bucket", map[string]string{"key": "value"}, &result) + tests := []struct { + name string + setupMocks func(*MockclusterProvider) + wantStatus string + wantErr error + }{ + { + name: "success: cluster is up", + setupMocks: func(cluster *MockclusterProvider) { + cluster.EXPECT().Ping(nil).Return(&gocb.PingResult{}, nil) + }, + wantStatus: "UP", + wantErr: nil, + }, + { + name: "error: cluster is down", + setupMocks: func(cluster *MockclusterProvider) { + cluster.EXPECT().Ping(nil).Return(nil, assert.AnError) + }, + wantStatus: "DOWN", + wantErr: errStatusDown, + }, + } - assert.Nil(t, err) - assert.Equal(t, wantResult, result) - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cluster := NewMockclusterProvider(ctrl) + tt.setupMocks(cluster) + + client := &Client{ + cluster: cluster, + config: &Config{ + Host: "localhost", + Bucket: "gofr", + }, + } + + health, err := client.HealthCheck() + + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.wantStatus, health.(*Health).Status) + }) + } +} + +func Test_generateCouchbaseURI(t *testing.T) { + tests := []struct { + name string + config *Config + want string + wantErr bool + }{ + { + name: "success: with host", + config: &Config{ + Host: "localhost", + }, + want: "couchbase://localhost", + wantErr: false, + }, + { + name: "success: with URI", + config: &Config{ + URI: "couchbase://remotehost", + }, + want: "couchbase://remotehost", + wantErr: false, + }, + { + name: "error: missing host and URI", + config: &Config{}, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := generateCouchbaseURI(tt.config) + if (err != nil) != tt.wantErr { + t.Errorf("generateCouchbaseURI() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if got != tt.want { + t.Errorf("generateCouchbaseURI() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestClient_AnalyticsQuery(t *testing.T) { + ctrl := gomock.NewController(t) + logger := NewMockLogger(ctrl) + metrics := NewMockMetrics(ctrl) + + tests := []struct { + name string + statement string + params map[string]any + result any + setupMocks func(*MockclusterProvider, *MockresultProvider) + client *Client + wantErr error + }{ + { + name: "success: Analytics query", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), + analyticsResult.EXPECT().Next().Return(true), + analyticsResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + data := `{"id": "1", "name": "test_analytics"}` + return json.Unmarshal([]byte(data), arg) + }), + analyticsResult.EXPECT().Next().Return(false), + analyticsResult.EXPECT().Err().Return(nil), + analyticsResult.EXPECT().Close().Return(nil)) + }, + wantErr: nil, + }, + { + name: "error: from cluster.AnalyticsQuery", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, _ *MockresultProvider) { + cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) + }, + wantErr: assert.AnError, + }, + { + name: "error: failed to unmarshal Analytics query row", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &[]map[string]any{}, + setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), + analyticsResult.EXPECT().Next().Return(true), + analyticsResult.EXPECT().Row(gomock.Any()).Return(assert.AnError), + analyticsResult.EXPECT().Close().Return(nil)) + }, + wantErr: assert.AnError, + }, + { + name: "error: failed to unmarshal analytics results into target", + statement: "SELECT * FROM `bucket`", + params: nil, + result: &struct{}{}, // This will cause unmarshal error if tempResults is not compatible + setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { + gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), + analyticsResult.EXPECT().Next().Return(true), + analyticsResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + data := `{"id": "1", "name": "test_analytics"}` + return json.Unmarshal([]byte(data), arg) + }), + analyticsResult.EXPECT().Next().Return(false), + analyticsResult.EXPECT().Err().Return(nil), + analyticsResult.EXPECT().Close().Return(nil)) + }, + wantErr: errFailedToUnmarshalAnalytics, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cluster := NewMockclusterProvider(ctrl) + analyticsResult := NewMockresultProvider(ctrl) + + tt.setupMocks(cluster, analyticsResult) + + client := tt.client + if client == nil { + client = &Client{ + cluster: cluster, + config: &Config{}, + logger: logger, + metrics: metrics, + } + } + + err := client.AnalyticsQuery(context.Background(), tt.statement, tt.params, tt.result) + + if tt.wantErr != nil { + assert.ErrorContains(t, err, tt.wantErr.Error()) + } else { + assert.NoError(t, err) + } + }) + } } diff --git a/pkg/gofr/datasource/couchbase/interfaces.go b/pkg/gofr/datasource/couchbase/interfaces.go index 0949462df..253bef9fd 100644 --- a/pkg/gofr/datasource/couchbase/interfaces.go +++ b/pkg/gofr/datasource/couchbase/interfaces.go @@ -1,17 +1,53 @@ package couchbase import ( + "context" "time" - "github.com/couchbase/gocb/v2" - "gofr.dev/pkg/gofr/container" + gocb "github.com/couchbase/gocb/v2" ) +type Couchbase interface { + // Get retrieves a document by its key from the specified bucket. + // The result parameter should be a pointer to the struct where the document will be unmarshaled. + Get(ctx context.Context, bucket, key string, result any) error + + // Upsert inserts a new document or replaces an existing one in the specified bucket. + // The document parameter can be any Go type that can be marshaled into JSON. + Upsert(ctx context.Context, bucket, key string, document any, result any) error + + // Remove deletes a document by its key from the specified bucket. + Remove(ctx context.Context, bucket, key string) error + + // Query executes a N1QL query against the Couchbase cluster. + // The statement is the N1QL query string, and params are any query parameters. + // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. + Query(ctx context.Context, statement string, params map[string]any, result any) error + + // AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. + // The statement is the Analytics query string, and params are any query parameters. + // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. + AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error + + Close(opts any) error +} + // clusterProvider is an interface that abstracts the gocb.Cluster for easier testing. type clusterProvider interface { - Bucket(bucketName string) *gocb.Bucket - AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error) + Bucket(bucketName string) bucketProvider + Query(statement string, opts *gocb.QueryOptions) (resultProvider, error) + AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (resultProvider, error) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error + Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) + Close(opts *gocb.ClusterCloseOptions) error +} + +// resultProvider is an interface that abstracts gocb.QueryResult and gocb.AnalyticsResult for easier testing. +type resultProvider interface { + Next() bool + Row(value any) error + Err() error + Close() error } // bucketProvider is an interface that abstracts the gocb.Bucket for easier testing. @@ -21,14 +57,13 @@ type bucketProvider interface { WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error } -// gocbProvider is an interface that embeds container.CouchbaseProvider for dependency injection. -type gocbProvider interface { - container.CouchbaseProvider -} - // collectionProvider is an interface that abstracts the gocb.Collection for easier testing. type collectionProvider interface { - Get(id string, opts *gocb.GetOptions) (docOut *gocb.GetResult, errOut error) - Upsert(id string, val any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) - Remove(id string, opts *gocb.RemoveOptions) (mutOut *gocb.MutationResult, errOut error) + Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) + Get(key string, opts *gocb.GetOptions) (getResultProvider, error) + Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) +} + +type getResultProvider interface { + Content(value any) error } diff --git a/pkg/gofr/datasource/couchbase/mock_interfaces.go b/pkg/gofr/datasource/couchbase/mock_interfaces.go index e0d583a77..002768cb0 100644 --- a/pkg/gofr/datasource/couchbase/mock_interfaces.go +++ b/pkg/gofr/datasource/couchbase/mock_interfaces.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: pkg/gofr/datasource/couchbase/interfaces.go +// Source: interfaces.go // // Generated by this command: // -// mockgen -source=pkg/gofr/datasource/couchbase/interfaces.go -destination=pkg/gofr/datasource/couchbase/mock_interfaces.go -package=couchbase +// mockgen -source=interfaces.go -destination=mock_interfaces.go -package=couchbase // // Package couchbase is a generated GoMock package. @@ -18,179 +18,169 @@ import ( gomock "go.uber.org/mock/gomock" ) -// MockclusterProvider is a mock of clusterProvider interface. -type MockclusterProvider struct { +// MockCouchbase is a mock of Couchbase interface. +type MockCouchbase struct { ctrl *gomock.Controller - recorder *MockclusterProviderMockRecorder + recorder *MockCouchbaseMockRecorder isgomock struct{} } -// MockclusterProviderMockRecorder is the mock recorder for MockclusterProvider. -type MockclusterProviderMockRecorder struct { - mock *MockclusterProvider +// MockCouchbaseMockRecorder is the mock recorder for MockCouchbase. +type MockCouchbaseMockRecorder struct { + mock *MockCouchbase } -// NewMockclusterProvider creates a new mock instance. -func NewMockclusterProvider(ctrl *gomock.Controller) *MockclusterProvider { - mock := &MockclusterProvider{ctrl: ctrl} - mock.recorder = &MockclusterProviderMockRecorder{mock} +// NewMockCouchbase creates a new mock instance. +func NewMockCouchbase(ctrl *gomock.Controller) *MockCouchbase { + mock := &MockCouchbase{ctrl: ctrl} + mock.recorder = &MockCouchbaseMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockclusterProvider) EXPECT() *MockclusterProviderMockRecorder { +func (m *MockCouchbase) EXPECT() *MockCouchbaseMockRecorder { return m.recorder } // AnalyticsQuery mocks base method. -func (m *MockclusterProvider) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error) { +func (m *MockCouchbase) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AnalyticsQuery", statement, opts) - ret0, _ := ret[0].(*gocb.AnalyticsResult) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "AnalyticsQuery", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 } // AnalyticsQuery indicates an expected call of AnalyticsQuery. -func (mr *MockclusterProviderMockRecorder) AnalyticsQuery(statement, opts any) *gomock.Call { +func (mr *MockCouchbaseMockRecorder) AnalyticsQuery(ctx, statement, params, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockclusterProvider)(nil).AnalyticsQuery), statement, opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockCouchbase)(nil).AnalyticsQuery), ctx, statement, params, result) } -// Bucket mocks base method. -func (m *MockclusterProvider) Bucket(bucketName string) *gocb.Bucket { +// Close mocks base method. +func (m *MockCouchbase) Close(opts any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Bucket", bucketName) - ret0, _ := ret[0].(*gocb.Bucket) + ret := m.ctrl.Call(m, "Close", opts) + ret0, _ := ret[0].(error) return ret0 } -// Bucket indicates an expected call of Bucket. -func (mr *MockclusterProviderMockRecorder) Bucket(bucketName any) *gomock.Call { +// Close indicates an expected call of Close. +func (mr *MockCouchbaseMockRecorder) Close(opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bucket", reflect.TypeOf((*MockclusterProvider)(nil).Bucket), bucketName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCouchbase)(nil).Close), opts) } -// WaitUntilReady mocks base method. -func (m *MockclusterProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { +// Get mocks base method. +func (m *MockCouchbase) Get(ctx context.Context, bucket, key string, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) + ret := m.ctrl.Call(m, "Get", ctx, bucket, key, result) ret0, _ := ret[0].(error) return ret0 } -// WaitUntilReady indicates an expected call of WaitUntilReady. -func (mr *MockclusterProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { +// Get indicates an expected call of Get. +func (mr *MockCouchbaseMockRecorder) Get(ctx, bucket, key, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockclusterProvider)(nil).WaitUntilReady), timeout, opts) -} - -// MockbucketProvider is a mock of bucketProvider interface. -type MockbucketProvider struct { - ctrl *gomock.Controller - recorder *MockbucketProviderMockRecorder - isgomock struct{} -} - -// MockbucketProviderMockRecorder is the mock recorder for MockbucketProvider. -type MockbucketProviderMockRecorder struct { - mock *MockbucketProvider -} - -// NewMockbucketProvider creates a new mock instance. -func NewMockbucketProvider(ctrl *gomock.Controller) *MockbucketProvider { - mock := &MockbucketProvider{ctrl: ctrl} - mock.recorder = &MockbucketProviderMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockbucketProvider) EXPECT() *MockbucketProviderMockRecorder { - return m.recorder + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbase)(nil).Get), ctx, bucket, key, result) } -// Collection mocks base method. -func (m *MockbucketProvider) Collection(collectionName string) collectionProvider { +// Query mocks base method. +func (m *MockCouchbase) Query(ctx context.Context, statement string, params map[string]any, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Collection", collectionName) - ret0, _ := ret[0].(collectionProvider) + ret := m.ctrl.Call(m, "Query", ctx, statement, params, result) + ret0, _ := ret[0].(error) return ret0 } -// Collection indicates an expected call of Collection. -func (mr *MockbucketProviderMockRecorder) Collection(collectionName any) *gomock.Call { +// Query indicates an expected call of Query. +func (mr *MockCouchbaseMockRecorder) Query(ctx, statement, params, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockbucketProvider)(nil).Collection), collectionName) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockCouchbase)(nil).Query), ctx, statement, params, result) } -// DefaultCollection mocks base method. -func (m *MockbucketProvider) DefaultCollection() collectionProvider { +// Remove mocks base method. +func (m *MockCouchbase) Remove(ctx context.Context, bucket, key string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DefaultCollection") - ret0, _ := ret[0].(collectionProvider) + ret := m.ctrl.Call(m, "Remove", ctx, bucket, key) + ret0, _ := ret[0].(error) return ret0 } -// DefaultCollection indicates an expected call of DefaultCollection. -func (mr *MockbucketProviderMockRecorder) DefaultCollection() *gomock.Call { +// Remove indicates an expected call of Remove. +func (mr *MockCouchbaseMockRecorder) Remove(ctx, bucket, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultCollection", reflect.TypeOf((*MockbucketProvider)(nil).DefaultCollection)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCouchbase)(nil).Remove), ctx, bucket, key) } -// WaitUntilReady mocks base method. -func (m *MockbucketProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { +// Upsert mocks base method. +func (m *MockCouchbase) Upsert(ctx context.Context, bucket, key string, document, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) + ret := m.ctrl.Call(m, "Upsert", ctx, bucket, key, document, result) ret0, _ := ret[0].(error) return ret0 } -// WaitUntilReady indicates an expected call of WaitUntilReady. -func (mr *MockbucketProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { +// Upsert indicates an expected call of Upsert. +func (mr *MockCouchbaseMockRecorder) Upsert(ctx, bucket, key, document, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockbucketProvider)(nil).WaitUntilReady), timeout, opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockCouchbase)(nil).Upsert), ctx, bucket, key, document, result) } -// MockgocbProvider is a mock of gocbProvider interface. -type MockgocbProvider struct { +// MockclusterProvider is a mock of clusterProvider interface. +type MockclusterProvider struct { ctrl *gomock.Controller - recorder *MockgocbProviderMockRecorder + recorder *MockclusterProviderMockRecorder isgomock struct{} } -// MockgocbProviderMockRecorder is the mock recorder for MockgocbProvider. -type MockgocbProviderMockRecorder struct { - mock *MockgocbProvider +// MockclusterProviderMockRecorder is the mock recorder for MockclusterProvider. +type MockclusterProviderMockRecorder struct { + mock *MockclusterProvider } -// NewMockgocbProvider creates a new mock instance. -func NewMockgocbProvider(ctrl *gomock.Controller) *MockgocbProvider { - mock := &MockgocbProvider{ctrl: ctrl} - mock.recorder = &MockgocbProviderMockRecorder{mock} +// NewMockclusterProvider creates a new mock instance. +func NewMockclusterProvider(ctrl *gomock.Controller) *MockclusterProvider { + mock := &MockclusterProvider{ctrl: ctrl} + mock.recorder = &MockclusterProviderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockgocbProvider) EXPECT() *MockgocbProviderMockRecorder { +func (m *MockclusterProvider) EXPECT() *MockclusterProviderMockRecorder { return m.recorder } // AnalyticsQuery mocks base method. -func (m *MockgocbProvider) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { +func (m *MockclusterProvider) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (resultProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AnalyticsQuery", ctx, statement, params, result) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "AnalyticsQuery", statement, opts) + ret0, _ := ret[0].(resultProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 } // AnalyticsQuery indicates an expected call of AnalyticsQuery. -func (mr *MockgocbProviderMockRecorder) AnalyticsQuery(ctx, statement, params, result any) *gomock.Call { +func (mr *MockclusterProviderMockRecorder) AnalyticsQuery(statement, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockclusterProvider)(nil).AnalyticsQuery), statement, opts) +} + +// Bucket mocks base method. +func (m *MockclusterProvider) Bucket(bucketName string) bucketProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Bucket", bucketName) + ret0, _ := ret[0].(bucketProvider) + return ret0 +} + +// Bucket indicates an expected call of Bucket. +func (mr *MockclusterProviderMockRecorder) Bucket(bucketName any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockgocbProvider)(nil).AnalyticsQuery), ctx, statement, params, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bucket", reflect.TypeOf((*MockclusterProvider)(nil).Bucket), bucketName) } // Close mocks base method. -func (m *MockgocbProvider) Close(opts any) error { +func (m *MockclusterProvider) Close(opts *gocb.ClusterCloseOptions) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close", opts) ret0, _ := ret[0].(error) @@ -198,128 +188,199 @@ func (m *MockgocbProvider) Close(opts any) error { } // Close indicates an expected call of Close. -func (mr *MockgocbProviderMockRecorder) Close(opts any) *gomock.Call { +func (mr *MockclusterProviderMockRecorder) Close(opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockgocbProvider)(nil).Close), opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockclusterProvider)(nil).Close), opts) } -// Connect mocks base method. -func (m *MockgocbProvider) Connect() { +// Ping mocks base method. +func (m *MockclusterProvider) Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) { m.ctrl.T.Helper() - m.ctrl.Call(m, "Connect") + ret := m.ctrl.Call(m, "Ping", opts) + ret0, _ := ret[0].(*gocb.PingResult) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// Connect indicates an expected call of Connect. -func (mr *MockgocbProviderMockRecorder) Connect() *gomock.Call { +// Ping indicates an expected call of Ping. +func (mr *MockclusterProviderMockRecorder) Ping(opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockgocbProvider)(nil).Connect)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockclusterProvider)(nil).Ping), opts) } -// Get mocks base method. -func (m *MockgocbProvider) Get(ctx context.Context, bucket, key string, result any) error { +// Query mocks base method. +func (m *MockclusterProvider) Query(statement string, opts *gocb.QueryOptions) (resultProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", ctx, bucket, key, result) + ret := m.ctrl.Call(m, "Query", statement, opts) + ret0, _ := ret[0].(resultProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockclusterProviderMockRecorder) Query(statement, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockclusterProvider)(nil).Query), statement, opts) +} + +// WaitUntilReady mocks base method. +func (m *MockclusterProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) ret0, _ := ret[0].(error) return ret0 } -// Get indicates an expected call of Get. -func (mr *MockgocbProviderMockRecorder) Get(ctx, bucket, key, result any) *gomock.Call { +// WaitUntilReady indicates an expected call of WaitUntilReady. +func (mr *MockclusterProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockgocbProvider)(nil).Get), ctx, bucket, key, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockclusterProvider)(nil).WaitUntilReady), timeout, opts) } -// HealthCheck mocks base method. -func (m *MockgocbProvider) HealthCheck(arg0 context.Context) (any, error) { +// MockresultProvider is a mock of resultProvider interface. +type MockresultProvider struct { + ctrl *gomock.Controller + recorder *MockresultProviderMockRecorder + isgomock struct{} +} + +// MockresultProviderMockRecorder is the mock recorder for MockresultProvider. +type MockresultProviderMockRecorder struct { + mock *MockresultProvider +} + +// NewMockresultProvider creates a new mock instance. +func NewMockresultProvider(ctrl *gomock.Controller) *MockresultProvider { + mock := &MockresultProvider{ctrl: ctrl} + mock.recorder = &MockresultProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockresultProvider) EXPECT() *MockresultProviderMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockresultProvider) Close() error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HealthCheck", arg0) - ret0, _ := ret[0].(any) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 } -// HealthCheck indicates an expected call of HealthCheck. -func (mr *MockgocbProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call { +// Close indicates an expected call of Close. +func (mr *MockresultProviderMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockgocbProvider)(nil).HealthCheck), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockresultProvider)(nil).Close)) } -// Query mocks base method. -func (m *MockgocbProvider) Query(ctx context.Context, statement string, params map[string]any, result any) error { +// Err mocks base method. +func (m *MockresultProvider) Err() error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Query", ctx, statement, params, result) + ret := m.ctrl.Call(m, "Err") ret0, _ := ret[0].(error) return ret0 } -// Query indicates an expected call of Query. -func (mr *MockgocbProviderMockRecorder) Query(ctx, statement, params, result any) *gomock.Call { +// Err indicates an expected call of Err. +func (mr *MockresultProviderMockRecorder) Err() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockgocbProvider)(nil).Query), ctx, statement, params, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Err", reflect.TypeOf((*MockresultProvider)(nil).Err)) } -// Remove mocks base method. -func (m *MockgocbProvider) Remove(ctx context.Context, bucket, key string) error { +// Next mocks base method. +func (m *MockresultProvider) Next() bool { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Remove", ctx, bucket, key) - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "Next") + ret0, _ := ret[0].(bool) return ret0 } -// Remove indicates an expected call of Remove. -func (mr *MockgocbProviderMockRecorder) Remove(ctx, bucket, key any) *gomock.Call { +// Next indicates an expected call of Next. +func (mr *MockresultProviderMockRecorder) Next() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockgocbProvider)(nil).Remove), ctx, bucket, key) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Next", reflect.TypeOf((*MockresultProvider)(nil).Next)) } -// Upsert mocks base method. -func (m *MockgocbProvider) Upsert(ctx context.Context, bucket, key string, document, result any) error { +// Row mocks base method. +func (m *MockresultProvider) Row(value any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Upsert", ctx, bucket, key, document, result) + ret := m.ctrl.Call(m, "Row", value) ret0, _ := ret[0].(error) return ret0 } -// Upsert indicates an expected call of Upsert. -func (mr *MockgocbProviderMockRecorder) Upsert(ctx, bucket, key, document, result any) *gomock.Call { +// Row indicates an expected call of Row. +func (mr *MockresultProviderMockRecorder) Row(value any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockgocbProvider)(nil).Upsert), ctx, bucket, key, document, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Row", reflect.TypeOf((*MockresultProvider)(nil).Row), value) } -// UseLogger mocks base method. -func (m *MockgocbProvider) UseLogger(logger any) { +// MockbucketProvider is a mock of bucketProvider interface. +type MockbucketProvider struct { + ctrl *gomock.Controller + recorder *MockbucketProviderMockRecorder + isgomock struct{} +} + +// MockbucketProviderMockRecorder is the mock recorder for MockbucketProvider. +type MockbucketProviderMockRecorder struct { + mock *MockbucketProvider +} + +// NewMockbucketProvider creates a new mock instance. +func NewMockbucketProvider(ctrl *gomock.Controller) *MockbucketProvider { + mock := &MockbucketProvider{ctrl: ctrl} + mock.recorder = &MockbucketProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockbucketProvider) EXPECT() *MockbucketProviderMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockbucketProvider) Collection(collectionName string) collectionProvider { m.ctrl.T.Helper() - m.ctrl.Call(m, "UseLogger", logger) + ret := m.ctrl.Call(m, "Collection", collectionName) + ret0, _ := ret[0].(collectionProvider) + return ret0 } -// UseLogger indicates an expected call of UseLogger. -func (mr *MockgocbProviderMockRecorder) UseLogger(logger any) *gomock.Call { +// Collection indicates an expected call of Collection. +func (mr *MockbucketProviderMockRecorder) Collection(collectionName any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockgocbProvider)(nil).UseLogger), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockbucketProvider)(nil).Collection), collectionName) } -// UseMetrics mocks base method. -func (m *MockgocbProvider) UseMetrics(metrics any) { +// DefaultCollection mocks base method. +func (m *MockbucketProvider) DefaultCollection() collectionProvider { m.ctrl.T.Helper() - m.ctrl.Call(m, "UseMetrics", metrics) + ret := m.ctrl.Call(m, "DefaultCollection") + ret0, _ := ret[0].(collectionProvider) + return ret0 } -// UseMetrics indicates an expected call of UseMetrics. -func (mr *MockgocbProviderMockRecorder) UseMetrics(metrics any) *gomock.Call { +// DefaultCollection indicates an expected call of DefaultCollection. +func (mr *MockbucketProviderMockRecorder) DefaultCollection() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockgocbProvider)(nil).UseMetrics), metrics) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultCollection", reflect.TypeOf((*MockbucketProvider)(nil).DefaultCollection)) } -// UseTracer mocks base method. -func (m *MockgocbProvider) UseTracer(tracer any) { +// WaitUntilReady mocks base method. +func (m *MockbucketProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { m.ctrl.T.Helper() - m.ctrl.Call(m, "UseTracer", tracer) + ret := m.ctrl.Call(m, "WaitUntilReady", timeout, opts) + ret0, _ := ret[0].(error) + return ret0 } -// UseTracer indicates an expected call of UseTracer. -func (mr *MockgocbProviderMockRecorder) UseTracer(tracer any) *gomock.Call { +// WaitUntilReady indicates an expected call of WaitUntilReady. +func (mr *MockbucketProviderMockRecorder) WaitUntilReady(timeout, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockgocbProvider)(nil).UseTracer), tracer) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitUntilReady", reflect.TypeOf((*MockbucketProvider)(nil).WaitUntilReady), timeout, opts) } // MockcollectionProvider is a mock of collectionProvider interface. @@ -347,46 +408,84 @@ func (m *MockcollectionProvider) EXPECT() *MockcollectionProviderMockRecorder { } // Get mocks base method. -func (m *MockcollectionProvider) Get(id string, opts *gocb.GetOptions) (*gocb.GetResult, error) { +func (m *MockcollectionProvider) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", id, opts) - ret0, _ := ret[0].(*gocb.GetResult) + ret := m.ctrl.Call(m, "Get", key, opts) + ret0, _ := ret[0].(getResultProvider) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. -func (mr *MockcollectionProviderMockRecorder) Get(id, opts any) *gomock.Call { +func (mr *MockcollectionProviderMockRecorder) Get(key, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockcollectionProvider)(nil).Get), id, opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockcollectionProvider)(nil).Get), key, opts) } // Remove mocks base method. -func (m *MockcollectionProvider) Remove(id string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { +func (m *MockcollectionProvider) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Remove", id, opts) + ret := m.ctrl.Call(m, "Remove", key, opts) ret0, _ := ret[0].(*gocb.MutationResult) ret1, _ := ret[1].(error) return ret0, ret1 } // Remove indicates an expected call of Remove. -func (mr *MockcollectionProviderMockRecorder) Remove(id, opts any) *gomock.Call { +func (mr *MockcollectionProviderMockRecorder) Remove(key, opts any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockcollectionProvider)(nil).Remove), id, opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockcollectionProvider)(nil).Remove), key, opts) } // Upsert mocks base method. -func (m *MockcollectionProvider) Upsert(id string, val any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { +func (m *MockcollectionProvider) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Upsert", id, val, opts) + ret := m.ctrl.Call(m, "Upsert", key, value, opts) ret0, _ := ret[0].(*gocb.MutationResult) ret1, _ := ret[1].(error) return ret0, ret1 } // Upsert indicates an expected call of Upsert. -func (mr *MockcollectionProviderMockRecorder) Upsert(id, val, opts any) *gomock.Call { +func (mr *MockcollectionProviderMockRecorder) Upsert(key, value, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockcollectionProvider)(nil).Upsert), key, value, opts) +} + +// MockgetResultProvider is a mock of getResultProvider interface. +type MockgetResultProvider struct { + ctrl *gomock.Controller + recorder *MockgetResultProviderMockRecorder + isgomock struct{} +} + +// MockgetResultProviderMockRecorder is the mock recorder for MockgetResultProvider. +type MockgetResultProviderMockRecorder struct { + mock *MockgetResultProvider +} + +// NewMockgetResultProvider creates a new mock instance. +func NewMockgetResultProvider(ctrl *gomock.Controller) *MockgetResultProvider { + mock := &MockgetResultProvider{ctrl: ctrl} + mock.recorder = &MockgetResultProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockgetResultProvider) EXPECT() *MockgetResultProviderMockRecorder { + return m.recorder +} + +// Content mocks base method. +func (m *MockgetResultProvider) Content(value any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Content", value) + ret0, _ := ret[0].(error) + return ret0 +} + +// Content indicates an expected call of Content. +func (mr *MockgetResultProviderMockRecorder) Content(value any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockcollectionProvider)(nil).Upsert), id, val, opts) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Content", reflect.TypeOf((*MockgetResultProvider)(nil).Content), value) } From 58e19cb86f6126ad64df18a2d222dceec1043db1 Mon Sep 17 00:00:00 2001 From: olxandr Date: Sat, 19 Jul 2025 12:35:58 +0300 Subject: [PATCH 03/22] add rest methods and tests --- go.work | 5 +- go.work.sum | 7 +- pkg/gofr/container/datasources.go | 11 +- pkg/gofr/datasource/couchbase/couchbase.go | 449 ++++++---- .../datasource/couchbase/couchbase_test.go | 826 +++++++++++------- pkg/gofr/datasource/couchbase/go.mod | 38 + pkg/gofr/datasource/couchbase/go.sum | 181 ++++ pkg/gofr/datasource/couchbase/interfaces.go | 39 +- pkg/gofr/datasource/couchbase/logger.go | 26 +- .../datasource/couchbase/mock_interfaces.go | 217 ++++- pkg/gofr/datasource/couchbase/mock_logger.go | 16 +- pkg/gofr/datasource/couchbase/wrappers.go | 190 ++++ 12 files changed, 1484 insertions(+), 521 deletions(-) create mode 100644 pkg/gofr/datasource/couchbase/go.mod create mode 100644 pkg/gofr/datasource/couchbase/go.sum create mode 100644 pkg/gofr/datasource/couchbase/wrappers.go diff --git a/go.work b/go.work index 4dbf620c3..f18d090ad 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,4 @@ -go 1.24.0 - -toolchain go1.24.0 +go 1.24.5 use ( . @@ -8,6 +6,7 @@ use ( ./pkg/gofr/datasource/arangodb ./pkg/gofr/datasource/cassandra ./pkg/gofr/datasource/clickhouse + ./pkg/gofr/datasource/couchbase ./pkg/gofr/datasource/dgraph ./pkg/gofr/datasource/elasticsearch ./pkg/gofr/datasource/file/ftp diff --git a/go.work.sum b/go.work.sum index 10fdc6e15..f1572782f 100644 --- a/go.work.sum +++ b/go.work.sum @@ -806,7 +806,6 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3 github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= @@ -1156,7 +1155,6 @@ go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF ======= >>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko= @@ -1346,7 +1344,6 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= @@ -1530,6 +1527,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1542,7 +1540,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= @@ -1550,12 +1547,12 @@ google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe0 google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/pkg/gofr/container/datasources.go b/pkg/gofr/container/datasources.go index 96fbeaaa9..fa91fb3ef 100644 --- a/pkg/gofr/container/datasources.go +++ b/pkg/gofr/container/datasources.go @@ -726,14 +726,17 @@ type ElasticsearchProvider interface { type Couchbase interface { // Get retrieves a document by its key from the specified bucket. // The result parameter should be a pointer to the struct where the document will be unmarshaled. - Get(ctx context.Context, bucket, key string, result any) error + Get(ctx context.Context, key string, result any) error + + // InsertOne inserts a new document in the collection. + Insert(ctx context.Context, key string, document, result any) error // Upsert inserts a new document or replaces an existing one in the specified bucket. // The document parameter can be any Go type that can be marshaled into JSON. - Upsert(ctx context.Context, bucket, key string, document any, result any) error + Upsert(ctx context.Context, key string, document any, result any) error // Remove deletes a document by its key from the specified bucket. - Remove(ctx context.Context, bucket, key string) error + Remove(ctx context.Context, key string) error // Query executes a N1QL query against the Couchbase cluster. // The statement is the N1QL query string, and params are any query parameters. @@ -745,6 +748,8 @@ type Couchbase interface { // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error + RunTransaction(ctx context.Context, logic func(attempt any) error) (any, error) + Close(opts any) error HealthChecker diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index bb641b370..1bbbd1203 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -8,7 +8,8 @@ import ( "time" "github.com/couchbase/gocb/v2" - "go.opencensus.io/trace" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // Error variables for the couchbase package. @@ -34,6 +35,18 @@ type Client struct { tracer trace.Tracer } +// Collection represents a handle to a Couchbase collection. +type Collection struct { + collection collectionProvider + client *Client +} + +// Scope represents a handle to a Couchbase scope. +type Scope struct { + scope scopeProvider + client *Client +} + // Config holds the configuration parameters for connecting to a Couchbase cluster. type Config struct { Host string @@ -44,31 +57,6 @@ type Config struct { ConnectionTimeout time.Duration } -// analyticsResultWrapper is a wrapper around gocb.AnalyticsResult to implement the analyticsResultProvider interface. -type analyticsResultWrapper struct { - *gocb.AnalyticsResult -} - -// clusterWrapper is a wrapper around gocb.Cluster to implement the clusterProvider interface. -type clusterWrapper struct { - *gocb.Cluster -} - -// collectionWrapper is a wrapper around gocb.Collection to implement the collectionProvider interface. -type collectionWrapper struct { - *gocb.Collection -} - -// bucketWrapper is a wrapper around gocb.Bucket to implement the bucketProvider interface. -type bucketWrapper struct { - *gocb.Bucket -} - -// queryResultWrapper is a wrapper around gocb.QueryResult to implement the queryResultProvider interface. -type queryResultWrapper struct { - *gocb.QueryResult -} - // Health represents the health status of the Couchbase connection. type Health struct { Status string `json:"status,omitempty"` @@ -95,113 +83,75 @@ func (c *Client) UseMetrics(metrics any) { } // UseTracer sets the tracer for the Couchbase client. -func (c *Client) UseTracer(tracer trace.Tracer) { - c.tracer = tracer -} - -// Bucket returns a bucketProvider for the specified bucket name. -func (cw *clusterWrapper) Bucket(bucketName string) bucketProvider { - return &bucketWrapper{cw.Cluster.Bucket(bucketName)} -} - -// Query executes a N1QL query against the Couchbase cluster. -func (cw *clusterWrapper) Query(statement string, opts *gocb.QueryOptions) (resultProvider, error) { - res, err := cw.Cluster.Query(statement, opts) - if err != nil { - return nil, err +func (c *Client) UseTracer(tracer any) { + if tracer, ok := tracer.(trace.Tracer); ok { + c.tracer = tracer } - - return &queryResultWrapper{res}, nil } -// AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. -func (cw *clusterWrapper) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (resultProvider, error) { - res, err := cw.Cluster.AnalyticsQuery(statement, opts) - if err != nil { - return nil, err - } +func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method string, span trace.Span) { + duration := time.Since(startTime).Microseconds() - return &analyticsResultWrapper{res}, nil -} - -func (cw *clusterWrapper) Close(opts *gocb.ClusterCloseOptions) error { - return cw.Cluster.Close(opts) -} + ql.Duration = duration -func (cw *clusterWrapper) Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) { - return cw.Cluster.Ping(opts) -} + c.logger.Debug(ql) -// WaitUntilReady waits until the cluster is ready. -func (cw *clusterWrapper) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { - return cw.Cluster.WaitUntilReady(timeout, opts) -} + c.metrics.RecordHistogram(context.Background(), "app_couchbase_stats", float64(duration), "hostname", c.config.Host, + "bucket", c.config.Bucket, "type", method) -// Collection returns a collectionProvider for the specified collection name. -func (bw *bucketWrapper) Collection(name string) collectionProvider { - return &collectionWrapper{bw.Bucket.Collection(name)} + if span != nil { + defer span.End() + span.SetAttributes(attribute.Int64(fmt.Sprintf("couchbase.%v.duration", method), duration)) + } } -// DefaultCollection returns the default collectionProvider for the bucket. -func (bw *bucketWrapper) DefaultCollection() collectionProvider { - return &collectionWrapper{bw.Bucket.DefaultCollection()} -} +func (c *Client) Connect() error { + uri, err := c.generateCouchbaseURI() + if err != nil { + c.logger.Errorf("error generating Couchbase URI: %v", err) + return err + } -// Next returns true if there are more rows to be retrieved. -func (qrw *queryResultWrapper) Next() bool { - return qrw.QueryResult.Next() -} + c.logger.Debugf("connecting to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) -// Row unmarshals the current row into the value pointed to by the result parameter. -func (qrw *queryResultWrapper) Row(value any) error { - return qrw.QueryResult.Row(value) -} + if err := c.establishConnection(uri); err != nil { + c.logger.Errorf("error while connecting to Couchbase, err:%v", err) + return err + } -// Err returns the error, if any, that occurred during the rows iteration. -func (qrw *queryResultWrapper) Err() error { - return qrw.QueryResult.Err() -} + if err := c.waitForClusterReady(); err != nil { + c.logger.Errorf("could not connect to Couchbase at %v due to err: %v", c.config.Host, err) + return err + } -// Close closes the query result. -func (qrw *queryResultWrapper) Close() error { - return qrw.QueryResult.Close() -} + c.bucket = c.cluster.Bucket(c.config.Bucket) -// Next returns true if there are more rows to be retrieved. -func (arw *analyticsResultWrapper) Next() bool { - return arw.AnalyticsResult.Next() -} + if err := c.waitForBucketReady(); err != nil { + c.logger.Errorf("could not connect to bucket %v at %v due to err: %v", c.config.Bucket, c.config.Host, err) + return err + } -// Row unmarshals the current row into the value pointed to by the result parameter. -func (arw *analyticsResultWrapper) Row(value any) error { - return arw.AnalyticsResult.Row(value) -} + couchbaseBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} + c.metrics.NewHistogram("app_couchbase_stats", "Response time of Couchbase queries in milliseconds.", couchbaseBuckets...) -// Err returns the error, if any, that occurred during the rows iteration. -func (arw *analyticsResultWrapper) Err() error { - return arw.AnalyticsResult.Err() -} + c.logger.Logf("connected to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) -// Close closes the analytics result. -func (arw *analyticsResultWrapper) Close() error { - return arw.AnalyticsResult.Close() + return nil } -// Connect establishes a connection to the Couchbase cluster and bucket. -func (c *Client) Connect() { - uri, err := generateCouchbaseURI(c.config) - if err != nil { - c.logger.Errorf("error generating Couchbase URI: %v", err) - return +func (c *Client) generateCouchbaseURI() (string, error) { + if c.config.URI != "" { + return c.config.URI, nil } - c.logger.Debugf("connecting to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) - - timeout := c.config.ConnectionTimeout - if timeout == 0 { - timeout = defaultTimeout + if c.config.Host == "" { + return "", fmt.Errorf("%w: host is empty", errMissingField) } + return fmt.Sprintf("couchbase://%s", c.config.Host), nil +} + +func (c *Client) establishConnection(uri string) error { cluster, err := gocb.Connect(uri, gocb.ClusterOptions{ Authenticator: gocb.PasswordAuthenticator{ Username: c.config.User, @@ -209,52 +159,39 @@ func (c *Client) Connect() { }, }) if err != nil { - c.logger.Errorf("error while connecting to Couchbase, err:%v", err) - return + return err } c.cluster = &clusterWrapper{cluster} - err = c.cluster.WaitUntilReady(timeout, nil) - if err != nil { - c.logger.Errorf("could not connect to Couchbase at %v due to err: %v", c.config.Host, err) - return - } - - c.bucket = c.cluster.Bucket(c.config.Bucket) - - err = c.bucket.WaitUntilReady(timeout, nil) - if err != nil { - c.logger.Errorf("could not connect to bucket %v at %v due to err: %v", c.config.Bucket, c.config.Host, err) - return - } - - couchbaseBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} - c.metrics.NewHistogram("app_couchbase_stats", "Response time of Couchbase queries in milliseconds.", couchbaseBuckets...) + return nil +} - c.logger.Logf("connected to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) +func (c *Client) waitForClusterReady() error { + timeout := c.getTimeout() + return c.cluster.WaitUntilReady(timeout, nil) } -// generateCouchbaseURI generates the Couchbase connection URI from the configuration. -func generateCouchbaseURI(config *Config) (string, error) { - if config.URI != "" { - return config.URI, nil - } +func (c *Client) waitForBucketReady() error { + timeout := c.getTimeout() + return c.bucket.WaitUntilReady(timeout, nil) +} - if config.Host == "" { - return "", fmt.Errorf("%w: host is empty", errMissingField) +func (c *Client) getTimeout() time.Duration { + if c.config.ConnectionTimeout == 0 { + return defaultTimeout } - return fmt.Sprintf("couchbase://%s", config.Host), nil + return c.config.ConnectionTimeout } // HealthCheck performs a health check on the Couchbase cluster. -func (c *Client) HealthCheck() (any, error) { +func (c *Client) HealthCheck(_ context.Context) (any, error) { h := Health{ Details: make(map[string]any), } - h.Details["host"] = c.config.Host + h.Details["host"] = c.config.URI h.Details["bucket"] = c.config.Bucket _, err := c.cluster.Ping(nil) @@ -268,15 +205,60 @@ func (c *Client) HealthCheck() (any, error) { return &h, nil } -// Upsert inserts a new document or replaces an existing one in the specified bucket. -func (c *Client) Upsert(ctx context.Context, key string, document, result any) error { +// DefaultCollection returns a handle for the default collection. +func (c *Client) DefaultCollection() *Collection { if c.bucket == nil { + c.logger.Error("bucket not initialized") + + return &Collection{client: c} + } + + return &Collection{ + collection: c.bucket.DefaultCollection(), + client: c, + } +} + +// Scope returns a handle for a specific scope. +func (c *Client) Scope(name string) *Scope { + if c.bucket == nil { + c.logger.Error("bucket not initialized") + + return &Scope{client: c} + } + + return &Scope{ + scope: c.bucket.Scope(name), + client: c, + } +} + +// Collection returns a handle for a specific collection within the scope. +func (s *Scope) Collection(name string) *Collection { + if s.scope == nil { + return &Collection{client: s.client} + } + + return &Collection{ + collection: s.scope.Collection(name), + client: s.client, + } +} + +// Upsert performs an upsert operation on the collection. +func (c *Collection) Upsert(ctx context.Context, key string, document, result any) error { + if c.collection == nil { return errBucketNotInitialized } - mr, err := c.bucket.DefaultCollection().Upsert(key, document, &gocb.UpsertOptions{Context: ctx}) + tracerCtx, span := c.client.addTrace(ctx, "Upsert", key) + + mr, err := c.collection.Upsert(key, document, &gocb.UpsertOptions{Context: tracerCtx}) + + defer c.client.sendOperationStats(&QueryLog{Query: "Upsert", Key: key, Parameters: document}, time.Now(), "Upsert", span) + if err != nil { - return fmt.Errorf("failed to upsert document with key %s: %w", key, err) + return fmt.Errorf("failed to Upsert document with key %s: %w", key, err) } switch r := result.(type) { @@ -291,19 +273,49 @@ func (c *Client) Upsert(ctx context.Context, key string, document, result any) e return nil } -// Upsert performs an upsert operation on the collection. -func (cw *collectionWrapper) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { - return cw.Collection.Upsert(key, value, opts) +// Insert inserts a new document in the collection. +func (c *Collection) Insert(ctx context.Context, key string, document, result any) error { + if c.collection == nil { + return errBucketNotInitialized + } + + tracerCtx, span := c.client.addTrace(ctx, "Insert", key) + + mr, err := c.collection.Insert(key, document, &gocb.InsertOptions{Context: tracerCtx}) + + defer c.client.sendOperationStats(&QueryLog{Query: "Insert", Key: key, Parameters: document}, time.Now(), "Insert", span) + + if err != nil { + return fmt.Errorf("failed to Insert document with key %s: %w", key, err) + } + + switch r := result.(type) { + case *gocb.MutationResult: + *r = *mr + case **gocb.MutationResult: + *r = mr + default: + return errWrongResultType + } + + return nil } -// Get retrieves a document by its key from the specified bucket. -func (c *Client) Get(ctx context.Context, key string, result any) error { - if c.bucket == nil { +// Get performs a get operation on the collection. +func (c *Collection) Get(ctx context.Context, key string, result any) error { + if c.collection == nil { return errBucketNotInitialized } - res, err := c.bucket.DefaultCollection().Get(key, &gocb.GetOptions{Context: ctx}) + tracerCtx, span := c.client.addTrace(ctx, "Get", key) + + res, err := c.collection.Get(key, &gocb.GetOptions{Context: tracerCtx}) + + defer c.client.sendOperationStats(&QueryLog{Query: "Get", Key: key}, time.Now(), "Get", span) + if err != nil { + c.client.logger.Errorf("failed to get document with key %s: %w", key, err) + return fmt.Errorf("failed to get document with key %s: %w", key, err) } @@ -314,18 +326,18 @@ func (c *Client) Get(ctx context.Context, key string, result any) error { return nil } -// Get performs a get operation on the collection. -func (cw *collectionWrapper) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) { - return cw.Collection.Get(key, opts) -} - -// Remove deletes a document by its key from a bucket. -func (c *Client) Remove(ctx context.Context, key string) error { - if c.bucket == nil { +// Remove performs a remove operation on the collection. +func (c *Collection) Remove(ctx context.Context, key string) error { + if c.collection == nil { return errBucketNotInitialized } - _, err := c.bucket.DefaultCollection().Remove(key, &gocb.RemoveOptions{Context: ctx}) + tracerCtx, span := c.client.addTrace(ctx, "Remove", key) + + _, err := c.collection.Remove(key, &gocb.RemoveOptions{Context: tracerCtx}) + + defer c.client.sendOperationStats(&QueryLog{Query: "Remove", Key: key}, time.Now(), "Remove", span) + if err != nil { return fmt.Errorf("failed to remove document with key %s: %w", key, err) } @@ -333,19 +345,40 @@ func (c *Client) Remove(ctx context.Context, key string) error { return nil } -// Remove performs a remove operation on the collection. -func (cw *collectionWrapper) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { - return cw.Collection.Remove(key, opts) -} - -// Query executes a N1QL query against the Couchbase cluster. -func (c *Client) Query(ctx context.Context, statement string, params map[string]any, result any) error { +func (c *Client) executeTracedQuery( + ctx context.Context, + statement string, + params map[string]any, + result any, + operation string, + queryType string, + queryFn func(tracerCtx context.Context) (resultProvider, error), +) error { if c.cluster == nil { return errClustertNotInitialized } - queryFn := func() (resultProvider, error) { - opts := &gocb.QueryOptions{Context: ctx} + tracerCtx, span := c.addTrace(ctx, operation, statement) + + startTime := time.Now() + + err := executeQuery(func() (resultProvider, error) { + return queryFn(tracerCtx) + }, queryType, result) + + defer c.sendOperationStats(&QueryLog{Query: operation, Statement: statement, Parameters: params}, startTime, operation, span) + + if err != nil { + c.logger.Errorf("%s query failed: %v", queryType, err) + } + + return err +} + +// Query executes a N1QL query against the Couchbase cluster. +func (c *Client) Query(ctx context.Context, statement string, params map[string]any, result any) error { + queryFn := func(tracerCtx context.Context) (resultProvider, error) { + opts := &gocb.QueryOptions{Context: tracerCtx} if params != nil { opts.NamedParameters = params } @@ -353,17 +386,13 @@ func (c *Client) Query(ctx context.Context, statement string, params map[string] return c.cluster.Query(statement, opts) } - return executeQuery(queryFn, "N1QL", result) + return c.executeTracedQuery(ctx, statement, params, result, "N1QLQuery", "N1QL", queryFn) } // AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { - if c.cluster == nil { - return errClustertNotInitialized - } - - queryFn := func() (resultProvider, error) { - opts := &gocb.AnalyticsOptions{Context: ctx} + queryFn := func(tracerCtx context.Context) (resultProvider, error) { + opts := &gocb.AnalyticsOptions{Context: tracerCtx} if params != nil { opts.NamedParameters = params } @@ -371,7 +400,7 @@ func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params ma return c.cluster.AnalyticsQuery(statement, opts) } - return executeQuery(queryFn, "Analytics", result) + return c.executeTracedQuery(ctx, statement, params, result, "AnalyticsQuery", "Analytics", queryFn) } func executeQuery(queryFn func() (resultProvider, error), queryType string, result any) error { @@ -402,17 +431,79 @@ func executeQuery(queryFn func() (resultProvider, error), queryType string, resu } if err := json.Unmarshal(data, result); err != nil { - return fmt.Errorf("failed to unmarshal %s results into target: %w", queryType, err) + switch queryType { + case "N1QL": + return fmt.Errorf("%w: %w", errFailedToUnmarshalN1QL, err) + case "Analytics": + return fmt.Errorf("%w: %w", errFailedToUnmarshalAnalytics, err) + } } return nil } +// RunTransaction executes a transaction. +func (c *Client) RunTransaction(ctx context.Context, logic func(t *gocb.TransactionAttemptContext) error) (*gocb.TransactionResult, error) { + if c.cluster == nil { + return nil, errClustertNotInitialized + } + + _, span := c.addTrace(ctx, "RunTransaction", "transaction") + + startTime := time.Now() + + // gocb transactions are not directly context-aware in the Run method signature in the same way as other operations. + // The context is passed down to operations within the transaction lambda. + result, err := c.cluster.Transactions().Run(logic, nil) + + defer c.sendOperationStats(&QueryLog{Query: "RunTransaction"}, startTime, "RunTransaction", span) + + if err != nil { + c.logger.Errorf("Transaction failed: %v", err) + } + + return result, err +} + +// Get performs a get operation on the default collection. +func (c *Client) Get(ctx context.Context, key string, result any) error { + return c.DefaultCollection().Get(ctx, key, result) +} + +// Insert inserts a new document in the default collection. +func (c *Client) Insert(ctx context.Context, key string, document, result any) error { + return c.DefaultCollection().Insert(ctx, key, document, result) +} + +// Upsert performs an upsert operation on the default collection. +func (c *Client) Upsert(ctx context.Context, key string, document, result any) error { + return c.DefaultCollection().Upsert(ctx, key, document, result) +} + +// Remove performs a remove operation on the default collection. +func (c *Client) Remove(ctx context.Context, key string) error { + return c.DefaultCollection().Remove(ctx, key) +} + // Close closes the connection to the Couchbase cluster. -func (c *Client) Close(opts *gocb.ClusterCloseOptions) error { +func (c *Client) Close(opts any) error { if c.cluster != nil { - return c.cluster.Close(opts) + return c.cluster.Close(opts.(*gocb.ClusterCloseOptions)) } return nil } + +func (c *Client) addTrace(ctx context.Context, method, statement string) (context.Context, trace.Span) { + if c.tracer != nil { + contextWithTrace, span := c.tracer.Start(ctx, fmt.Sprintf("couchbase-%v", method)) + + span.SetAttributes( + attribute.String("couchbase.statement", statement), + ) + + return contextWithTrace, span + } + + return ctx, nil +} diff --git a/pkg/gofr/datasource/couchbase/couchbase_test.go b/pkg/gofr/datasource/couchbase/couchbase_test.go index c3f3f0aa9..7609d8c16 100644 --- a/pkg/gofr/datasource/couchbase/couchbase_test.go +++ b/pkg/gofr/datasource/couchbase/couchbase_test.go @@ -3,16 +3,51 @@ package couchbase import ( "context" "encoding/json" + "errors" "testing" "time" "github.com/couchbase/gocb/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.opencensus.io/trace" + "go.opentelemetry.io/otel/trace/noop" "go.uber.org/mock/gomock" ) +var ( + errMockTransaction = errors.New("transaction failed") + errLogic = errors.New("logic error") +) + +type testMocks struct { + logger *MockLogger + metrics *MockMetrics + cluster *MockclusterProvider + bucket *MockbucketProvider + transactions *MocktransactionsProvider + collection *MockcollectionProvider + getResult *MockgetResultProvider + scope *MockscopeProvider + queryResult *MockresultProvider +} + +func newTestMocks(t *testing.T) *testMocks { + t.Helper() + ctrl := gomock.NewController(t) + + return &testMocks{ + logger: NewMockLogger(ctrl), + metrics: NewMockMetrics(ctrl), + cluster: NewMockclusterProvider(ctrl), + bucket: NewMockbucketProvider(ctrl), + transactions: NewMocktransactionsProvider(ctrl), + collection: NewMockcollectionProvider(ctrl), + getResult: NewMockgetResultProvider(ctrl), + scope: NewMockscopeProvider(ctrl), + queryResult: NewMockresultProvider(ctrl), + } +} + func TestClient_New(t *testing.T) { client := New(&Config{ Host: "localhost", @@ -20,171 +55,242 @@ func TestClient_New(t *testing.T) { Password: "password", Bucket: "gofr", ConnectionTimeout: time.Second * 5, + URI: "", }) require.NotNil(t, client) } func TestClient_Upsert(t *testing.T) { - ctrl := gomock.NewController(t) - logger := NewMockLogger(ctrl) - metrics := NewMockMetrics(ctrl) - cluster := NewMockclusterProvider(ctrl) - tests := []struct { - name string - key string - document any - result any - setupMocks func(*MockbucketProvider, *MockcollectionProvider) - client *Client - wantErr error + name string + key string + document any + result any + setup func(mocks *testMocks) *Client + wantErr error }{ { name: "success: upsert document with *gocb.MutationResult", key: "test-key", document: map[string]string{"key": "value"}, result: &gocb.MutationResult{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { - gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: nil, }, { name: "success: upsert document with **gocb.MutationResult", key: "test-key", document: map[string]string{"key": "value"}, - result: new(*gocb.MutationResult), - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { - gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + result: func() **gocb.MutationResult { var res *gocb.MutationResult; return &res }(), + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any())) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: nil, }, { name: "error: from collection.Upsert", key: "test-key", document: map[string]string{"key": "value"}, result: &gocb.MutationResult{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { - gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(nil, assert.AnError)) + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(nil, gocb.ErrDocumentExists), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrDocumentExists, }, { name: "error: wrong result type", key: "test-key", document: map[string]string{"key": "value"}, result: &struct{}{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { - gomock.InOrder(bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil)) + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Upsert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, wantErr: errWrongResultType, }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mocks := newTestMocks(t) + client := tt.setup(mocks) + err := client.Upsert(context.Background(), tt.key, tt.document, tt.result) + + assert.ErrorIs(t, err, tt.wantErr) + }) + } +} + +func TestClient_Insert(t *testing.T) { + tests := []struct { + name string + key string + document any + result any + setup func(mocks *testMocks) *Client + wantErr error + }{ + { + name: "success: insert document with *gocb.MutationResult", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &gocb.MutationResult{}, + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Insert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } + }, + }, + { + name: "error: from collection.Insert", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &gocb.MutationResult{}, + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Insert("test-key", gomock.Any(), gomock.Any()).Return(nil, gocb.ErrDocumentExists), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } + }, + wantErr: gocb.ErrDocumentExists, + }, { - name: "error: bucket not initialized", - key: "test-key", - document: map[string]string{"key": "value"}, - result: &gocb.MutationResult{}, - setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider) {}, - client: &Client{ - bucket: nil, + name: "error: wrong result type", + key: "test-key", + document: map[string]string{"key": "value"}, + result: &struct{}{}, + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Insert("test-key", gomock.Any(), gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any()), + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes(), + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: errBucketNotInitialized, + wantErr: errWrongResultType, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - bucket := NewMockbucketProvider(ctrl) - collection := NewMockcollectionProvider(ctrl) - - tt.setupMocks(bucket, collection) - - client := tt.client - if client == nil { - client = &Client{ - cluster: cluster, - bucket: bucket, - config: &Config{}, - logger: logger, - metrics: metrics, - } - } - - err := client.Upsert(context.Background(), tt.key, tt.document, tt.result) + mocks := newTestMocks(t) + client := tt.setup(mocks) + err := client.Insert(context.Background(), tt.key, tt.document, tt.result) - if tt.wantErr != nil { - assert.ErrorIs(t, err, tt.wantErr) - } else { - assert.NoError(t, err) - } + assert.ErrorIs(t, err, tt.wantErr) }) } } func TestClient_Get(t *testing.T) { - ctrl := gomock.NewController(t) - logger := NewMockLogger(ctrl) - metrics := NewMockMetrics(ctrl) - cluster := NewMockclusterProvider(ctrl) - tests := []struct { - name string - key string - result any - setupMocks func(*MockbucketProvider, *MockcollectionProvider, *MockgetResultProvider) - client *Client - wantErr error + name string + key string + result any + setup func(mocks *testMocks) *Client + wantErr error }{ { name: "success: get document", key: "test-key", result: &struct{}{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, getResult *MockgetResultProvider) { - gomock.InOrder( - bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Get("test-key", gomock.Any()).Return(getResult, nil), - getResult.EXPECT().Content(gomock.Any()).Return(nil), - ) + setup: func(mocks *testMocks) *Client { + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection) + mocks.collection.EXPECT().Get("test-key", gomock.Any()).Return(mocks.getResult, nil) + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.getResult.EXPECT().Content(gomock.Any()).Return(nil) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: nil, }, { name: "error: from collection.Get", key: "test-key", result: &struct{}{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, _ *MockgetResultProvider) { + setup: func(mocks *testMocks) *Client { gomock.InOrder( - bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Get("test-key", gomock.Any()).Return(nil, assert.AnError), + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Get("test-key", gomock.Any()).Return(nil, gocb.ErrDocumentNotFound), + mocks.logger.EXPECT().Debug(gomock.Any()), ) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any()) + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrDocumentNotFound, }, { name: "error: from getResult.Content", key: "test-key", result: &struct{}{}, - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider, getResult *MockgetResultProvider) { + setup: func(mocks *testMocks) *Client { gomock.InOrder( - bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Get("test-key", gomock.Any()).Return(getResult, nil), - getResult.EXPECT().Content(gomock.Any()).Return(assert.AnError), + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Get("test-key", gomock.Any()).Return(mocks.getResult, nil), + mocks.getResult.EXPECT().Content(gomock.Any()).Return(gocb.ErrDecodingFailure), ) + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrDecodingFailure, }, { - name: "error: bucket not initialized", - key: "test-key", - result: nil, - setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider, _ *MockgetResultProvider) {}, - client: &Client{ - bucket: nil, + name: "error: bucket not initialized", + key: "test-key", + result: nil, + setup: func(mocks *testMocks) *Client { + mocks.logger.EXPECT().Error("bucket not initialized") + client := &Client{bucket: nil} + client.UseLogger(mocks.logger) + return client }, wantErr: errBucketNotInitialized, }, @@ -192,75 +298,63 @@ func TestClient_Get(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - bucket := NewMockbucketProvider(ctrl) - collection := NewMockcollectionProvider(ctrl) - getResult := NewMockgetResultProvider(ctrl) - - tt.setupMocks(bucket, collection, getResult) - - client := tt.client - if client == nil { - client = &Client{ - cluster: cluster, - bucket: bucket, - config: &Config{}, - logger: logger, - metrics: metrics, - } - } - + mocks := newTestMocks(t) + client := tt.setup(mocks) err := client.Get(context.Background(), tt.key, tt.result) - if tt.wantErr != nil { - assert.ErrorIs(t, err, tt.wantErr) - } else { - assert.NoError(t, err) - } + assert.ErrorIs(t, err, tt.wantErr) }) } } func TestClient_Remove(t *testing.T) { - ctrl := gomock.NewController(t) - logger := NewMockLogger(ctrl) - metrics := NewMockMetrics(ctrl) - cluster := NewMockclusterProvider(ctrl) - tests := []struct { - name string - key string - setupMocks func(*MockbucketProvider, *MockcollectionProvider) - client *Client - wantErr error + name string + key string + setup func(mocks *testMocks) *Client + wantErr error }{ { name: "success: remove document", key: "test-key", - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + setup: func(mocks *testMocks) *Client { gomock.InOrder( - bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Remove("test-key", gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Remove("test-key", gomock.Any()).Return(&gocb.MutationResult{}, nil), + mocks.logger.EXPECT().Debug(gomock.Any()), ) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debugf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: nil, }, { name: "error: from collection.Remove", key: "test-key", - setupMocks: func(bucket *MockbucketProvider, collection *MockcollectionProvider) { + setup: func(mocks *testMocks) *Client { gomock.InOrder( - bucket.EXPECT().DefaultCollection().Return(collection), - collection.EXPECT().Remove("test-key", gomock.Any()).Return(nil, assert.AnError), + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection), + mocks.collection.EXPECT().Remove("test-key", gomock.Any()).Return(nil, gocb.ErrDocumentNotFound), + mocks.logger.EXPECT().Debug(gomock.Any()), ) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, bucket: mocks.bucket, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrDocumentNotFound, }, { - name: "error: bucket not initialized", - key: "test-key", - setupMocks: func(_ *MockbucketProvider, _ *MockcollectionProvider) {}, - client: &Client{ - bucket: nil, + name: "error: bucket not initialized", + key: "test-key", + setup: func(mocks *testMocks) *Client { + mocks.logger.EXPECT().Error("bucket not initialized") + client := &Client{bucket: nil} + client.UseLogger(mocks.logger) + + return client }, wantErr: errBucketNotInitialized, }, @@ -268,103 +362,271 @@ func TestClient_Remove(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - bucket := NewMockbucketProvider(ctrl) - collection := NewMockcollectionProvider(ctrl) - - tt.setupMocks(bucket, collection) - - client := tt.client - if client == nil { - client = &Client{ - cluster: cluster, - bucket: bucket, - config: &Config{}, - logger: logger, - metrics: metrics, + mocks := newTestMocks(t) + client := tt.setup(mocks) + err := client.Remove(context.Background(), tt.key) + + if tt.wantErr != nil { + assert.ErrorIs(t, err, tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestClient_DefaultCollection(t *testing.T) { + tests := []struct { + name string + setup func(mocks *testMocks) *Client + wantCollection *Collection + }{ + { + name: "success: default collection returned", + setup: func(mocks *testMocks) *Client { + mocks.bucket.EXPECT().DefaultCollection().Return(mocks.collection) + return &Client{ + bucket: mocks.bucket, + logger: mocks.logger, + metrics: mocks.metrics, + tracer: noop.NewTracerProvider().Tracer("test"), } + }, + wantCollection: &Collection{ + collection: NewMockcollectionProvider(gomock.NewController(t)), + }, + }, + { + name: "error: bucket not initialized", + setup: func(mocks *testMocks) *Client { + mocks.logger.EXPECT().Error("bucket not initialized") + return &Client{ + bucket: nil, + logger: mocks.logger, + } + }, + wantCollection: &Collection{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mocks := newTestMocks(t) + client := tt.setup(mocks) + got := client.DefaultCollection() + + // We cannot directly compare the collection, so we check for nil and non-nil cases. + if tt.wantCollection == nil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) } + }) + } +} - err := client.Remove(context.Background(), tt.key) +func TestClient_Scope(t *testing.T) { + tests := []struct { + name string + scopeName string + setup func(mocks *testMocks) *Client + wantScope *Scope + }{ + { + name: "success: scope returned", + scopeName: "test-scope", + setup: func(mocks *testMocks) *Client { + mocks.bucket.EXPECT().Scope("test-scope").Return(mocks.scope) + return &Client{ + bucket: mocks.bucket, + logger: mocks.logger, + metrics: mocks.metrics, + tracer: noop.NewTracerProvider().Tracer("test"), + } + }, + wantScope: &Scope{ + scope: NewMockscopeProvider(gomock.NewController(t)), + }, + }, + { + name: "error: bucket not initialized", + scopeName: "test-scope", + setup: func(mocks *testMocks) *Client { + mocks.logger.EXPECT().Error("bucket not initialized") + return &Client{ + bucket: nil, + logger: mocks.logger, + } + }, + wantScope: &Scope{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mocks := newTestMocks(t) + client := tt.setup(mocks) + got := client.Scope(tt.scopeName) + + if tt.wantScope == nil { + assert.Nil(t, got) + } else { + assert.NotNil(t, got) + } + }) + } +} + +func TestClient_RunTransaction(t *testing.T) { + tests := []struct { + name string + setup func(mocks *testMocks) *Client + logic func(t *gocb.TransactionAttemptContext) error + wantErr error + }{ + { + name: "success: transaction runs", + setup: func(mocks *testMocks) *Client { + mocks.cluster.EXPECT().Transactions().Return(mocks.transactions) + mocks.transactions.EXPECT().Run(gomock.Any(), gomock.Any()).Return(&gocb.TransactionResult{}, nil) + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any()).AnyTimes() + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + return &Client{ + bucket: mocks.bucket, + cluster: mocks.cluster, + logger: mocks.logger, + metrics: mocks.metrics, + config: &Config{Bucket: "bucket"}, + tracer: noop.NewTracerProvider().Tracer("test"), + } + }, + logic: func(*gocb.TransactionAttemptContext) error { + return nil + }, + }, + { + name: "error: cluster not initialized", + setup: func(*testMocks) *Client { + return &Client{cluster: nil} + }, + logic: func(*gocb.TransactionAttemptContext) error { + return nil + }, + wantErr: errClustertNotInitialized, + }, + { + name: "error: transaction fails", + setup: func(mocks *testMocks) *Client { + mocks.cluster.EXPECT().Transactions().Return(mocks.transactions) + mocks.transactions.EXPECT().Run(gomock.Any(), gomock.Any()).Return(nil, errMockTransaction) + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any()).AnyTimes() + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + return &Client{ + cluster: mocks.cluster, + config: &Config{Bucket: "bucket"}, + logger: mocks.logger, + metrics: mocks.metrics, + tracer: noop.NewTracerProvider().Tracer("test"), + } + }, + logic: func(*gocb.TransactionAttemptContext) error { + return errLogic + }, + wantErr: errMockTransaction, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mocks := newTestMocks(t) + client := tt.setup(mocks) + + _, err := client.RunTransaction(context.Background(), tt.logic) if tt.wantErr != nil { - assert.ErrorIs(t, err, tt.wantErr) + assert.ErrorContains(t, err, tt.wantErr.Error()) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } } func TestClient_Query(t *testing.T) { - ctrl := gomock.NewController(t) - logger := NewMockLogger(ctrl) - metrics := NewMockMetrics(ctrl) - tests := []struct { - name string - statement string - params map[string]any - result any - setupMocks func(*MockclusterProvider, *MockresultProvider) - client *Client - wantErr error + name string + statement string + params map[string]any + result any + setup func(mocks *testMocks) *Client + wantErr error }{ { name: "success: N1QL query", statement: "SELECT * FROM `bucket`", params: nil, result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), - queryResult.EXPECT().Next().Return(true), - queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(mocks.queryResult, nil), + mocks.queryResult.EXPECT().Next().Return(true), + mocks.queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { data := `{"id": "1", "name": "test"}` return json.Unmarshal([]byte(data), arg) }), - queryResult.EXPECT().Next().Return(false), - queryResult.EXPECT().Err().Return(nil), - queryResult.EXPECT().Close().Return(nil)) + mocks.queryResult.EXPECT().Next().Return(false), + mocks.queryResult.EXPECT().Err().Return(nil), + mocks.queryResult.EXPECT().Close().Return(nil)) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: nil, }, { name: "error: from cluster.Query", statement: "SELECT * FROM `bucket`", params: nil, result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, _ *MockresultProvider) { - cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) - }, - wantErr: assert.AnError, - }, - { - name: "error: N1QL query iteration error", - statement: "SELECT * FROM `bucket`", - params: nil, - result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), - queryResult.EXPECT().Next().Return(false), - queryResult.EXPECT().Err().Return(assert.AnError), - queryResult.EXPECT().Close().Return(nil)) + setup: func(mocks *testMocks) *Client { + mocks.cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(nil, gocb.ErrPlanningFailure) + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrPlanningFailure, }, { name: "error: failed to unmarshal N1QL results into target", statement: "SELECT * FROM `bucket`", params: nil, - result: &struct{}{}, // This will cause unmarshal error if tempResults is not compatible - setupMocks: func(cluster *MockclusterProvider, queryResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(queryResult, nil), - queryResult.EXPECT().Next().Return(true), - queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + result: &struct{}{}, + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.cluster.EXPECT().Query(gomock.Any(), gomock.Any()).Return(mocks.queryResult, nil), + mocks.queryResult.EXPECT().Next().Return(true), + mocks.queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { data := `{"id": "1", "name": "test"}` return json.Unmarshal([]byte(data), arg) }), - queryResult.EXPECT().Next().Return(false), - queryResult.EXPECT().Err().Return(nil), - queryResult.EXPECT().Close().Return(nil)) + mocks.queryResult.EXPECT().Next().Return(false), + mocks.queryResult.EXPECT().Err().Return(nil), + mocks.queryResult.EXPECT().Close().Return(nil)) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, wantErr: errFailedToUnmarshalN1QL, }, @@ -372,27 +634,14 @@ func TestClient_Query(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cluster := NewMockclusterProvider(ctrl) - queryResult := NewMockresultProvider(ctrl) - - tt.setupMocks(cluster, queryResult) - - client := tt.client - if client == nil { - client = &Client{ - cluster: cluster, - config: &Config{}, - logger: logger, - metrics: metrics, - } - } - + mocks := newTestMocks(t) + client := tt.setup(mocks) err := client.Query(context.Background(), tt.statement, tt.params, tt.result) if tt.wantErr != nil { assert.ErrorContains(t, err, tt.wantErr.Error()) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -416,47 +665,43 @@ func TestClient_UseMetrics(t *testing.T) { func TestClient_UseTracer(t *testing.T) { client := New(&Config{}) - client.UseTracer(trace.DefaultTracer) - assert.NotNil(t, client.tracer) + provider := noop.NewTracerProvider() + tracer := provider.Tracer("test") + client.UseTracer(tracer) + assert.Equal(t, tracer, client.tracer) } func TestClient_Close(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - cluster := NewMockclusterProvider(ctrl) - cluster.EXPECT().Close(nil).Return(nil) + mocks := newTestMocks(t) + mocks.cluster.EXPECT().Close(&gocb.ClusterCloseOptions{}).Return(nil) client := &Client{ - cluster: cluster, + cluster: mocks.cluster, } - err := client.Close(nil) - assert.NoError(t, err) + err := client.Close(&gocb.ClusterCloseOptions{}) + require.NoError(t, err) } func TestClient_HealthCheck(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - tests := []struct { name string - setupMocks func(*MockclusterProvider) + setupMocks func(mocks *testMocks) wantStatus string wantErr error }{ { name: "success: cluster is up", - setupMocks: func(cluster *MockclusterProvider) { - cluster.EXPECT().Ping(nil).Return(&gocb.PingResult{}, nil) + setupMocks: func(mocks *testMocks) { + mocks.cluster.EXPECT().Ping(nil).Return(&gocb.PingResult{}, nil) }, wantStatus: "UP", wantErr: nil, }, { name: "error: cluster is down", - setupMocks: func(cluster *MockclusterProvider) { - cluster.EXPECT().Ping(nil).Return(nil, assert.AnError) + setupMocks: func(mocks *testMocks) { + mocks.cluster.EXPECT().Ping(nil).Return(nil, gocb.ErrUnambiguousTimeout) }, wantStatus: "DOWN", wantErr: errStatusDown, @@ -465,18 +710,18 @@ func TestClient_HealthCheck(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cluster := NewMockclusterProvider(ctrl) - tt.setupMocks(cluster) + mocks := newTestMocks(t) + tt.setupMocks(mocks) client := &Client{ - cluster: cluster, + cluster: mocks.cluster, config: &Config{ Host: "localhost", Bucket: "gofr", }, } - health, err := client.HealthCheck() + health, err := client.HealthCheck(context.Background()) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.wantStatus, health.(*Health).Status) @@ -516,7 +761,9 @@ func Test_generateCouchbaseURI(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := generateCouchbaseURI(tt.config) + c := &Client{config: tt.config} + got, err := c.generateCouchbaseURI() + if (err != nil) != tt.wantErr { t.Errorf("generateCouchbaseURI() error = %v, wantErr %v", err, tt.wantErr) return @@ -530,75 +777,79 @@ func Test_generateCouchbaseURI(t *testing.T) { } func TestClient_AnalyticsQuery(t *testing.T) { - ctrl := gomock.NewController(t) - logger := NewMockLogger(ctrl) - metrics := NewMockMetrics(ctrl) - tests := []struct { - name string - statement string - params map[string]any - result any - setupMocks func(*MockclusterProvider, *MockresultProvider) - client *Client - wantErr error + name string + statement string + params map[string]any + result any + setup func(mocks *testMocks) *Client + wantErr error }{ { name: "success: Analytics query", statement: "SELECT * FROM `bucket`", params: nil, result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), - analyticsResult.EXPECT().Next().Return(true), - analyticsResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(mocks.queryResult, nil), + mocks.queryResult.EXPECT().Next().Return(true), + mocks.queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { data := `{"id": "1", "name": "test_analytics"}` return json.Unmarshal([]byte(data), arg) }), - analyticsResult.EXPECT().Next().Return(false), - analyticsResult.EXPECT().Err().Return(nil), - analyticsResult.EXPECT().Close().Return(nil)) - }, - wantErr: nil, - }, - { - name: "error: from cluster.AnalyticsQuery", - statement: "SELECT * FROM `bucket`", - params: nil, - result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, _ *MockresultProvider) { - cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(nil, assert.AnError) + mocks.queryResult.EXPECT().Next().Return(false), + mocks.queryResult.EXPECT().Err().Return(nil), + mocks.queryResult.EXPECT().Close().Return(nil)) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, }, { name: "error: failed to unmarshal Analytics query row", statement: "SELECT * FROM `bucket`", params: nil, result: &[]map[string]any{}, - setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), - analyticsResult.EXPECT().Next().Return(true), - analyticsResult.EXPECT().Row(gomock.Any()).Return(assert.AnError), - analyticsResult.EXPECT().Close().Return(nil)) + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(mocks.queryResult, nil), + mocks.queryResult.EXPECT().Next().Return(true), + mocks.queryResult.EXPECT().Row(gomock.Any()).Return(gocb.ErrDecodingFailure), + mocks.queryResult.EXPECT().Close().Return(nil)) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, - wantErr: assert.AnError, + wantErr: gocb.ErrDecodingFailure, }, { name: "error: failed to unmarshal analytics results into target", statement: "SELECT * FROM `bucket`", params: nil, - result: &struct{}{}, // This will cause unmarshal error if tempResults is not compatible - setupMocks: func(cluster *MockclusterProvider, analyticsResult *MockresultProvider) { - gomock.InOrder(cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(analyticsResult, nil), - analyticsResult.EXPECT().Next().Return(true), - analyticsResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { + result: &struct{}{}, + setup: func(mocks *testMocks) *Client { + gomock.InOrder(mocks.cluster.EXPECT().AnalyticsQuery(gomock.Any(), gomock.Any()).Return(mocks.queryResult, nil), + mocks.queryResult.EXPECT().Next().Return(true), + mocks.queryResult.EXPECT().Row(gomock.Any()).DoAndReturn(func(arg any) error { data := `{"id": "1", "name": "test_analytics"}` return json.Unmarshal([]byte(data), arg) }), - analyticsResult.EXPECT().Next().Return(false), - analyticsResult.EXPECT().Err().Return(nil), - analyticsResult.EXPECT().Close().Return(nil)) + mocks.queryResult.EXPECT().Next().Return(false), + mocks.queryResult.EXPECT().Err().Return(nil), + mocks.queryResult.EXPECT().Close().Return(nil)) + mocks.metrics.EXPECT().RecordHistogram(gomock.Any(), "app_couchbase_stats", gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Debug(gomock.Any()) + mocks.logger.EXPECT().Logf(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + mocks.logger.EXPECT().Errorf(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + return &Client{ + cluster: mocks.cluster, config: &Config{}, logger: mocks.logger, metrics: mocks.metrics, + } }, wantErr: errFailedToUnmarshalAnalytics, }, @@ -606,27 +857,14 @@ func TestClient_AnalyticsQuery(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cluster := NewMockclusterProvider(ctrl) - analyticsResult := NewMockresultProvider(ctrl) - - tt.setupMocks(cluster, analyticsResult) - - client := tt.client - if client == nil { - client = &Client{ - cluster: cluster, - config: &Config{}, - logger: logger, - metrics: metrics, - } - } - + mocks := newTestMocks(t) + client := tt.setup(mocks) err := client.AnalyticsQuery(context.Background(), tt.statement, tt.params, tt.result) if tt.wantErr != nil { assert.ErrorContains(t, err, tt.wantErr.Error()) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } diff --git a/pkg/gofr/datasource/couchbase/go.mod b/pkg/gofr/datasource/couchbase/go.mod new file mode 100644 index 000000000..f9976067a --- /dev/null +++ b/pkg/gofr/datasource/couchbase/go.mod @@ -0,0 +1,38 @@ +module gofr.dev/pkg/gofr/datasource/couchbase + +go 1.24.5 + +require ( + github.com/couchbase/gocb/v2 v2.10.0 + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/otel v1.37.0 + go.opentelemetry.io/otel/trace v1.37.0 + go.uber.org/mock v0.5.2 +) + +require ( + github.com/couchbase/gocbcore/v10 v10.7.0 // indirect + github.com/couchbase/gocbcoreps v0.1.3 // indirect + github.com/couchbase/goprotostellar v1.0.2 // indirect + github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/pkg/gofr/datasource/couchbase/go.sum b/pkg/gofr/datasource/couchbase/go.sum new file mode 100644 index 000000000..1dd68a76c --- /dev/null +++ b/pkg/gofr/datasource/couchbase/go.sum @@ -0,0 +1,181 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/couchbase/gocb/v2 v2.10.0 h1:NNxZ4okToU1Ylqp6F8tE41CEJQPhb2WjufryAkeubOk= +github.com/couchbase/gocb/v2 v2.10.0/go.mod h1:OSbMfQkP7ltbKiDZhsT2mGDhkQNmvGXxptKcxAUJQ2Y= +github.com/couchbase/gocbcore/v10 v10.7.0 h1:lAEi0PNeEGKOu8pWrPUdtLOT2oGr1J/UTdGHVPC3r/0= +github.com/couchbase/gocbcore/v10 v10.7.0/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI= +github.com/couchbase/gocbcoreps v0.1.3 h1:fILaKGCjxFIeCgAUG8FGmRDSpdrRggohOMKEgO9CUpg= +github.com/couchbase/gocbcoreps v0.1.3/go.mod h1:hBFpDNPnRno6HH5cRXExhqXYRmTsFJlFHQx7vztcXPk= +github.com/couchbase/goprotostellar v1.0.2 h1:yoPbAL9sCtcyZ5e/DcU5PRMOEFaJrF9awXYu3VPfGls= +github.com/couchbase/goprotostellar v1.0.2/go.mod h1:5/yqVnZlW2/NSbAWu1hPJCFBEwjxgpe0PFFOlRixnp4= +github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8 h1:MQfvw4BiLTuyR69FuA5Kex+tXUeLkH+/ucJfVL1/hkM= +github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 h1:lhGOw8rNG6RAadmmaJAF3PJ7MNt7rFuWG7BHCYMgnGE= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28/go.mod h1:o7T431UOfFVHDNvMBUmUxpHnhivwv7BziUao/nMl81E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/gofr/datasource/couchbase/interfaces.go b/pkg/gofr/datasource/couchbase/interfaces.go index 253bef9fd..7da20b2f3 100644 --- a/pkg/gofr/datasource/couchbase/interfaces.go +++ b/pkg/gofr/datasource/couchbase/interfaces.go @@ -8,27 +8,13 @@ import ( ) type Couchbase interface { - // Get retrieves a document by its key from the specified bucket. - // The result parameter should be a pointer to the struct where the document will be unmarshaled. - Get(ctx context.Context, bucket, key string, result any) error - - // Upsert inserts a new document or replaces an existing one in the specified bucket. - // The document parameter can be any Go type that can be marshaled into JSON. - Upsert(ctx context.Context, bucket, key string, document any, result any) error - - // Remove deletes a document by its key from the specified bucket. - Remove(ctx context.Context, bucket, key string) error - - // Query executes a N1QL query against the Couchbase cluster. - // The statement is the N1QL query string, and params are any query parameters. - // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. + Get(ctx context.Context, key string, result any) error + Insert(ctx context.Context, key string, document, result any) error + Upsert(ctx context.Context, key string, document any, result any) error + Remove(ctx context.Context, key string) error Query(ctx context.Context, statement string, params map[string]any, result any) error - - // AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. - // The statement is the Analytics query string, and params are any query parameters. - // The result parameter should be a pointer to a slice of structs or maps where the query results will be unmarshaled. AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error - + RunTransaction(ctx context.Context, logic func(attempt *gocb.TransactionAttemptContext) error) (*gocb.TransactionResult, error) Close(opts any) error } @@ -40,6 +26,7 @@ type clusterProvider interface { WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) Close(opts *gocb.ClusterCloseOptions) error + Transactions() transactionsProvider } // resultProvider is an interface that abstracts gocb.QueryResult and gocb.AnalyticsResult for easier testing. @@ -55,15 +42,29 @@ type bucketProvider interface { Collection(collectionName string) collectionProvider DefaultCollection() collectionProvider WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error + Scope(name string) scopeProvider } // collectionProvider is an interface that abstracts the gocb.Collection for easier testing. type collectionProvider interface { Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) + Insert(key string, value any, opts *gocb.InsertOptions) (*gocb.MutationResult, error) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) + LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) + MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) } type getResultProvider interface { Content(value any) error } + +// scopeProvider is an interface that abstracts the gocb.Scope for easier testing. +type scopeProvider interface { + Collection(name string) collectionProvider +} + +// transactionsProvider is an interface that abstracts the gocb.Transactions for easier testing. +type transactionsProvider interface { + Run(logic func(*gocb.TransactionAttemptContext) error, opts *gocb.TransactionOptions) (*gocb.TransactionResult, error) +} diff --git a/pkg/gofr/datasource/couchbase/logger.go b/pkg/gofr/datasource/couchbase/logger.go index 845f021c2..dcaa66827 100644 --- a/pkg/gofr/datasource/couchbase/logger.go +++ b/pkg/gofr/datasource/couchbase/logger.go @@ -12,19 +12,37 @@ type Logger interface { Debug(args ...any) Debugf(pattern string, args ...any) Logf(pattern string, args ...any) + Error(pattern string) Errorf(pattern string, args ...any) } // QueryLog represents a log entry for a Couchbase query. type QueryLog struct { - Query string `json:"query"` - Duration int64 `json:"duration"` + Query string `json:"query"` + Duration int64 `json:"duration"` + Key string `json:"key,omitempty"` + Statement string `json:"statement,omitempty"` + Parameters any `json:"parameters,omitempty"` } // PrettyPrint prints the query log in a human-readable format. func (ql *QueryLog) PrettyPrint(writer io.Writer) { - fmt.Fprintf(writer, "\u001b[38;5;8m%-32s \u001b[38;5;207m%-6s\u001b[0m %8d\u001b[38;5;8mµs\u001b[0m %s\n", - clean(ql.Query), "COUCHBASE", ql.Duration, "") + if ql.Key == "" && ql.Statement == "" { + return + } + + if ql.Parameters == nil { + ql.Parameters = "" + } + + query := ql.Query + if query == "" { + query = ql.Statement + } + + fmt.Fprintf(writer, "\u001B[38;5;8m%-32s \u001B[38;5;207m%-6s\u001B[0m %8d\u001B[38;5;8m5s\u001B[0m %s\n", + clean(query), "COUCHBASE", ql.Duration, + clean(strings.Join([]string{ql.Key + " " + fmt.Sprint(ql.Parameters)}, " "))) } // clean takes a string query as input and performs two operations to clean it up: diff --git a/pkg/gofr/datasource/couchbase/mock_interfaces.go b/pkg/gofr/datasource/couchbase/mock_interfaces.go index 002768cb0..58c63d3b4 100644 --- a/pkg/gofr/datasource/couchbase/mock_interfaces.go +++ b/pkg/gofr/datasource/couchbase/mock_interfaces.go @@ -71,17 +71,45 @@ func (mr *MockCouchbaseMockRecorder) Close(opts any) *gomock.Call { } // Get mocks base method. -func (m *MockCouchbase) Get(ctx context.Context, bucket, key string, result any) error { +func (m *MockCouchbase) Get(ctx context.Context, key string, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", ctx, bucket, key, result) + ret := m.ctrl.Call(m, "Get", ctx, key, result) ret0, _ := ret[0].(error) return ret0 } // Get indicates an expected call of Get. -func (mr *MockCouchbaseMockRecorder) Get(ctx, bucket, key, result any) *gomock.Call { +func (mr *MockCouchbaseMockRecorder) Get(ctx, key, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbase)(nil).Get), ctx, bucket, key, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbase)(nil).Get), ctx, key, result) +} + +// InsertMany mocks base method. +func (m *MockCouchbase) InsertMany(ctx context.Context, documents map[string]any, results any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertMany", ctx, documents, results) + ret0, _ := ret[0].(error) + return ret0 +} + +// InsertMany indicates an expected call of InsertMany. +func (mr *MockCouchbaseMockRecorder) InsertMany(ctx, documents, results any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertMany", reflect.TypeOf((*MockCouchbase)(nil).InsertMany), ctx, documents, results) +} + +// InsertOne mocks base method. +func (m *MockCouchbase) InsertOne(ctx context.Context, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertOne", ctx, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// InsertOne indicates an expected call of InsertOne. +func (mr *MockCouchbaseMockRecorder) InsertOne(ctx, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOne", reflect.TypeOf((*MockCouchbase)(nil).InsertOne), ctx, key, document, result) } // Query mocks base method. @@ -99,31 +127,46 @@ func (mr *MockCouchbaseMockRecorder) Query(ctx, statement, params, result any) * } // Remove mocks base method. -func (m *MockCouchbase) Remove(ctx context.Context, bucket, key string) error { +func (m *MockCouchbase) Remove(ctx context.Context, key string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Remove", ctx, bucket, key) + ret := m.ctrl.Call(m, "Remove", ctx, key) ret0, _ := ret[0].(error) return ret0 } // Remove indicates an expected call of Remove. -func (mr *MockCouchbaseMockRecorder) Remove(ctx, bucket, key any) *gomock.Call { +func (mr *MockCouchbaseMockRecorder) Remove(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCouchbase)(nil).Remove), ctx, bucket, key) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCouchbase)(nil).Remove), ctx, key) +} + +// RunTransaction mocks base method. +func (m *MockCouchbase) RunTransaction(ctx context.Context, logic func(*gocb.TransactionAttemptContext) error) (*gocb.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunTransaction", ctx, logic) + ret0, _ := ret[0].(*gocb.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RunTransaction indicates an expected call of RunTransaction. +func (mr *MockCouchbaseMockRecorder) RunTransaction(ctx, logic any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunTransaction", reflect.TypeOf((*MockCouchbase)(nil).RunTransaction), ctx, logic) } // Upsert mocks base method. -func (m *MockCouchbase) Upsert(ctx context.Context, bucket, key string, document, result any) error { +func (m *MockCouchbase) Upsert(ctx context.Context, key string, document, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Upsert", ctx, bucket, key, document, result) + ret := m.ctrl.Call(m, "Upsert", ctx, key, document, result) ret0, _ := ret[0].(error) return ret0 } // Upsert indicates an expected call of Upsert. -func (mr *MockCouchbaseMockRecorder) Upsert(ctx, bucket, key, document, result any) *gomock.Call { +func (mr *MockCouchbaseMockRecorder) Upsert(ctx, key, document, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockCouchbase)(nil).Upsert), ctx, bucket, key, document, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockCouchbase)(nil).Upsert), ctx, key, document, result) } // MockclusterProvider is a mock of clusterProvider interface. @@ -223,6 +266,20 @@ func (mr *MockclusterProviderMockRecorder) Query(statement, opts any) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockclusterProvider)(nil).Query), statement, opts) } +// Transactions mocks base method. +func (m *MockclusterProvider) Transactions() transactionsProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Transactions") + ret0, _ := ret[0].(transactionsProvider) + return ret0 +} + +// Transactions indicates an expected call of Transactions. +func (mr *MockclusterProviderMockRecorder) Transactions() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transactions", reflect.TypeOf((*MockclusterProvider)(nil).Transactions)) +} + // WaitUntilReady mocks base method. func (m *MockclusterProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { m.ctrl.T.Helper() @@ -369,6 +426,20 @@ func (mr *MockbucketProviderMockRecorder) DefaultCollection() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DefaultCollection", reflect.TypeOf((*MockbucketProvider)(nil).DefaultCollection)) } +// Scope mocks base method. +func (m *MockbucketProvider) Scope(name string) scopeProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Scope", name) + ret0, _ := ret[0].(scopeProvider) + return ret0 +} + +// Scope indicates an expected call of Scope. +func (mr *MockbucketProviderMockRecorder) Scope(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Scope", reflect.TypeOf((*MockbucketProvider)(nil).Scope), name) +} + // WaitUntilReady mocks base method. func (m *MockbucketProvider) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { m.ctrl.T.Helper() @@ -422,6 +493,51 @@ func (mr *MockcollectionProviderMockRecorder) Get(key, opts any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockcollectionProvider)(nil).Get), key, opts) } +// Insert mocks base method. +func (m *MockcollectionProvider) Insert(key string, value any, opts *gocb.InsertOptions) (*gocb.MutationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", key, value, opts) + ret0, _ := ret[0].(*gocb.MutationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Insert indicates an expected call of Insert. +func (mr *MockcollectionProviderMockRecorder) Insert(key, value, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockcollectionProvider)(nil).Insert), key, value, opts) +} + +// LookupIn mocks base method. +func (m *MockcollectionProvider) LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LookupIn", key, specs, opts) + ret0, _ := ret[0].(*gocb.LookupInResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LookupIn indicates an expected call of LookupIn. +func (mr *MockcollectionProviderMockRecorder) LookupIn(key, specs, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIn", reflect.TypeOf((*MockcollectionProvider)(nil).LookupIn), key, specs, opts) +} + +// MutateIn mocks base method. +func (m *MockcollectionProvider) MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MutateIn", key, specs, opts) + ret0, _ := ret[0].(*gocb.MutateInResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MutateIn indicates an expected call of MutateIn. +func (mr *MockcollectionProviderMockRecorder) MutateIn(key, specs, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MutateIn", reflect.TypeOf((*MockcollectionProvider)(nil).MutateIn), key, specs, opts) +} + // Remove mocks base method. func (m *MockcollectionProvider) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { m.ctrl.T.Helper() @@ -489,3 +605,80 @@ func (mr *MockgetResultProviderMockRecorder) Content(value any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Content", reflect.TypeOf((*MockgetResultProvider)(nil).Content), value) } + +// MockscopeProvider is a mock of scopeProvider interface. +type MockscopeProvider struct { + ctrl *gomock.Controller + recorder *MockscopeProviderMockRecorder + isgomock struct{} +} + +// MockscopeProviderMockRecorder is the mock recorder for MockscopeProvider. +type MockscopeProviderMockRecorder struct { + mock *MockscopeProvider +} + +// NewMockscopeProvider creates a new mock instance. +func NewMockscopeProvider(ctrl *gomock.Controller) *MockscopeProvider { + mock := &MockscopeProvider{ctrl: ctrl} + mock.recorder = &MockscopeProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockscopeProvider) EXPECT() *MockscopeProviderMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockscopeProvider) Collection(name string) collectionProvider { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection", name) + ret0, _ := ret[0].(collectionProvider) + return ret0 +} + +// Collection indicates an expected call of Collection. +func (mr *MockscopeProviderMockRecorder) Collection(name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockscopeProvider)(nil).Collection), name) +} + +// MocktransactionsProvider is a mock of transactionsProvider interface. +type MocktransactionsProvider struct { + ctrl *gomock.Controller + recorder *MocktransactionsProviderMockRecorder + isgomock struct{} +} + +// MocktransactionsProviderMockRecorder is the mock recorder for MocktransactionsProvider. +type MocktransactionsProviderMockRecorder struct { + mock *MocktransactionsProvider +} + +// NewMocktransactionsProvider creates a new mock instance. +func NewMocktransactionsProvider(ctrl *gomock.Controller) *MocktransactionsProvider { + mock := &MocktransactionsProvider{ctrl: ctrl} + mock.recorder = &MocktransactionsProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MocktransactionsProvider) EXPECT() *MocktransactionsProviderMockRecorder { + return m.recorder +} + +// Run mocks base method. +func (m *MocktransactionsProvider) Run(logic func(*gocb.TransactionAttemptContext) error, opts *gocb.TransactionOptions) (*gocb.TransactionResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Run", logic, opts) + ret0, _ := ret[0].(*gocb.TransactionResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Run indicates an expected call of Run. +func (mr *MocktransactionsProviderMockRecorder) Run(logic, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MocktransactionsProvider)(nil).Run), logic, opts) +} diff --git a/pkg/gofr/datasource/couchbase/mock_logger.go b/pkg/gofr/datasource/couchbase/mock_logger.go index aee5b4918..f69afa424 100644 --- a/pkg/gofr/datasource/couchbase/mock_logger.go +++ b/pkg/gofr/datasource/couchbase/mock_logger.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: pkg/gofr/datasource/couchbase/logger.go +// Source: logger.go // // Generated by this command: // -// mockgen -source=pkg/gofr/datasource/couchbase/logger.go -destination=pkg/gofr/datasource/couchbase/mock_logger.go -package=couchbase +// mockgen -source=logger.go -destination=mock_logger.go -package=couchbase // // Package couchbase is a generated GoMock package. @@ -72,6 +72,18 @@ func (mr *MockLoggerMockRecorder) Debugf(pattern any, args ...any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) } +// Error mocks base method. +func (m *MockLogger) Error(pattern string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Error", pattern) +} + +// Error indicates an expected call of Error. +func (mr *MockLoggerMockRecorder) Error(pattern any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), pattern) +} + // Errorf mocks base method. func (m *MockLogger) Errorf(pattern string, args ...any) { m.ctrl.T.Helper() diff --git a/pkg/gofr/datasource/couchbase/wrappers.go b/pkg/gofr/datasource/couchbase/wrappers.go new file mode 100644 index 000000000..6542c5ef2 --- /dev/null +++ b/pkg/gofr/datasource/couchbase/wrappers.go @@ -0,0 +1,190 @@ +package couchbase + +import ( + "time" + + "github.com/couchbase/gocb/v2" +) + +// analyticsResultWrapper is a wrapper around gocb.AnalyticsResult to implement the analyticsResultProvider interface. +type analyticsResultWrapper struct { + *gocb.AnalyticsResult +} + +// clusterWrapper is a wrapper around gocb.Cluster to implement the clusterProvider interface. +type clusterWrapper struct { + *gocb.Cluster +} + +// collectionWrapper is a wrapper around gocb.Collection to implement the collectionProvider interface. +type collectionWrapper struct { + *gocb.Collection +} + +// bucketWrapper is a wrapper around gocb.Bucket to implement the bucketProvider interface. +type bucketWrapper struct { + *gocb.Bucket +} + +// queryResultWrapper is a wrapper around gocb.QueryResult to implement the queryResultProvider interface. +type queryResultWrapper struct { + *gocb.QueryResult +} + +// scopeWrapper is a wrapper around gocb.Scope to implement the scopeProvider interface. +type scopeWrapper struct { + *gocb.Scope +} + +// transactionsWrapper is a wrapper around gocb.Transactions to implement the transactionsProvider interface. +type transactionsWrapper struct { + *gocb.Transactions +} + +type getResultWrapper struct { + *gocb.GetResult +} + +// Bucket returns a bucketProvider for the specified bucket name. +func (cw *clusterWrapper) Bucket(bucketName string) bucketProvider { + return &bucketWrapper{cw.Cluster.Bucket(bucketName)} +} + +// Query executes a N1QL query against the Couchbase cluster. +func (cw *clusterWrapper) Query(statement string, opts *gocb.QueryOptions) (resultProvider, error) { + res, err := cw.Cluster.Query(statement, opts) + if err != nil { + return nil, err + } + + return &queryResultWrapper{res}, nil +} + +// AnalyticsQuery executes an Analytics query against the Couchbase Analytics service. +func (cw *clusterWrapper) AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (resultProvider, error) { + res, err := cw.Cluster.AnalyticsQuery(statement, opts) + if err != nil { + return nil, err + } + + return &analyticsResultWrapper{res}, nil +} + +func (cw *clusterWrapper) Close(opts *gocb.ClusterCloseOptions) error { + return cw.Cluster.Close(opts) +} + +func (cw *clusterWrapper) Ping(opts *gocb.PingOptions) (*gocb.PingResult, error) { + return cw.Cluster.Ping(opts) +} + +// WaitUntilReady waits until the cluster is ready. +func (cw *clusterWrapper) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + return cw.Cluster.WaitUntilReady(timeout, opts) +} + +func (cw *clusterWrapper) Transactions() transactionsProvider { + return &transactionsWrapper{cw.Cluster.Transactions()} +} + +// Collection returns a collectionProvider for the specified collection name. +func (bw *bucketWrapper) Collection(name string) collectionProvider { + return &collectionWrapper{bw.Bucket.Collection(name)} +} + +// DefaultCollection returns the default collectionProvider for the bucket. +func (bw *bucketWrapper) DefaultCollection() collectionProvider { + return &collectionWrapper{bw.Bucket.DefaultCollection()} +} + +func (bw *bucketWrapper) Scope(name string) scopeProvider { + return &scopeWrapper{bw.Bucket.Scope(name)} +} + +func (bw *bucketWrapper) WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error { + return bw.Bucket.WaitUntilReady(timeout, opts) +} + +// Next returns true if there are more rows to be retrieved. +func (qrw *queryResultWrapper) Next() bool { + return qrw.QueryResult.Next() +} + +// Row unmarshals the current row into the value pointed to by the result parameter. +func (qrw *queryResultWrapper) Row(value any) error { + return qrw.QueryResult.Row(value) +} + +// Err returns the error, if any, that occurred during the rows iteration. +func (qrw *queryResultWrapper) Err() error { + return qrw.QueryResult.Err() +} + +// Close closes the query result. +func (qrw *queryResultWrapper) Close() error { + return qrw.QueryResult.Close() +} + +// Next returns true if there are more rows to be retrieved. +func (arw *analyticsResultWrapper) Next() bool { + return arw.AnalyticsResult.Next() +} + +// Row unmarshals the current row into the value pointed to by the result parameter. +func (arw *analyticsResultWrapper) Row(value any) error { + return arw.AnalyticsResult.Row(value) +} + +// Err returns the error, if any, that occurred during the rows iteration. +func (arw *analyticsResultWrapper) Err() error { + return arw.AnalyticsResult.Err() +} + +// Close closes the analytics result. +func (arw *analyticsResultWrapper) Close() error { + return arw.AnalyticsResult.Close() +} + +func (sw *scopeWrapper) Collection(name string) collectionProvider { + return &collectionWrapper{sw.Scope.Collection(name)} +} + +func (tw *transactionsWrapper) Run(logic func(*gocb.TransactionAttemptContext) error, opts *gocb.TransactionOptions) (*gocb.TransactionResult, error) { + return tw.Transactions.Run(logic, opts) +} + +func (grw *getResultWrapper) Content(value any) error { + return grw.GetResult.Content(value) +} + +// Upsert performs an upsert operation on the collection. +func (cw *collectionWrapper) Upsert(key string, value any, opts *gocb.UpsertOptions) (*gocb.MutationResult, error) { + return cw.Collection.Upsert(key, value, opts) +} + +// Get performs a get operation on the collection. +func (cw *collectionWrapper) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) { + res, err := cw.Collection.Get(key, opts) + if err != nil { + return nil, err + } + + return &getResultWrapper{res}, nil +} + +// Remove performs a remove operation on the collection. +func (cw *collectionWrapper) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { + return cw.Collection.Remove(key, opts) +} + +func (cw *collectionWrapper) Insert(key string, value any, opts *gocb.InsertOptions) (*gocb.MutationResult, error) { + return cw.Collection.Insert(key, value, opts) +} + +func (cw *collectionWrapper) LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) { + return cw.Collection.LookupIn(key, specs, opts) +} + +func (cw *collectionWrapper) MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) { + return cw.Collection.MutateIn(key, specs, opts) +} From 7edcfdd7d9bb606b9bd299c2aa4ced27b49a5386 Mon Sep 17 00:00:00 2001 From: olxandr Date: Sat, 19 Jul 2025 13:07:58 +0300 Subject: [PATCH 04/22] address Insert and Upsert methods duplication --- pkg/gofr/datasource/couchbase/couchbase.go | 46 ++++++++-------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index 1bbbd1203..58780f6e9 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -245,20 +245,20 @@ func (s *Scope) Collection(name string) *Collection { } } -// Upsert performs an upsert operation on the collection. -func (c *Collection) Upsert(ctx context.Context, key string, document, result any) error { +func (c *Collection) mutationOperation(ctx context.Context, opName, key string, document, result any, + op func(tracerCtx context.Context) (*gocb.MutationResult, error)) error { if c.collection == nil { return errBucketNotInitialized } - tracerCtx, span := c.client.addTrace(ctx, "Upsert", key) + tracerCtx, span := c.client.addTrace(ctx, opName, key) - mr, err := c.collection.Upsert(key, document, &gocb.UpsertOptions{Context: tracerCtx}) + defer c.client.sendOperationStats(&QueryLog{Query: opName, Key: key, Parameters: document}, time.Now(), opName, span) - defer c.client.sendOperationStats(&QueryLog{Query: "Upsert", Key: key, Parameters: document}, time.Now(), "Upsert", span) + mr, err := op(tracerCtx) if err != nil { - return fmt.Errorf("failed to Upsert document with key %s: %w", key, err) + return fmt.Errorf("failed to %s document with key %s: %w", opName, key, err) } switch r := result.(type) { @@ -273,32 +273,18 @@ func (c *Collection) Upsert(ctx context.Context, key string, document, result an return nil } +// Upsert performs an upsert operation on the collection. +func (c *Collection) Upsert(ctx context.Context, key string, document, result any) error { + return c.mutationOperation(ctx, "Upsert", key, document, result, func(tracerCtx context.Context) (*gocb.MutationResult, error) { + return c.collection.Upsert(key, document, &gocb.UpsertOptions{Context: tracerCtx}) + }) +} + // Insert inserts a new document in the collection. func (c *Collection) Insert(ctx context.Context, key string, document, result any) error { - if c.collection == nil { - return errBucketNotInitialized - } - - tracerCtx, span := c.client.addTrace(ctx, "Insert", key) - - mr, err := c.collection.Insert(key, document, &gocb.InsertOptions{Context: tracerCtx}) - - defer c.client.sendOperationStats(&QueryLog{Query: "Insert", Key: key, Parameters: document}, time.Now(), "Insert", span) - - if err != nil { - return fmt.Errorf("failed to Insert document with key %s: %w", key, err) - } - - switch r := result.(type) { - case *gocb.MutationResult: - *r = *mr - case **gocb.MutationResult: - *r = mr - default: - return errWrongResultType - } - - return nil + return c.mutationOperation(ctx, "Insert", key, document, result, func(tracerCtx context.Context) (*gocb.MutationResult, error) { + return c.collection.Insert(key, document, &gocb.InsertOptions{Context: tracerCtx}) + }) } // Get performs a get operation on the collection. From d843a08edf04a4d78e7dd40fe7cf3388bb90f9bb Mon Sep 17 00:00:00 2001 From: olxandr Date: Sat, 19 Jul 2025 13:38:25 +0300 Subject: [PATCH 05/22] go.work.sum conflicts resolve --- go.work.sum | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/go.work.sum b/go.work.sum index f1572782f..ea8c1d9a2 100644 --- a/go.work.sum +++ b/go.work.sum @@ -718,10 +718,7 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= -<<<<<<< HEAD github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= -======= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= @@ -786,7 +783,6 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1Ig github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -<<<<<<< HEAD github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= @@ -794,12 +790,6 @@ github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9 github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -======= -github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= -github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= @@ -827,10 +817,7 @@ github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrb github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -<<<<<<< HEAD github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= -======= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= @@ -868,10 +855,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= -<<<<<<< HEAD github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -======= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -880,6 +864,7 @@ github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= @@ -1150,10 +1135,7 @@ go.opentelemetry.io/contrib/detectors/gcp v1.33.0 h1:FVPoXEoILwgbZUu4X7YSgsESsAm go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c9s2SOlgoSRyMpNVzUj2fZqI= go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= -<<<<<<< HEAD go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= -======= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= @@ -1161,6 +1143,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0. go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= @@ -1168,6 +1151,7 @@ go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6c go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= @@ -1180,10 +1164,12 @@ go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzau go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= @@ -1192,6 +1178,7 @@ go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06F go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -1503,10 +1490,7 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20250425173222-7b384671a google.golang.org/genproto/googleapis/bytestream v0.0.0-20250428153025-10db94c68c34/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9 h1:YI36gCL8AQMhzYN6+jH8PdV/iZ0On+Zd0rO/7lCH3k8= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= -<<<<<<< HEAD google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ= -======= ->>>>>>> d612e1c8 (init couchbase integration with basic methods and mocks) google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= From 3ccb022976e7c53c22a8324ccb40d0dea362d435 Mon Sep 17 00:00:00 2001 From: olxandr Date: Sat, 19 Jul 2025 14:08:06 +0300 Subject: [PATCH 06/22] remove unused wrappers --- .../datasource/couchbase/couchbase_test.go | 17 +++--- pkg/gofr/datasource/couchbase/interfaces.go | 2 - pkg/gofr/datasource/couchbase/logger.go | 2 +- .../datasource/couchbase/mock_interfaces.go | 56 ++----------------- pkg/gofr/datasource/couchbase/wrappers.go | 12 +--- 5 files changed, 18 insertions(+), 71 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase_test.go b/pkg/gofr/datasource/couchbase/couchbase_test.go index 7609d8c16..3df627d7b 100644 --- a/pkg/gofr/datasource/couchbase/couchbase_test.go +++ b/pkg/gofr/datasource/couchbase/couchbase_test.go @@ -1,7 +1,6 @@ package couchbase import ( - "context" "encoding/json" "errors" "testing" @@ -142,7 +141,7 @@ func TestClient_Upsert(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.Upsert(context.Background(), tt.key, tt.document, tt.result) + err := client.Upsert(t.Context(), tt.key, tt.document, tt.result) assert.ErrorIs(t, err, tt.wantErr) }) @@ -214,7 +213,7 @@ func TestClient_Insert(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.Insert(context.Background(), tt.key, tt.document, tt.result) + err := client.Insert(t.Context(), tt.key, tt.document, tt.result) assert.ErrorIs(t, err, tt.wantErr) }) @@ -300,7 +299,7 @@ func TestClient_Get(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.Get(context.Background(), tt.key, tt.result) + err := client.Get(t.Context(), tt.key, tt.result) assert.ErrorIs(t, err, tt.wantErr) }) @@ -364,7 +363,7 @@ func TestClient_Remove(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.Remove(context.Background(), tt.key) + err := client.Remove(t.Context(), tt.key) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) @@ -546,7 +545,7 @@ func TestClient_RunTransaction(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - _, err := client.RunTransaction(context.Background(), tt.logic) + _, err := client.RunTransaction(t.Context(), tt.logic) if tt.wantErr != nil { assert.ErrorContains(t, err, tt.wantErr.Error()) } else { @@ -636,7 +635,7 @@ func TestClient_Query(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.Query(context.Background(), tt.statement, tt.params, tt.result) + err := client.Query(t.Context(), tt.statement, tt.params, tt.result) if tt.wantErr != nil { assert.ErrorContains(t, err, tt.wantErr.Error()) @@ -721,7 +720,7 @@ func TestClient_HealthCheck(t *testing.T) { }, } - health, err := client.HealthCheck(context.Background()) + health, err := client.HealthCheck(t.Context()) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.wantStatus, health.(*Health).Status) @@ -859,7 +858,7 @@ func TestClient_AnalyticsQuery(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - err := client.AnalyticsQuery(context.Background(), tt.statement, tt.params, tt.result) + err := client.AnalyticsQuery(t.Context(), tt.statement, tt.params, tt.result) if tt.wantErr != nil { assert.ErrorContains(t, err, tt.wantErr.Error()) diff --git a/pkg/gofr/datasource/couchbase/interfaces.go b/pkg/gofr/datasource/couchbase/interfaces.go index 7da20b2f3..0d9da69bf 100644 --- a/pkg/gofr/datasource/couchbase/interfaces.go +++ b/pkg/gofr/datasource/couchbase/interfaces.go @@ -51,8 +51,6 @@ type collectionProvider interface { Insert(key string, value any, opts *gocb.InsertOptions) (*gocb.MutationResult, error) Get(key string, opts *gocb.GetOptions) (getResultProvider, error) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) - LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) - MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) } type getResultProvider interface { diff --git a/pkg/gofr/datasource/couchbase/logger.go b/pkg/gofr/datasource/couchbase/logger.go index dcaa66827..535625cd8 100644 --- a/pkg/gofr/datasource/couchbase/logger.go +++ b/pkg/gofr/datasource/couchbase/logger.go @@ -40,7 +40,7 @@ func (ql *QueryLog) PrettyPrint(writer io.Writer) { query = ql.Statement } - fmt.Fprintf(writer, "\u001B[38;5;8m%-32s \u001B[38;5;207m%-6s\u001B[0m %8d\u001B[38;5;8m5s\u001B[0m %s\n", + fmt.Fprintf(writer, "\u001B[38;5;8m%-32s \u001B[38;5;207m%-6s\u001B[0m %8d\u001B[38;5;8m\b 5s\u001B[0m %s\n", clean(query), "COUCHBASE", ql.Duration, clean(strings.Join([]string{ql.Key + " " + fmt.Sprint(ql.Parameters)}, " "))) } diff --git a/pkg/gofr/datasource/couchbase/mock_interfaces.go b/pkg/gofr/datasource/couchbase/mock_interfaces.go index 58c63d3b4..21208a7ce 100644 --- a/pkg/gofr/datasource/couchbase/mock_interfaces.go +++ b/pkg/gofr/datasource/couchbase/mock_interfaces.go @@ -84,32 +84,18 @@ func (mr *MockCouchbaseMockRecorder) Get(ctx, key, result any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbase)(nil).Get), ctx, key, result) } -// InsertMany mocks base method. -func (m *MockCouchbase) InsertMany(ctx context.Context, documents map[string]any, results any) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertMany", ctx, documents, results) - ret0, _ := ret[0].(error) - return ret0 -} - -// InsertMany indicates an expected call of InsertMany. -func (mr *MockCouchbaseMockRecorder) InsertMany(ctx, documents, results any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertMany", reflect.TypeOf((*MockCouchbase)(nil).InsertMany), ctx, documents, results) -} - -// InsertOne mocks base method. -func (m *MockCouchbase) InsertOne(ctx context.Context, key string, document, result any) error { +// Insert mocks base method. +func (m *MockCouchbase) Insert(ctx context.Context, key string, document, result any) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InsertOne", ctx, key, document, result) + ret := m.ctrl.Call(m, "Insert", ctx, key, document, result) ret0, _ := ret[0].(error) return ret0 } -// InsertOne indicates an expected call of InsertOne. -func (mr *MockCouchbaseMockRecorder) InsertOne(ctx, key, document, result any) *gomock.Call { +// Insert indicates an expected call of Insert. +func (mr *MockCouchbaseMockRecorder) Insert(ctx, key, document, result any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOne", reflect.TypeOf((*MockCouchbase)(nil).InsertOne), ctx, key, document, result) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockCouchbase)(nil).Insert), ctx, key, document, result) } // Query mocks base method. @@ -508,36 +494,6 @@ func (mr *MockcollectionProviderMockRecorder) Insert(key, value, opts any) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockcollectionProvider)(nil).Insert), key, value, opts) } -// LookupIn mocks base method. -func (m *MockcollectionProvider) LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LookupIn", key, specs, opts) - ret0, _ := ret[0].(*gocb.LookupInResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LookupIn indicates an expected call of LookupIn. -func (mr *MockcollectionProviderMockRecorder) LookupIn(key, specs, opts any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIn", reflect.TypeOf((*MockcollectionProvider)(nil).LookupIn), key, specs, opts) -} - -// MutateIn mocks base method. -func (m *MockcollectionProvider) MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MutateIn", key, specs, opts) - ret0, _ := ret[0].(*gocb.MutateInResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// MutateIn indicates an expected call of MutateIn. -func (mr *MockcollectionProviderMockRecorder) MutateIn(key, specs, opts any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MutateIn", reflect.TypeOf((*MockcollectionProvider)(nil).MutateIn), key, specs, opts) -} - // Remove mocks base method. func (m *MockcollectionProvider) Remove(key string, opts *gocb.RemoveOptions) (*gocb.MutationResult, error) { m.ctrl.T.Helper() diff --git a/pkg/gofr/datasource/couchbase/wrappers.go b/pkg/gofr/datasource/couchbase/wrappers.go index 6542c5ef2..2037be072 100644 --- a/pkg/gofr/datasource/couchbase/wrappers.go +++ b/pkg/gofr/datasource/couchbase/wrappers.go @@ -149,7 +149,9 @@ func (sw *scopeWrapper) Collection(name string) collectionProvider { return &collectionWrapper{sw.Scope.Collection(name)} } -func (tw *transactionsWrapper) Run(logic func(*gocb.TransactionAttemptContext) error, opts *gocb.TransactionOptions) (*gocb.TransactionResult, error) { +func (tw *transactionsWrapper) Run( + logic func(*gocb.TransactionAttemptContext) error, opts *gocb.TransactionOptions, +) (*gocb.TransactionResult, error) { return tw.Transactions.Run(logic, opts) } @@ -180,11 +182,3 @@ func (cw *collectionWrapper) Remove(key string, opts *gocb.RemoveOptions) (*gocb func (cw *collectionWrapper) Insert(key string, value any, opts *gocb.InsertOptions) (*gocb.MutationResult, error) { return cw.Collection.Insert(key, value, opts) } - -func (cw *collectionWrapper) LookupIn(key string, specs []gocb.LookupInSpec, opts *gocb.LookupInOptions) (*gocb.LookupInResult, error) { - return cw.Collection.LookupIn(key, specs, opts) -} - -func (cw *collectionWrapper) MutateIn(key string, specs []gocb.MutateInSpec, opts *gocb.MutateInOptions) (*gocb.MutateInResult, error) { - return cw.Collection.MutateIn(key, specs, opts) -} From c2992bc1af9c2dc87c9be0ae641dc8fadfcc4fc9 Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:26:11 +0300 Subject: [PATCH 07/22] restore go.mod to its original state --- go.mod | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 292742293..f44dac821 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/go-redis/redismock/v9 v9.2.0 github.com/go-sql-driver/mysql v1.9.3 github.com/gogo/protobuf v1.3.2 - github.com/golang-jwt/jwt/v5 v5.2.2 + github.com/golang-jwt/jwt/v5 v5.2.3 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.3 @@ -40,15 +40,15 @@ require ( golang.org/x/sync v0.16.0 golang.org/x/term v0.33.0 golang.org/x/text v0.27.0 - google.golang.org/api v0.241.0 - google.golang.org/grpc v1.73.0 + google.golang.org/api v0.243.0 + google.golang.org/grpc v1.74.2 google.golang.org/protobuf v1.36.6 - modernc.org/sqlite v1.38.0 + modernc.org/sqlite v1.38.1 ) require ( cloud.google.com/go v0.120.0 // indirect - cloud.google.com/go/auth v0.16.2 // indirect + cloud.google.com/go/auth v0.16.3 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect cloud.google.com/go/iam v1.5.2 // indirect @@ -56,11 +56,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/couchbase/gocb/v2 v2.10.0 // indirect - github.com/couchbase/gocbcore/v10 v10.7.0 // indirect - github.com/couchbase/gocbcoreps v0.1.3 // indirect - github.com/couchbase/goprotostellar v1.0.2 // indirect - github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -68,11 +63,10 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.14.2 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -97,18 +91,16 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect go.opentelemetry.io/proto/otlp v1.7.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.39.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/net v0.41.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/net v0.42.0 // indirect golang.org/x/sys v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect - google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.65.10 // indirect + modernc.org/libc v1.66.3 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect ) From 5c3aadf60f8a868f9b65f5134bc1de0ff0c975ea Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:28:32 +0300 Subject: [PATCH 08/22] add couchbase to mock container --- pkg/gofr/container/mock_container.go | 8 +- pkg/gofr/container/mock_datasources.go | 388 ++++++++++++++++++++++++- 2 files changed, 378 insertions(+), 18 deletions(-) diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go index 095bb3361..fe9f0afed 100644 --- a/pkg/gofr/container/mock_container.go +++ b/pkg/gofr/container/mock_container.go @@ -27,6 +27,7 @@ type Mocks struct { SurrealDB *MockSurrealDB Elasticsearch *MockElasticsearch PubSub *MockPubSubProvider + Couchbase *MockCouchbase File *file.MockFileSystemProvider HTTPService *service.MockHTTP Metrics *MockMetrics @@ -101,6 +102,9 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { pubsubMock := NewMockPubSubProvider(ctrl) container.PubSub = pubsubMock + couchbaseMock := NewMockCouchbase(ctrl) + container.Couchbase = couchbaseMock + var httpMock *service.MockHTTP container.Services = make(map[string]service.HTTP) @@ -134,6 +138,7 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { SurrealDB: surrealMock, Elasticsearch: elasticsearchMock, PubSub: pubsubMock, + Couchbase: couchbaseMock, Metrics: mockMetrics, } @@ -144,8 +149,7 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { return container, &mocks } -type MockPubSub struct { -} +type MockPubSub struct{} func (*MockPubSub) Query(_ context.Context, _ string, _ ...any) ([]byte, error) { return nil, nil diff --git a/pkg/gofr/container/mock_datasources.go b/pkg/gofr/container/mock_datasources.go index 3dc04196b..e0976aa82 100644 --- a/pkg/gofr/container/mock_datasources.go +++ b/pkg/gofr/container/mock_datasources.go @@ -10794,22 +10794,6 @@ type MockPubSubProvider struct { isgomock struct{} } -func (m *MockPubSubProvider) Query(ctx context.Context, query string, args ...any) ([]byte, error) { - m.ctrl.T.Helper() - callArgs := append([]any{ctx, query}, args...) - ret := m.ctrl.Call(m, "Query", callArgs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Query indicates an expected call of Query. -func (mr *MockPubSubProviderMockRecorder) Query(ctx, query any, args ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockPubSubProvider)(nil).Query), - append([]any{ctx, query}, args...)..., ) -} - // MockPubSubProviderMockRecorder is the mock recorder for MockPubSubProvider. type MockPubSubProviderMockRecorder struct { mock *MockPubSubProvider @@ -10909,6 +10893,26 @@ func (mr *MockPubSubProviderMockRecorder) Publish(ctx, topic, message any) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Publish", reflect.TypeOf((*MockPubSubProvider)(nil).Publish), ctx, topic, message) } +// Query mocks base method. +func (m *MockPubSubProvider) Query(ctx context.Context, query string, args ...any) ([]byte, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, query} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Query", varargs...) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockPubSubProviderMockRecorder) Query(ctx, query any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, query}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockPubSubProvider)(nil).Query), varargs...) +} + // Subscribe mocks base method. func (m *MockPubSubProvider) Subscribe(ctx context.Context, topic string) (*pubsub.Message, error) { m.ctrl.T.Helper() @@ -13397,3 +13401,355 @@ func (mr *MockElasticsearchProviderMockRecorder) UseTracer(tracer any) *gomock.C mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockElasticsearchProvider)(nil).UseTracer), tracer) } + +// MockCouchbase is a mock of Couchbase interface. +type MockCouchbase struct { + ctrl *gomock.Controller + recorder *MockCouchbaseMockRecorder + isgomock struct{} +} + +// MockCouchbaseMockRecorder is the mock recorder for MockCouchbase. +type MockCouchbaseMockRecorder struct { + mock *MockCouchbase +} + +// NewMockCouchbase creates a new mock instance. +func NewMockCouchbase(ctrl *gomock.Controller) *MockCouchbase { + mock := &MockCouchbase{ctrl: ctrl} + mock.recorder = &MockCouchbaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCouchbase) EXPECT() *MockCouchbaseMockRecorder { + return m.recorder +} + +// AnalyticsQuery mocks base method. +func (m *MockCouchbase) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnalyticsQuery", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// AnalyticsQuery indicates an expected call of AnalyticsQuery. +func (mr *MockCouchbaseMockRecorder) AnalyticsQuery(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockCouchbase)(nil).AnalyticsQuery), ctx, statement, params, result) +} + +// Close mocks base method. +func (m *MockCouchbase) Close(opts any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockCouchbaseMockRecorder) Close(opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCouchbase)(nil).Close), opts) +} + +// Get mocks base method. +func (m *MockCouchbase) Get(ctx context.Context, key string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, key, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockCouchbaseMockRecorder) Get(ctx, key, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbase)(nil).Get), ctx, key, result) +} + +// HealthCheck mocks base method. +func (m *MockCouchbase) HealthCheck(arg0 context.Context) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthCheck", arg0) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HealthCheck indicates an expected call of HealthCheck. +func (mr *MockCouchbaseMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockCouchbase)(nil).HealthCheck), arg0) +} + +// Insert mocks base method. +func (m *MockCouchbase) Insert(ctx context.Context, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockCouchbaseMockRecorder) Insert(ctx, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockCouchbase)(nil).Insert), ctx, key, document, result) +} + +// Query mocks base method. +func (m *MockCouchbase) Query(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockCouchbaseMockRecorder) Query(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockCouchbase)(nil).Query), ctx, statement, params, result) +} + +// Remove mocks base method. +func (m *MockCouchbase) Remove(ctx context.Context, key string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx, key) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockCouchbaseMockRecorder) Remove(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCouchbase)(nil).Remove), ctx, key) +} + +// RunTransaction mocks base method. +func (m *MockCouchbase) RunTransaction(ctx context.Context, logic func(any) error) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunTransaction", ctx, logic) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RunTransaction indicates an expected call of RunTransaction. +func (mr *MockCouchbaseMockRecorder) RunTransaction(ctx, logic any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunTransaction", reflect.TypeOf((*MockCouchbase)(nil).RunTransaction), ctx, logic) +} + +// Upsert mocks base method. +func (m *MockCouchbase) Upsert(ctx context.Context, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upsert", ctx, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Upsert indicates an expected call of Upsert. +func (mr *MockCouchbaseMockRecorder) Upsert(ctx, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockCouchbase)(nil).Upsert), ctx, key, document, result) +} + +// MockCouchbaseProvider is a mock of CouchbaseProvider interface. +type MockCouchbaseProvider struct { + ctrl *gomock.Controller + recorder *MockCouchbaseProviderMockRecorder + isgomock struct{} +} + +// MockCouchbaseProviderMockRecorder is the mock recorder for MockCouchbaseProvider. +type MockCouchbaseProviderMockRecorder struct { + mock *MockCouchbaseProvider +} + +// NewMockCouchbaseProvider creates a new mock instance. +func NewMockCouchbaseProvider(ctrl *gomock.Controller) *MockCouchbaseProvider { + mock := &MockCouchbaseProvider{ctrl: ctrl} + mock.recorder = &MockCouchbaseProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCouchbaseProvider) EXPECT() *MockCouchbaseProviderMockRecorder { + return m.recorder +} + +// AnalyticsQuery mocks base method. +func (m *MockCouchbaseProvider) AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnalyticsQuery", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// AnalyticsQuery indicates an expected call of AnalyticsQuery. +func (mr *MockCouchbaseProviderMockRecorder) AnalyticsQuery(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnalyticsQuery", reflect.TypeOf((*MockCouchbaseProvider)(nil).AnalyticsQuery), ctx, statement, params, result) +} + +// Close mocks base method. +func (m *MockCouchbaseProvider) Close(opts any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close", opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockCouchbaseProviderMockRecorder) Close(opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCouchbaseProvider)(nil).Close), opts) +} + +// Connect mocks base method. +func (m *MockCouchbaseProvider) Connect() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Connect") +} + +// Connect indicates an expected call of Connect. +func (mr *MockCouchbaseProviderMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockCouchbaseProvider)(nil).Connect)) +} + +// Get mocks base method. +func (m *MockCouchbaseProvider) Get(ctx context.Context, key string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, key, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockCouchbaseProviderMockRecorder) Get(ctx, key, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCouchbaseProvider)(nil).Get), ctx, key, result) +} + +// HealthCheck mocks base method. +func (m *MockCouchbaseProvider) HealthCheck(arg0 context.Context) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthCheck", arg0) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HealthCheck indicates an expected call of HealthCheck. +func (mr *MockCouchbaseProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockCouchbaseProvider)(nil).HealthCheck), arg0) +} + +// Insert mocks base method. +func (m *MockCouchbaseProvider) Insert(ctx context.Context, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockCouchbaseProviderMockRecorder) Insert(ctx, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockCouchbaseProvider)(nil).Insert), ctx, key, document, result) +} + +// Query mocks base method. +func (m *MockCouchbaseProvider) Query(ctx context.Context, statement string, params map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, statement, params, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockCouchbaseProviderMockRecorder) Query(ctx, statement, params, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockCouchbaseProvider)(nil).Query), ctx, statement, params, result) +} + +// Remove mocks base method. +func (m *MockCouchbaseProvider) Remove(ctx context.Context, key string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx, key) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockCouchbaseProviderMockRecorder) Remove(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCouchbaseProvider)(nil).Remove), ctx, key) +} + +// RunTransaction mocks base method. +func (m *MockCouchbaseProvider) RunTransaction(ctx context.Context, logic func(any) error) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunTransaction", ctx, logic) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RunTransaction indicates an expected call of RunTransaction. +func (mr *MockCouchbaseProviderMockRecorder) RunTransaction(ctx, logic any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunTransaction", reflect.TypeOf((*MockCouchbaseProvider)(nil).RunTransaction), ctx, logic) +} + +// Upsert mocks base method. +func (m *MockCouchbaseProvider) Upsert(ctx context.Context, key string, document, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Upsert", ctx, key, document, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Upsert indicates an expected call of Upsert. +func (mr *MockCouchbaseProviderMockRecorder) Upsert(ctx, key, document, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockCouchbaseProvider)(nil).Upsert), ctx, key, document, result) +} + +// UseLogger mocks base method. +func (m *MockCouchbaseProvider) UseLogger(logger any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseLogger", logger) +} + +// UseLogger indicates an expected call of UseLogger. +func (mr *MockCouchbaseProviderMockRecorder) UseLogger(logger any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockCouchbaseProvider)(nil).UseLogger), logger) +} + +// UseMetrics mocks base method. +func (m *MockCouchbaseProvider) UseMetrics(metrics any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseMetrics", metrics) +} + +// UseMetrics indicates an expected call of UseMetrics. +func (mr *MockCouchbaseProviderMockRecorder) UseMetrics(metrics any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockCouchbaseProvider)(nil).UseMetrics), metrics) +} + +// UseTracer mocks base method. +func (m *MockCouchbaseProvider) UseTracer(tracer any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseTracer", tracer) +} + +// UseTracer indicates an expected call of UseTracer. +func (mr *MockCouchbaseProviderMockRecorder) UseTracer(tracer any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockCouchbaseProvider)(nil).UseTracer), tracer) +} From b269c4df5e205c9e68619ea93dc4d79029e2e466 Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:35:15 +0300 Subject: [PATCH 09/22] make methods that aren't exposed via interface private --- pkg/gofr/datasource/couchbase/couchbase.go | 31 ++++++------------- .../datasource/couchbase/couchbase_test.go | 12 +++---- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index 58780f6e9..fea0126b4 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -205,8 +205,8 @@ func (c *Client) HealthCheck(_ context.Context) (any, error) { return &h, nil } -// DefaultCollection returns a handle for the default collection. -func (c *Client) DefaultCollection() *Collection { +// defaultCollection returns a handle for the default collection. +func (c *Client) defaultCollection() *Collection { if c.bucket == nil { c.logger.Error("bucket not initialized") @@ -219,8 +219,8 @@ func (c *Client) DefaultCollection() *Collection { } } -// Scope returns a handle for a specific scope. -func (c *Client) Scope(name string) *Scope { +// scope returns a handle for a specific scope. +func (c *Client) scope(name string) *Scope { if c.bucket == nil { c.logger.Error("bucket not initialized") @@ -233,20 +233,9 @@ func (c *Client) Scope(name string) *Scope { } } -// Collection returns a handle for a specific collection within the scope. -func (s *Scope) Collection(name string) *Collection { - if s.scope == nil { - return &Collection{client: s.client} - } - - return &Collection{ - collection: s.scope.Collection(name), - client: s.client, - } -} - func (c *Collection) mutationOperation(ctx context.Context, opName, key string, document, result any, - op func(tracerCtx context.Context) (*gocb.MutationResult, error)) error { + op func(tracerCtx context.Context) (*gocb.MutationResult, error), +) error { if c.collection == nil { return errBucketNotInitialized } @@ -453,22 +442,22 @@ func (c *Client) RunTransaction(ctx context.Context, logic func(t *gocb.Transact // Get performs a get operation on the default collection. func (c *Client) Get(ctx context.Context, key string, result any) error { - return c.DefaultCollection().Get(ctx, key, result) + return c.defaultCollection().Get(ctx, key, result) } // Insert inserts a new document in the default collection. func (c *Client) Insert(ctx context.Context, key string, document, result any) error { - return c.DefaultCollection().Insert(ctx, key, document, result) + return c.defaultCollection().Insert(ctx, key, document, result) } // Upsert performs an upsert operation on the default collection. func (c *Client) Upsert(ctx context.Context, key string, document, result any) error { - return c.DefaultCollection().Upsert(ctx, key, document, result) + return c.defaultCollection().Upsert(ctx, key, document, result) } // Remove performs a remove operation on the default collection. func (c *Client) Remove(ctx context.Context, key string) error { - return c.DefaultCollection().Remove(ctx, key) + return c.defaultCollection().Remove(ctx, key) } // Close closes the connection to the Couchbase cluster. diff --git a/pkg/gofr/datasource/couchbase/couchbase_test.go b/pkg/gofr/datasource/couchbase/couchbase_test.go index 3df627d7b..d9fd4d60a 100644 --- a/pkg/gofr/datasource/couchbase/couchbase_test.go +++ b/pkg/gofr/datasource/couchbase/couchbase_test.go @@ -412,7 +412,7 @@ func TestClient_DefaultCollection(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - got := client.DefaultCollection() + got := client.defaultCollection() // We cannot directly compare the collection, so we check for nil and non-nil cases. if tt.wantCollection == nil { @@ -465,7 +465,7 @@ func TestClient_Scope(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mocks := newTestMocks(t) client := tt.setup(mocks) - got := client.Scope(tt.scopeName) + got := client.scope(tt.scopeName) if tt.wantScope == nil { assert.Nil(t, got) @@ -480,7 +480,7 @@ func TestClient_RunTransaction(t *testing.T) { tests := []struct { name string setup func(mocks *testMocks) *Client - logic func(t *gocb.TransactionAttemptContext) error + logic func(any) error wantErr error }{ { @@ -501,7 +501,7 @@ func TestClient_RunTransaction(t *testing.T) { tracer: noop.NewTracerProvider().Tracer("test"), } }, - logic: func(*gocb.TransactionAttemptContext) error { + logic: func(any) error { return nil }, }, @@ -510,7 +510,7 @@ func TestClient_RunTransaction(t *testing.T) { setup: func(*testMocks) *Client { return &Client{cluster: nil} }, - logic: func(*gocb.TransactionAttemptContext) error { + logic: func(any) error { return nil }, wantErr: errClustertNotInitialized, @@ -533,7 +533,7 @@ func TestClient_RunTransaction(t *testing.T) { tracer: noop.NewTracerProvider().Tracer("test"), } }, - logic: func(*gocb.TransactionAttemptContext) error { + logic: func(any) error { return errLogic }, wantErr: errMockTransaction, From 15fd0d678d187a8726a8afeaa30b1552394678fa Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:37:18 +0300 Subject: [PATCH 10/22] refactor Client methods so they match the interface methods signatures --- pkg/gofr/datasource/couchbase/couchbase.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index fea0126b4..388cab0af 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -105,38 +105,36 @@ func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method st } } -func (c *Client) Connect() error { +func (c *Client) Connect() { uri, err := c.generateCouchbaseURI() if err != nil { c.logger.Errorf("error generating Couchbase URI: %v", err) - return err + return } c.logger.Debugf("connecting to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) if err := c.establishConnection(uri); err != nil { c.logger.Errorf("error while connecting to Couchbase, err:%v", err) - return err + return } if err := c.waitForClusterReady(); err != nil { c.logger.Errorf("could not connect to Couchbase at %v due to err: %v", c.config.Host, err) - return err + return } c.bucket = c.cluster.Bucket(c.config.Bucket) if err := c.waitForBucketReady(); err != nil { c.logger.Errorf("could not connect to bucket %v at %v due to err: %v", c.config.Bucket, c.config.Host, err) - return err + return } couchbaseBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} c.metrics.NewHistogram("app_couchbase_stats", "Response time of Couchbase queries in milliseconds.", couchbaseBuckets...) c.logger.Logf("connected to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) - - return nil } func (c *Client) generateCouchbaseURI() (string, error) { @@ -245,7 +243,6 @@ func (c *Collection) mutationOperation(ctx context.Context, opName, key string, defer c.client.sendOperationStats(&QueryLog{Query: opName, Key: key, Parameters: document}, time.Now(), opName, span) mr, err := op(tracerCtx) - if err != nil { return fmt.Errorf("failed to %s document with key %s: %w", opName, key, err) } @@ -418,7 +415,7 @@ func executeQuery(queryFn func() (resultProvider, error), queryType string, resu } // RunTransaction executes a transaction. -func (c *Client) RunTransaction(ctx context.Context, logic func(t *gocb.TransactionAttemptContext) error) (*gocb.TransactionResult, error) { +func (c *Client) RunTransaction(ctx context.Context, logic func(any) error) (any, error) { if c.cluster == nil { return nil, errClustertNotInitialized } @@ -427,9 +424,14 @@ func (c *Client) RunTransaction(ctx context.Context, logic func(t *gocb.Transact startTime := time.Now() + // Wrap the generic logic function to match the expected signature + wrappedLogic := func(t *gocb.TransactionAttemptContext) error { + return logic(t) + } + // gocb transactions are not directly context-aware in the Run method signature in the same way as other operations. // The context is passed down to operations within the transaction lambda. - result, err := c.cluster.Transactions().Run(logic, nil) + result, err := c.cluster.Transactions().Run(wrappedLogic, nil) defer c.sendOperationStats(&QueryLog{Query: "RunTransaction"}, startTime, "RunTransaction", span) From a053ab1bd89df42e6b668327201632d4678809c7 Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:39:07 +0300 Subject: [PATCH 11/22] add docs --- docs/datasources/couchbase/page.md | 98 ++++++++++++++++++++++++++++++ docs/navigation.js | 5 ++ pkg/gofr/datasource/README.md | 3 +- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 docs/datasources/couchbase/page.md diff --git a/docs/datasources/couchbase/page.md b/docs/datasources/couchbase/page.md new file mode 100644 index 000000000..fde192f14 --- /dev/null +++ b/docs/datasources/couchbase/page.md @@ -0,0 +1,98 @@ +## Couchbase + +GoFr provides first-class support for Couchbase, one of the leading NoSQL databases in the industry. This integration allows developers to seamlessly connect their applications with Couchbase and leverage its powerful features for data management. + +The Couchbase interface in GoFr is designed to be intuitive and easy to use, abstracting away the complexities of the underlying driver. This allows developers to focus on their application logic without worrying about the boilerplate code for database interactions. + +```go +type Couchbase interface { + Get(ctx context.Context, key string, result any) error + + Insert(ctx context.Context, key string, document, result any) error + + Upsert(ctx context.Context, key string, document any, result any) error + + Remove(ctx context.Context, key string) error + + Query(ctx context.Context, statement string, params map[string]any, result any) error + + AnalyticsQuery(ctx context.Context, statement string, params map[string]any, result any) error +} +``` + +To begin using Couchbase in your GoFr application, you need to import the Couchbase datasource package: + +```shell +go get gofr.dev/pkg/gofr/datasource/couchbase@latest +``` + +### Example + +Here is an example of how to use the Couchbase datasource in a GoFr application: + +```go +package main + +import ( + "context" + "fmt" + "log" + + "gofr.dev/pkg/gofr" + "gofr.dev/pkg/gofr/datasource/couchbase" +) + +type User struct { + ID string `json:"id"` + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + // Create a new GoFr application + a := gofr.New() + + // Add the Couchbase datasource to the application + a.AddCouchbase(couchbase.New(&couchbase.Config{ + Host: "localhost", + User: "Administrator", + Password: "password", + Bucket: "test-bucket", + })) + + // Add the routes + a.GET("/users/{id}", getUser) + a.POST("/users", createUser) + + // Run the application + a.Run() +} + +func getUser(c *gofr.Context) (any, error) { + // Get the user ID from the URL path + id := c.PathParam("id") + + // Get the user from Couchbase + var user User + if err := c.Couchbase.Get(c, id, &user); err != nil { + return nil, err + } + + return user, nil +} + +func createUser(c *gofr.Context) (any, error) { + // Get the user from the request body + var user User + if err := c.Bind(&user); err != nil { + return nil, err + } + + // Insert the user into Couchbase + if err := c.Couchbase.Insert(c, user.ID, user, nil); err != nil { + return nil, err + } + + return "user created successfully", nil +} +``` diff --git a/docs/navigation.js b/docs/navigation.js index 150f3f1ad..42f57306e 100644 --- a/docs/navigation.js +++ b/docs/navigation.js @@ -177,6 +177,11 @@ export const navigation = [ href: "/docs/datasources/cockroachdb", desc: "Learn how to connect to and interact with CockroachDB in GoFr." }, + { + title: "Couchbase", + href: "/docs/datasources/couchbase", + desc: "Learn how to connect to and interact with couchbase database in GoFr." + }, { title: "DGraph", href: "/docs/datasources/dgraph", diff --git a/pkg/gofr/datasource/README.md b/pkg/gofr/datasource/README.md index 336a90ade..e32873d2f 100644 --- a/pkg/gofr/datasource/README.md +++ b/pkg/gofr/datasource/README.md @@ -89,4 +89,5 @@ Therefore, GoFr utilizes a pluggable approach for new datasources by separating | ArangoDB | ✅ | ✅ | ✅ | ✅ | ✅ | | NATS-KV | ✅ | ✅ | ✅ | ✅ | ✅ | | ScyllaDB | ✅ | ✅ | ✅ | ✅ | | -| Elasticsearch | ✅ | ✅ | ✅ | ✅ | ✅ | \ No newline at end of file +| Elasticsearch | ✅ | ✅ | ✅ | ✅ | ✅ | +| Couchbase | ✅ | ✅ | ✅ | ✅ | ✅ | From fadb676e7b6616ac195359ce255c1ae00aba1500 Mon Sep 17 00:00:00 2001 From: olxandr Date: Thu, 31 Jul 2025 22:39:24 +0300 Subject: [PATCH 12/22] register healthcheck method --- pkg/gofr/container/health.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/gofr/container/health.go b/pkg/gofr/container/health.go index bfbf58a57..9d81502e3 100644 --- a/pkg/gofr/container/health.go +++ b/pkg/gofr/container/health.go @@ -67,6 +67,7 @@ func checkExternalDBHealth(ctx context.Context, c *Container, healthMap map[stri "dgraph": c.DGraph, "opentsdb": c.OpenTSDB, "elasticsearch": c.Elasticsearch, + "couchbase": c.Couchbase, } for name, service := range services { From 116ed9c66a18cf697cc8ccc79d90c1ddb5cccb9f Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 11:03:19 +0300 Subject: [PATCH 13/22] restore go.sum to its original state --- go.sum | 78 ++++++++++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/go.sum b/go.sum index c1b28a88b..758bebc87 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,16 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= -cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4= -cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA= +cloud.google.com/go/auth v0.16.3 h1:kabzoQ9/bobUmnseYnBO6qQG7q4a/CffFRlJSxv2wCc= +cloud.google.com/go/auth v0.16.3/go.mod h1:NucRGjaXfzP1ltpcQ7On/VTZ0H4kWB5Jy+Y9Dnm76fA= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= -cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE= -cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w= +cloud.google.com/go/kms v1.22.0 h1:dBRIj7+GDeeEvatJeTB19oYZNV0aj6wEqSIT/7gLqtk= +cloud.google.com/go/kms v1.22.0/go.mod h1:U7mf8Sva5jpOb4bxYZdtw/9zsbIjrklYwPcvMk34AL8= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/pubsub v1.49.0 h1:5054IkbslnrMCgA2MAEPcsN3Ky+AyMpEZcii/DoySPo= @@ -38,16 +38,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/couchbase/gocb/v2 v2.10.0 h1:NNxZ4okToU1Ylqp6F8tE41CEJQPhb2WjufryAkeubOk= -github.com/couchbase/gocb/v2 v2.10.0/go.mod h1:OSbMfQkP7ltbKiDZhsT2mGDhkQNmvGXxptKcxAUJQ2Y= -github.com/couchbase/gocbcore/v10 v10.7.0 h1:lAEi0PNeEGKOu8pWrPUdtLOT2oGr1J/UTdGHVPC3r/0= -github.com/couchbase/gocbcore/v10 v10.7.0/go.mod h1:Q8JWVenMCEOuRgrDQKApHbzzPif38HzefGgRVe9apAI= -github.com/couchbase/gocbcoreps v0.1.3 h1:fILaKGCjxFIeCgAUG8FGmRDSpdrRggohOMKEgO9CUpg= -github.com/couchbase/gocbcoreps v0.1.3/go.mod h1:hBFpDNPnRno6HH5cRXExhqXYRmTsFJlFHQx7vztcXPk= -github.com/couchbase/goprotostellar v1.0.2 h1:yoPbAL9sCtcyZ5e/DcU5PRMOEFaJrF9awXYu3VPfGls= -github.com/couchbase/goprotostellar v1.0.2/go.mod h1:5/yqVnZlW2/NSbAWu1hPJCFBEwjxgpe0PFFOlRixnp4= -github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28 h1:lhGOw8rNG6RAadmmaJAF3PJ7MNt7rFuWG7BHCYMgnGE= -github.com/couchbaselabs/gocbconnstr/v2 v2.0.0-20240607131231-fb385523de28/go.mod h1:o7T431UOfFVHDNvMBUmUxpHnhivwv7BziUao/nMl81E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -81,8 +71,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= +github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= @@ -100,8 +90,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -119,8 +107,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= -github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -259,21 +247,17 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -298,8 +282,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= @@ -363,28 +347,28 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE= -google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= +google.golang.org/api v0.243.0 h1:sw+ESIJ4BVnlJcWu9S+p2Z6Qq1PjG77T8IJ1xtp4jZQ= +google.golang.org/api v0.243.0/go.mod h1:GE4QtYfaybx1KmeHMdBnNnyLzBZCVihGBXAmJu/uUr8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= -google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= -google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -412,16 +396,18 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= -modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= +modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= -modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA= -modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= +modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM= +modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.65.10 h1:ZwEk8+jhW7qBjHIT+wd0d9VjitRyQef9BnzlzGwMODc= -modernc.org/libc v1.65.10/go.mod h1:StFvYpx7i/mXtBAfVOjaU0PWZOvIRoZSgXhrwXzr8Po= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= +modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= @@ -430,8 +416,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI= -modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE= +modernc.org/sqlite v1.38.1 h1:jNnIjleVta+DKSAr3TnkKK87EEhjPhBLzi6hvIX9Bas= +modernc.org/sqlite v1.38.1/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 5e3659ea9e7c903b6980417ec41513b6a18a4131 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 16:25:42 +0300 Subject: [PATCH 14/22] adapt logger interface according to main Logger --- pkg/gofr/datasource/couchbase/logger.go | 3 ++- pkg/gofr/datasource/couchbase/mock_logger.go | 28 +++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/logger.go b/pkg/gofr/datasource/couchbase/logger.go index 535625cd8..1f336fae6 100644 --- a/pkg/gofr/datasource/couchbase/logger.go +++ b/pkg/gofr/datasource/couchbase/logger.go @@ -11,8 +11,9 @@ import ( type Logger interface { Debug(args ...any) Debugf(pattern string, args ...any) + Log(args ...any) Logf(pattern string, args ...any) - Error(pattern string) + Error(args ...any) Errorf(pattern string, args ...any) } diff --git a/pkg/gofr/datasource/couchbase/mock_logger.go b/pkg/gofr/datasource/couchbase/mock_logger.go index f69afa424..1c627d148 100644 --- a/pkg/gofr/datasource/couchbase/mock_logger.go +++ b/pkg/gofr/datasource/couchbase/mock_logger.go @@ -73,15 +73,19 @@ func (mr *MockLoggerMockRecorder) Debugf(pattern any, args ...any) *gomock.Call } // Error mocks base method. -func (m *MockLogger) Error(pattern string) { +func (m *MockLogger) Error(args ...any) { m.ctrl.T.Helper() - m.ctrl.Call(m, "Error", pattern) + varargs := []any{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Error", varargs...) } // Error indicates an expected call of Error. -func (mr *MockLoggerMockRecorder) Error(pattern any) *gomock.Call { +func (mr *MockLoggerMockRecorder) Error(args ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), pattern) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), args...) } // Errorf mocks base method. @@ -101,6 +105,22 @@ func (mr *MockLoggerMockRecorder) Errorf(pattern any, args ...any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) } +// Log mocks base method. +func (m *MockLogger) Log(args ...any) { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Log", varargs...) +} + +// Log indicates an expected call of Log. +func (mr *MockLoggerMockRecorder) Log(args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Log", reflect.TypeOf((*MockLogger)(nil).Log), args...) +} + // Logf mocks base method. func (m *MockLogger) Logf(pattern string, args ...any) { m.ctrl.T.Helper() From 37db2a085ab01bb4c96627632177aff812b33491 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 16:27:45 +0300 Subject: [PATCH 15/22] do nothing if MutationResult is nil --- pkg/gofr/datasource/couchbase/couchbase.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index 388cab0af..8ed8d8923 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -252,6 +252,7 @@ func (c *Collection) mutationOperation(ctx context.Context, opName, key string, *r = *mr case **gocb.MutationResult: *r = mr + case nil: default: return errWrongResultType } From fe72bec97855ccf2e8ea844b2655ccedff05b935 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 16:44:43 +0300 Subject: [PATCH 16/22] more detailed info in docs --- docs/datasources/couchbase/page.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/datasources/couchbase/page.md b/docs/datasources/couchbase/page.md index fde192f14..28fcee17b 100644 --- a/docs/datasources/couchbase/page.md +++ b/docs/datasources/couchbase/page.md @@ -1,8 +1,7 @@ ## Couchbase -GoFr provides first-class support for Couchbase, one of the leading NoSQL databases in the industry. This integration allows developers to seamlessly connect their applications with Couchbase and leverage its powerful features for data management. - -The Couchbase interface in GoFr is designed to be intuitive and easy to use, abstracting away the complexities of the underlying driver. This allows developers to focus on their application logic without worrying about the boilerplate code for database interactions. +GoFr supports injecting `Couchbase` that implements the following interface. Any driver that implements the interface can be +added using the `app.AddCouchbase()` method, and users can use Couchbase across the application with `gofr.Context`. ```go type Couchbase interface { @@ -20,6 +19,8 @@ type Couchbase interface { } ``` +Users can easily inject a driver that supports this interface, providing usability without compromising the extensibility to use multiple databases.
+Don't forget to serup the Couchbase cluster in Couchbase Web Concole first. [Follow for more details](https://docs.couchbase.com/server/current/install/getting-started-docker.html#section_jvt_zvj_42b).
To begin using Couchbase in your GoFr application, you need to import the Couchbase datasource package: ```shell @@ -63,6 +64,7 @@ func main() { // Add the routes a.GET("/users/{id}", getUser) a.POST("/users", createUser) + a.DELETE("/users/{id}", deleteUser) // Run the application a.Run() @@ -95,4 +97,16 @@ func createUser(c *gofr.Context) (any, error) { return "user created successfully", nil } + +func deleteUser(c *gofr.Context) (any, error) { + // Get the user ID from the URL path + id := c.PathParam("id") + + // Remove the user from Couchbase + if err := c.Couchbase.Remove(c, id); err != nil { + return nil, err + } + + return "user deleted successfully", nil +} ``` From eae1ac97d0b85908322ed55419d0c5bc536fc903 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 16:44:52 +0300 Subject: [PATCH 17/22] add docker container setup --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc123d109..3aa66b224 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,6 +96,7 @@ docker run -d --name arangodb \ -e ARANGO_ROOT_PASSWORD=rootpassword \ --pull always \ arangodb:latest +docker run -d --name db -p 8091-8096:8091-8096 -p 11210-11211:11210-11211 couchbase ``` > [!NOTE] From cd985d1fe706175208865a814d406b855ff0e2e1 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 18:34:50 +0300 Subject: [PATCH 18/22] tracing refactor --- pkg/gofr/datasource/couchbase/couchbase.go | 132 ++++++++++++++------- 1 file changed, 88 insertions(+), 44 deletions(-) diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index 8ed8d8923..ff3e80e3a 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -5,10 +5,12 @@ import ( "encoding/json" "errors" "fmt" + "strings" "time" "github.com/couchbase/gocb/v2" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) @@ -89,7 +91,7 @@ func (c *Client) UseTracer(tracer any) { } } -func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method string, span trace.Span) { +func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method string) { duration := time.Since(startTime).Microseconds() ql.Duration = duration @@ -98,11 +100,6 @@ func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method st c.metrics.RecordHistogram(context.Background(), "app_couchbase_stats", float64(duration), "hostname", c.config.Host, "bucket", c.config.Bucket, "type", method) - - if span != nil { - defer span.End() - span.SetAttributes(attribute.Int64(fmt.Sprintf("couchbase.%v.duration", method), duration)) - } } func (c *Client) Connect() { @@ -239,10 +236,15 @@ func (c *Collection) mutationOperation(ctx context.Context, opName, key string, } tracerCtx, span := c.client.addTrace(ctx, opName, key) - - defer c.client.sendOperationStats(&QueryLog{Query: opName, Key: key, Parameters: document}, time.Now(), opName, span) + startTime := time.Now() mr, err := op(tracerCtx) + + // Finish span with error status + c.client.finishSpan(span, err) + + defer c.client.sendOperationStats(&QueryLog{Query: opName, Key: key, Parameters: document}, startTime, opName) + if err != nil { return fmt.Errorf("failed to %s document with key %s: %w", opName, key, err) } @@ -274,42 +276,20 @@ func (c *Collection) Insert(ctx context.Context, key string, document, result an }) } -// Get performs a get operation on the collection. -func (c *Collection) Get(ctx context.Context, key string, result any) error { - if c.collection == nil { - return errBucketNotInitialized - } - - tracerCtx, span := c.client.addTrace(ctx, "Get", key) - - res, err := c.collection.Get(key, &gocb.GetOptions{Context: tracerCtx}) - - defer c.client.sendOperationStats(&QueryLog{Query: "Get", Key: key}, time.Now(), "Get", span) - - if err != nil { - c.client.logger.Errorf("failed to get document with key %s: %w", key, err) - - return fmt.Errorf("failed to get document with key %s: %w", key, err) - } - - if err = res.Content(result); err != nil { - return fmt.Errorf("failed to unmarshal document content for key %s: %w", key, err) - } - - return nil -} - -// Remove performs a remove operation on the collection. func (c *Collection) Remove(ctx context.Context, key string) error { if c.collection == nil { return errBucketNotInitialized } tracerCtx, span := c.client.addTrace(ctx, "Remove", key) + startTime := time.Now() _, err := c.collection.Remove(key, &gocb.RemoveOptions{Context: tracerCtx}) - defer c.client.sendOperationStats(&QueryLog{Query: "Remove", Key: key}, time.Now(), "Remove", span) + // Finish span with error status + c.client.finishSpan(span, err) + + defer c.client.sendOperationStats(&QueryLog{Query: "Remove", Key: key}, startTime, "Remove") if err != nil { return fmt.Errorf("failed to remove document with key %s: %w", key, err) @@ -333,13 +313,22 @@ func (c *Client) executeTracedQuery( tracerCtx, span := c.addTrace(ctx, operation, statement) + // Add query parameters as span attributes if they exist + if len(params) > 0 && c.tracer != nil { + // Only add a count of parameters to avoid sensitive data leakage + span.SetAttributes(attribute.Int("db.couchbase.parameter_count", len(params))) + } + startTime := time.Now() err := executeQuery(func() (resultProvider, error) { return queryFn(tracerCtx) }, queryType, result) - defer c.sendOperationStats(&QueryLog{Query: operation, Statement: statement, Parameters: params}, startTime, operation, span) + // Finish span with error status + c.finishSpan(span, err) + + defer c.sendOperationStats(&QueryLog{Query: operation, Statement: statement, Parameters: params}, startTime, operation) if err != nil { c.logger.Errorf("%s query failed: %v", queryType, err) @@ -422,6 +411,7 @@ func (c *Client) RunTransaction(ctx context.Context, logic func(any) error) (any } _, span := c.addTrace(ctx, "RunTransaction", "transaction") + defer span.End() startTime := time.Now() @@ -434,7 +424,7 @@ func (c *Client) RunTransaction(ctx context.Context, logic func(any) error) (any // The context is passed down to operations within the transaction lambda. result, err := c.cluster.Transactions().Run(wrappedLogic, nil) - defer c.sendOperationStats(&QueryLog{Query: "RunTransaction"}, startTime, "RunTransaction", span) + defer c.sendOperationStats(&QueryLog{Query: "RunTransaction"}, startTime, "RunTransaction") if err != nil { c.logger.Errorf("Transaction failed: %v", err) @@ -473,15 +463,69 @@ func (c *Client) Close(opts any) error { } func (c *Client) addTrace(ctx context.Context, method, statement string) (context.Context, trace.Span) { - if c.tracer != nil { - contextWithTrace, span := c.tracer.Start(ctx, fmt.Sprintf("couchbase-%v", method)) + if c.tracer == nil { + // Return a no-op span when tracer is not available + return ctx, trace.SpanFromContext(ctx) + } + + // Set the span attributes following OpenTelemetry semantic conventions + attrs := []attribute.KeyValue{ + attribute.String("db.system", "couchbase"), + attribute.String("db.operation", method), + attribute.String("db.name", c.config.Bucket), + attribute.String("server.address", c.config.Host), + } + + // Add statement/key information based on the operation + if statement != "" { + if method == "Get" || method == "Insert" || method == "Upsert" || method == "Remove" { + attrs = append(attrs, attribute.String("db.couchbase.document_key", statement)) + } else { + attrs = append(attrs, attribute.String("db.statement", statement)) + } + } + + // Create a new span with proper naming + spanName := fmt.Sprintf("couchbase.%s", strings.ToLower(method)) + ctx, span := c.tracer.Start(ctx, spanName, trace.WithAttributes(attrs...)) + + return ctx, span +} + +func (c *Client) finishSpan(span trace.Span, err error) { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } else { + span.SetStatus(codes.Ok, "") + } + span.End() +} + +// Updated Get method with proper tracing +func (c *Collection) Get(ctx context.Context, key string, result any) error { + if c.collection == nil { + return errBucketNotInitialized + } + + tracerCtx, span := c.client.addTrace(ctx, "Get", key) + startTime := time.Now() + + res, err := c.collection.Get(key, &gocb.GetOptions{Context: tracerCtx}) - span.SetAttributes( - attribute.String("couchbase.statement", statement), - ) + // Finish span with error status + c.client.finishSpan(span, err) - return contextWithTrace, span + defer c.client.sendOperationStats(&QueryLog{Query: "Get", Key: key}, startTime, "Get") + + if err != nil { + c.client.logger.Errorf("failed to get document with key %s: %v", key, err) + return fmt.Errorf("failed to get document with key %s: %w", key, err) + } + + if err = res.Content(result); err != nil { + return fmt.Errorf("failed to unmarshal document content for key %s: %w", key, err) } - return ctx, nil + return nil } From 7f3f5748b2353b68766d5e1b1cbfe0563ac05d48 Mon Sep 17 00:00:00 2001 From: olxandr Date: Fri, 1 Aug 2025 18:41:02 +0300 Subject: [PATCH 19/22] linter fixes --- docs/datasources/couchbase/page.md | 2 +- pkg/gofr/datasource/couchbase/couchbase.go | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/datasources/couchbase/page.md b/docs/datasources/couchbase/page.md index 28fcee17b..e6f957fc6 100644 --- a/docs/datasources/couchbase/page.md +++ b/docs/datasources/couchbase/page.md @@ -20,7 +20,7 @@ type Couchbase interface { ``` Users can easily inject a driver that supports this interface, providing usability without compromising the extensibility to use multiple databases.
-Don't forget to serup the Couchbase cluster in Couchbase Web Concole first. [Follow for more details](https://docs.couchbase.com/server/current/install/getting-started-docker.html#section_jvt_zvj_42b).
+Don't forget to serup the Couchbase cluster in Couchbase Web Console first. [Follow for more details](https://docs.couchbase.com/server/current/install/getting-started-docker.html#section_jvt_zvj_42b).
To begin using Couchbase in your GoFr application, you need to import the Couchbase datasource package: ```shell diff --git a/pkg/gofr/datasource/couchbase/couchbase.go b/pkg/gofr/datasource/couchbase/couchbase.go index ff3e80e3a..15dbd2a29 100644 --- a/pkg/gofr/datasource/couchbase/couchbase.go +++ b/pkg/gofr/datasource/couchbase/couchbase.go @@ -91,6 +91,7 @@ func (c *Client) UseTracer(tracer any) { } } +// sendOperationStats sends statistics about a Couchbase operation. func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method string) { duration := time.Since(startTime).Microseconds() @@ -102,6 +103,7 @@ func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method st "bucket", c.config.Bucket, "type", method) } +// Connect establishes a connection to the Couchbase cluster. func (c *Client) Connect() { uri, err := c.generateCouchbaseURI() if err != nil { @@ -134,6 +136,7 @@ func (c *Client) Connect() { c.logger.Logf("connected to Couchbase at %v to bucket %v", c.config.Host, c.config.Bucket) } +// generateCouchbaseURI generates the Couchbase connection URI. func (c *Client) generateCouchbaseURI() (string, error) { if c.config.URI != "" { return c.config.URI, nil @@ -146,6 +149,7 @@ func (c *Client) generateCouchbaseURI() (string, error) { return fmt.Sprintf("couchbase://%s", c.config.Host), nil } +// establishConnection establishes a connection to the Couchbase cluster. func (c *Client) establishConnection(uri string) error { cluster, err := gocb.Connect(uri, gocb.ClusterOptions{ Authenticator: gocb.PasswordAuthenticator{ @@ -162,16 +166,19 @@ func (c *Client) establishConnection(uri string) error { return nil } +// waitForClusterReady waits for the Couchbase cluster to be ready. func (c *Client) waitForClusterReady() error { timeout := c.getTimeout() return c.cluster.WaitUntilReady(timeout, nil) } +// waitForBucketReady waits for the Couchbase bucket to be ready. func (c *Client) waitForBucketReady() error { timeout := c.getTimeout() return c.bucket.WaitUntilReady(timeout, nil) } +// getTimeout returns the connection timeout. func (c *Client) getTimeout() time.Duration { if c.config.ConnectionTimeout == 0 { return defaultTimeout @@ -180,7 +187,7 @@ func (c *Client) getTimeout() time.Duration { return c.config.ConnectionTimeout } -// HealthCheck performs a health check on the Couchbase cluster. +// HealthCheck performs a health check on the Couchbase connection. func (c *Client) HealthCheck(_ context.Context) (any, error) { h := Health{ Details: make(map[string]any), @@ -228,6 +235,7 @@ func (c *Client) scope(name string) *Scope { } } +// mutationOperation performs a mutation operation on the collection. func (c *Collection) mutationOperation(ctx context.Context, opName, key string, document, result any, op func(tracerCtx context.Context) (*gocb.MutationResult, error), ) error { @@ -276,6 +284,7 @@ func (c *Collection) Insert(ctx context.Context, key string, document, result an }) } +// Remove removes a document from the collection. func (c *Collection) Remove(ctx context.Context, key string) error { if c.collection == nil { return errBucketNotInitialized @@ -298,6 +307,7 @@ func (c *Collection) Remove(ctx context.Context, key string) error { return nil } +// executeTracedQuery executes a traced query. func (c *Client) executeTracedQuery( ctx context.Context, statement string, @@ -365,6 +375,7 @@ func (c *Client) AnalyticsQuery(ctx context.Context, statement string, params ma return c.executeTracedQuery(ctx, statement, params, result, "AnalyticsQuery", "Analytics", queryFn) } +// executeQuery executes a query and processes the results. func executeQuery(queryFn func() (resultProvider, error), queryType string, result any) error { rows, err := queryFn() if err != nil { @@ -462,6 +473,7 @@ func (c *Client) Close(opts any) error { return nil } +// addTrace adds a trace to the context. func (c *Client) addTrace(ctx context.Context, method, statement string) (context.Context, trace.Span) { if c.tracer == nil { // Return a no-op span when tracer is not available @@ -492,17 +504,19 @@ func (c *Client) addTrace(ctx context.Context, method, statement string) (contex return ctx, span } -func (c *Client) finishSpan(span trace.Span, err error) { +// finishSpan finishes a trace span. +func (*Client) finishSpan(span trace.Span, err error) { if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) } else { span.SetStatus(codes.Ok, "") } + span.End() } -// Updated Get method with proper tracing +// Get performs a get operation on the collection. func (c *Collection) Get(ctx context.Context, key string, result any) error { if c.collection == nil { return errBucketNotInitialized From d78dcf21c9e85f688b934fe50564a2ab4fc9455c Mon Sep 17 00:00:00 2001 From: olxandr Date: Wed, 6 Aug 2025 16:24:57 +0300 Subject: [PATCH 20/22] NewMockContainer() refactor (funlen statements limit exceeded) --- pkg/gofr/container/mock_container.go | 122 ++++++++++++--------------- 1 file changed, 53 insertions(+), 69 deletions(-) diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go index 278b7e83a..959445187 100644 --- a/pkg/gofr/container/mock_container.go +++ b/pkg/gofr/container/mock_container.go @@ -34,6 +34,34 @@ type Mocks struct { ScyllaDB *MockScyllaDB } +func newMocks(t *testing.T, ctrl *gomock.Controller) (*Mocks, *sqlMockDB) { + t.Helper() + mockDB, sqlMock, _ := sql.NewSQLMocks(t) + expectation := expectedQuery{} + sqlMockWrapper := &mockSQL{sqlMock, &expectation} + sqlDB := &sqlMockDB{mockDB, &expectation, logging.NewLogger(logging.DEBUG)} + sqlDB.finish(t) + + return &Mocks{ + Redis: NewMockRedis(ctrl), + SQL: sqlMockWrapper, + Clickhouse: NewMockClickhouse(ctrl), + Cassandra: NewMockCassandraWithContext(ctrl), + Mongo: NewMockMongo(ctrl), + KVStore: NewMockKVStore(ctrl), + DGraph: NewMockDgraph(ctrl), + ArangoDB: NewMockArangoDBProvider(ctrl), + OpenTSDB: NewMockOpenTSDB(ctrl), + SurrealDB: NewMockSurrealDB(ctrl), + Elasticsearch: NewMockElasticsearch(ctrl), + PubSub: NewMockPubSubProvider(ctrl), + Couchbase: NewMockCouchbase(ctrl), + File: file.NewMockFileSystemProvider(ctrl), + Metrics: NewMockMetrics(ctrl), + ScyllaDB: NewMockScyllaDB(ctrl), + }, sqlDB +} + type options func(c *Container, ctrl *gomock.Controller) any func WithMockHTTPService(httpServiceNames ...string) options { //nolint:revive // WithMockHTTPService returns an @@ -56,101 +84,57 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { ctrl := gomock.NewController(t) - mockDB, sqlMock, _ := sql.NewSQLMocks(t) - // initialization of expectations - expectation := expectedQuery{} - - sqlMockWrapper := &mockSQL{sqlMock, &expectation} - - sqlDB := &sqlMockDB{mockDB, &expectation, logging.NewLogger(logging.DEBUG)} - sqlDB.finish(t) - + mocks, sqlDB := newMocks(t, ctrl) container.SQL = sqlDB - redisMock := NewMockRedis(ctrl) - container.Redis = redisMock + container.Redis = mocks.Redis + mocks.Redis.EXPECT().Close().AnyTimes() + + container.Cassandra = mocks.Cassandra - cassandraMock := NewMockCassandraWithContext(ctrl) - container.Cassandra = cassandraMock + container.Clickhouse = mocks.Clickhouse - clickhouseMock := NewMockClickhouse(ctrl) - container.Clickhouse = clickhouseMock + container.Mongo = mocks.Mongo - mongoMock := NewMockMongo(ctrl) - container.Mongo = mongoMock + container.KVStore = mocks.KVStore - kvStoreMock := NewMockKVStore(ctrl) - container.KVStore = kvStoreMock + container.File = mocks.File - fileStoreMock := file.NewMockFileSystemProvider(ctrl) - container.File = fileStoreMock + container.DGraph = mocks.DGraph - dgraphMock := NewMockDgraph(ctrl) - container.DGraph = dgraphMock + container.OpenTSDB = mocks.OpenTSDB - opentsdbMock := NewMockOpenTSDB(ctrl) - container.OpenTSDB = opentsdbMock + container.ArangoDB = mocks.ArangoDB - arangoMock := NewMockArangoDBProvider(ctrl) - container.ArangoDB = arangoMock + container.SurrealDB = mocks.SurrealDB - surrealMock := NewMockSurrealDB(ctrl) - container.SurrealDB = surrealMock + container.Elasticsearch = mocks.Elasticsearch - elasticsearchMock := NewMockElasticsearch(ctrl) - container.Elasticsearch = elasticsearchMock + container.PubSub = mocks.PubSub - scyllaMock := NewMockScyllaDB(ctrl) - container.ScyllaDB = scyllaMock - pubsubMock := NewMockPubSubProvider(ctrl) - container.PubSub = pubsubMock + container.ScyllaDB = mocks.ScyllaDB - couchbaseMock := NewMockCouchbase(ctrl) - container.Couchbase = couchbaseMock + container.PubSub = mocks.PubSub - var httpMock *service.MockHTTP + container.Couchbase = mocks.Couchbase container.Services = make(map[string]service.HTTP) + container.metricsManager = mocks.Metrics + // TODO: Remove this expectation from mock container (previous generalization) to the actual tests where their expectations are being set. + mocks.Metrics.EXPECT().RecordHistogram(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + for _, option := range options { optionsAdded := option(container, ctrl) val, ok := optionsAdded.(*service.MockHTTP) if ok { - httpMock = val + mocks.HTTPService = val } } - redisMock.EXPECT().Close().AnyTimes() - - mockMetrics := NewMockMetrics(ctrl) - container.metricsManager = mockMetrics - - mocks := Mocks{ - Redis: redisMock, - SQL: sqlMockWrapper, - Clickhouse: clickhouseMock, - Cassandra: cassandraMock, - Mongo: mongoMock, - KVStore: kvStoreMock, - File: fileStoreMock, - HTTPService: httpMock, - DGraph: dgraphMock, - OpenTSDB: opentsdbMock, - ArangoDB: arangoMock, - SurrealDB: surrealMock, - Elasticsearch: elasticsearchMock, - PubSub: pubsubMock, - Couchbase: couchbaseMock, - Metrics: mockMetrics, - ScyllaDB: scyllaMock, - } - - // TODO: Remove this expectation from mock container (previous generalization) to the actual tests where their expectations are being set. - mockMetrics.EXPECT().RecordHistogram(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), - gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - - return container, &mocks + return container, mocks } type MockPubSub struct{} From afb14dccccdfd8594d1d5bd6b40bc1f9ac5d6a91 Mon Sep 17 00:00:00 2001 From: Umang01-hash Date: Tue, 12 Aug 2025 20:13:58 +0530 Subject: [PATCH 21/22] resolve merge conflicts --- go.work.sum | 75 ++++++++++++++++++++++++--- pkg/gofr/container/mock_container.go | 76 +++++++++++++++------------- 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/go.work.sum b/go.work.sum index ea8c1d9a2..b18de40ab 100644 --- a/go.work.sum +++ b/go.work.sum @@ -10,6 +10,8 @@ cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.20.0 h1:OunBvVCfvpWlt4dN7zg3FM6TDkzOePe1+foGJ9AXeeI= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -48,6 +50,8 @@ cloud.google.com/go/aiplatform v1.74.0 h1:rE2P5H7FOAFISAZilmdkapbk4CVgwfVs6FDWlh cloud.google.com/go/aiplatform v1.74.0/go.mod h1:hVEw30CetNut5FrblYd1AJUWRVSIjoyIvp0EVUh51HA= cloud.google.com/go/aiplatform v1.85.0 h1:80/GqdP8Tovaaw9Qr6fYZNDvwJeA9rLk8mYkqBJNIJQ= cloud.google.com/go/aiplatform v1.85.0/go.mod h1:S4DIKz3TFLSt7ooF2aCRdAqsUR4v/YDXUoHqn5P0EFc= +cloud.google.com/go/aiplatform v1.89.0 h1:niSJYc6ldWWVM9faXPo1Et1MVSQoLvVGriD7fwbJdtE= +cloud.google.com/go/aiplatform v1.89.0/go.mod h1:TzZtegPkinfXTtXVvZZpxx7noINFMVDrLkE7cEWhYEk= cloud.google.com/go/analytics v0.25.2/go.mod h1:th0DIunqrhI1ZWVlT3PH2Uw/9ANX8YHfFDEPqf/+7xM= cloud.google.com/go/analytics v0.25.3 h1:hX6JAsNbXd2uVjqjIuMcKpmhIybKrEunBiGxK4SwEFI= cloud.google.com/go/analytics v0.25.3/go.mod h1:pWoYg4yEr0iYg83LZRAicjDDdv54+Z//RyhzWwKbavI= @@ -55,6 +59,8 @@ cloud.google.com/go/analytics v0.26.0 h1:O2kWr2Sd4ep3I+YJ4aiY0G4+zWz6sp4eTce+JVn cloud.google.com/go/analytics v0.26.0/go.mod h1:KZWJfs8uX/+lTjdIjvT58SFa86V9KM6aPXwZKK6uNVI= cloud.google.com/go/analytics v0.28.0 h1:Bs17XtOjd+BhJtn+4QsCo8huMt7Zzziqn0umPz8ov2A= cloud.google.com/go/analytics v0.28.0/go.mod h1:hNT09bdzGB3HsL7DBhZkoPi4t5yzZPZROoFv+JzGR7I= +cloud.google.com/go/analytics v0.28.1 h1:W2ft49J/LeEj9A07Jsd5Q2kAzajK0j0IffOyyzbxw04= +cloud.google.com/go/analytics v0.28.1/go.mod h1:iPaIVr5iXPB3JzkKPW1JddswksACRFl3NSHgVHsuYC4= cloud.google.com/go/apigateway v1.7.2/go.mod h1:+weId+9aR9J6GRwDka7jIUSrKEX60XGcikX7dGU8O7M= cloud.google.com/go/apigateway v1.7.3 h1:Mn7cC5iWJz+cSMS/Hb+N2410CpZ6c8XpJKaexBl0Gxs= cloud.google.com/go/apigateway v1.7.3/go.mod h1:uK0iRHdl2rdTe79bHW/bTsKhhXPcFihjUdb7RzhTPf4= @@ -90,6 +96,8 @@ cloud.google.com/go/asset v1.20.4 h1:6oNgjcs5KCPGBD71G0IccK6TfeFsEtBTyQ3Q+Dn09bs cloud.google.com/go/asset v1.20.4/go.mod h1:DP09pZ+SoFWUZyPZx26xVroHk+6+9umnQv+01yfJxbM= cloud.google.com/go/asset v1.21.0 h1:AtsFIJU1gH3jXHf+2cyugTkpOPT8VYyjCK2yNmQltvg= cloud.google.com/go/asset v1.21.0/go.mod h1:0lMJ0STdyImZDSCB8B3i/+lzIquLBpJ9KZ4pyRvzccM= +cloud.google.com/go/asset v1.21.1 h1:i55wWC/EwVdHMyJgRfbLp/L6ez4nQuOpZwSxkuqN9ek= +cloud.google.com/go/asset v1.21.1/go.mod h1:7AzY1GCC+s1O73yzLM1IpHFLHz3ws2OigmCpOQHwebk= cloud.google.com/go/assuredworkloads v1.12.2/go.mod h1:/WeRr/q+6EQYgnoYrqCVgw7boMoDfjXZZev3iJxs2Iw= cloud.google.com/go/assuredworkloads v1.12.3 h1:RU1WhF1zMggdXAZ+ezYTn4Eh/FdiX7sz8lLXGERn4Po= cloud.google.com/go/assuredworkloads v1.12.3/go.mod h1:iGBkyMGdtlsxhCi4Ys5SeuvIrPTeI6HeuEJt7qJgJT8= @@ -138,6 +146,8 @@ cloud.google.com/go/bigquery v1.66.2 h1:EKOSqjtO7jPpJoEzDmRctGea3c2EOGoexy8VyY9d cloud.google.com/go/bigquery v1.66.2/go.mod h1:+Yd6dRyW8D/FYEjUGodIbu0QaoEmgav7Lwhotup6njo= cloud.google.com/go/bigquery v1.67.0 h1:GXleMyn/cu5+DPLy9Rz5f5IULWTLrepwbQnP/5qrVbY= cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFRBGo6EEKHug= +cloud.google.com/go/bigquery v1.69.0 h1:rZvHnjSUs5sHK3F9awiuFk2PeOaB8suqNuim21GbaTc= +cloud.google.com/go/bigquery v1.69.0/go.mod h1:TdGLquA3h/mGg+McX+GsqG9afAzTAcldMjqhdjHTLew= cloud.google.com/go/bigtable v1.33.0/go.mod h1:HtpnH4g25VT1pejHRtInlFPnN5sjTxbQlsYBjh9t5l0= cloud.google.com/go/bigtable v1.34.0 h1:eIgi3QLcN4aq8p6n9U/zPgmHeBP34sm9FiKq4ik/ZoY= cloud.google.com/go/bigtable v1.34.0/go.mod h1:p94uLf6cy6D73POkudMagaFF3x9c7ktZjRnOUVGjZAw= @@ -194,6 +204,8 @@ cloud.google.com/go/compute v1.34.0 h1:+k/kmViu4TEi97NGaxAATYtpYBviOWJySPZ+ekA95 cloud.google.com/go/compute v1.34.0/go.mod h1:zWZwtLwZQyonEvIQBuIa0WvraMYK69J5eDCOw9VZU4g= cloud.google.com/go/compute v1.37.0 h1:XxtZlXYkZXub3LNaLu90TTemcFqIU1yZ4E4q9VlR39A= cloud.google.com/go/compute v1.37.0/go.mod h1:AsK4VqrSyXBo4SMbRtfAO1VfaMjUEjEwv1UB/AwVp5Q= +cloud.google.com/go/compute v1.38.0 h1:MilCLYQW2m7Dku8hRIIKo4r0oKastlD74sSu16riYKs= +cloud.google.com/go/compute v1.38.0/go.mod h1:oAFNIuXOmXbK/ssXm3z4nZB8ckPdjltJ7xhHCdbWFZM= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= @@ -209,6 +221,8 @@ cloud.google.com/go/container v1.42.2 h1:8ncSEBjkng6ucCICauaUGzBomoM2VyYzleAum1O cloud.google.com/go/container v1.42.2/go.mod h1:y71YW7uR5Ck+9Vsbst0AF2F3UMgqmsN4SP8JR9xEsR8= cloud.google.com/go/container v1.42.4 h1:N8I+GiImhrSMUcKSOYTd8D6wBWyWSgPa4IJkSdlR2jk= cloud.google.com/go/container v1.42.4/go.mod h1:wf9lKc3ayWVbbV/IxKIDzT7E+1KQgzkzdxEJpj1pebE= +cloud.google.com/go/container v1.43.0 h1:A6J92FJPfxTvyX7MHF+w4t2W9WCqvHOi9UB5SAeSy3w= +cloud.google.com/go/container v1.43.0/go.mod h1:ETU9WZ1KM9ikEKLzrhRVao7KHtalDQu6aPqM34zDr/U= cloud.google.com/go/containeranalysis v0.13.2/go.mod h1:AiKvXJkc3HiqkHzVIt6s5M81wk+q7SNffc6ZlkTDgiE= cloud.google.com/go/containeranalysis v0.13.3 h1:1D8U75BeotZxrG4jR6NYBtOt+uAeBsWhpBZmSYLakQw= cloud.google.com/go/containeranalysis v0.13.3/go.mod h1:0SYnagA1Ivb7qPqKNYPkCtphhkJn3IzgaSp3mj+9XAY= @@ -226,11 +240,15 @@ cloud.google.com/go/dataflow v0.10.3 h1:+7IfIXzYWSybIIDGK9FN2uqBsP/5b/Y0pBYzNhcm cloud.google.com/go/dataflow v0.10.3/go.mod h1:5EuVGDh5Tg4mDePWXMMGAG6QYAQhLNyzxdNQ0A1FfW4= cloud.google.com/go/dataflow v0.10.6 h1:UKUD8I7So3H646JHZWcrYVgf2nuEB27l015zUErPnow= cloud.google.com/go/dataflow v0.10.6/go.mod h1:Vi0pTYCVGPnM2hWOQRyErovqTu2xt2sr8Rp4ECACwUI= +cloud.google.com/go/dataflow v0.11.0 h1:AdhB4cAkMOC9NtrHJxpKOVvO/VqBLaIyk0tEEhbGjYM= +cloud.google.com/go/dataflow v0.11.0/go.mod h1:gNHC9fUjlV9miu0hd4oQaXibIuVYTQvZhMdPievKsPk= cloud.google.com/go/dataform v0.10.2/go.mod h1:oZHwMBxG6jGZCVZqqMx+XWXK+dA/ooyYiyeRbUxI15M= cloud.google.com/go/dataform v0.10.3 h1:ZpGkZV8OyhUhvN/tfLffU2ki5ERTtqOunkIaiVAhmw0= cloud.google.com/go/dataform v0.10.3/go.mod h1:8SruzxHYCxtvG53gXqDZvZCx12BlsUchuV/JQFtyTCw= cloud.google.com/go/dataform v0.11.2 h1:poGCMWMvu/t2SooaWDHJAJiUyAtWYzKy+SGDNez2RI0= cloud.google.com/go/dataform v0.11.2/go.mod h1:IMmueJPEKpptT2ZLWlvIYjw6P/mYHHxA7/SUBiXqZUY= +cloud.google.com/go/dataform v0.12.0 h1:0eCPTPUC/RZ863aVfXTJLkg0tEpdpn62VD6ywSmmzxM= +cloud.google.com/go/dataform v0.12.0/go.mod h1:PuDIEY0lSVuPrZqcFji1fmr5RRvz3DGz4YP/cONc8g4= cloud.google.com/go/datafusion v1.8.2/go.mod h1:XernijudKtVG/VEvxtLv08COyVuiYPraSxm+8hd4zXA= cloud.google.com/go/datafusion v1.8.3 h1:FTMtsf2nfGGlDCuE84/RvVaCcTIYE7WQSB0noeO0cwI= cloud.google.com/go/datafusion v1.8.3/go.mod h1:hyglMzE57KRf0Rf/N2VRPcHCwKfZAAucx+LATY6Jc6Q= @@ -248,6 +266,8 @@ cloud.google.com/go/dataplex v1.22.0 h1:j4hD6opb+gq9CJNPFIlIggoW8Kjymg8Wmy2mdHmQ cloud.google.com/go/dataplex v1.22.0/go.mod h1:g166QMCGHvwc3qlTG4p34n+lHwu7JFfaNpMfI2uO7b8= cloud.google.com/go/dataplex v1.25.2 h1:jgfG6iqPVJxNPSpVCxH4diHMFb87wNd0F1kDgU3XJCk= cloud.google.com/go/dataplex v1.25.2/go.mod h1:AH2/a7eCYvFP58scJGR7YlSY9qEhM8jq5IeOA/32IZ0= +cloud.google.com/go/dataplex v1.25.3 h1:Xr0Toh6wyBlmL3H4EPu1YKwxUtkDSzzq+IP0iLc88kk= +cloud.google.com/go/dataplex v1.25.3/go.mod h1:wOJXnOg6bem0tyslu4hZBTncfqcPNDpYGKzed3+bd+E= cloud.google.com/go/dataproc/v2 v2.10.0/go.mod h1:HD16lk4rv2zHFhbm8gGOtrRaFohMDr9f0lAUMLmg1PM= cloud.google.com/go/dataproc/v2 v2.10.1 h1:2vOv471LrcSn91VNzijcH+OkDRLa3kdyymOfKqbwZ4c= cloud.google.com/go/dataproc/v2 v2.10.1/go.mod h1:fq+LSN/HYUaaV2EnUPFVPxfe1XpzGVqFnL0TTXs8juk= @@ -260,6 +280,8 @@ cloud.google.com/go/dataqna v0.9.3 h1:lGUj2FYs650EUPDMV6plWBAoh8qH9Bu1KCz1PUYF2V cloud.google.com/go/dataqna v0.9.3/go.mod h1:PiAfkXxa2LZYxMnOWVYWz3KgY7txdFg9HEMQPb4u1JA= cloud.google.com/go/dataqna v0.9.6 h1:ymqgCzymbsVgBvD4jhdt7HN9cVwg9x60jkozpp/omFQ= cloud.google.com/go/dataqna v0.9.6/go.mod h1:rjnNwjh8l3ZsvrANy6pWseBJL2/tJpCcBwJV8XCx4kU= +cloud.google.com/go/dataqna v0.9.7 h1:qTRAG/E3T63Xj1orefRlwupfwH9c9ERUAnWSRGp75so= +cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.20.0 h1:NNpXoyEqIJmZFc0ACcwBEaXnmscUpcG4NkKnbCePmiM= @@ -278,6 +300,8 @@ cloud.google.com/go/deploy v1.26.2 h1:1c2Cd3jdb0mrKHHfyzSQ5DRmxgYd07tIZZzuMNrwDx cloud.google.com/go/deploy v1.26.2/go.mod h1:XpS3sG/ivkXCfzbzJXY9DXTeCJ5r68gIyeOgVGxGNEs= cloud.google.com/go/deploy v1.27.1 h1:Rs8v4J68cZ45RfimX0wjraXaF4WZl1SIR+hkmGaK6Ag= cloud.google.com/go/deploy v1.27.1/go.mod h1:il2gxiMgV3AMlySoQYe54/xpgVDoEh185nj4XjJ+GRk= +cloud.google.com/go/deploy v1.27.2 h1:C0VqBhFyQFp6+xgPHZAD7LeRA4XGy5YLzGmPQ2NhlLk= +cloud.google.com/go/deploy v1.27.2/go.mod h1:4NHWE7ENry2A4O1i/4iAPfXHnJCZ01xckAKpZQwhg1M= cloud.google.com/go/dialogflow v1.60.0/go.mod h1:PjsrI+d2FI4BlGThxL0+Rua/g9vLI+2A1KL7s/Vo3pY= cloud.google.com/go/dialogflow v1.64.1 h1:6fU4IKLpvgpXqiUCE8gUp8eV5u629SCtiyXMudXtZSg= cloud.google.com/go/dialogflow v1.64.1/go.mod h1:jkv4vTiGhEUPBzmk1sJ+S1Duu2epCOBNHoWUImHkO5U= @@ -292,6 +316,8 @@ cloud.google.com/go/dlp v1.21.0 h1:9kz7+gaB/0gBZsDUnNT1asDihNZSrRFSeUTBcBdUAkk= cloud.google.com/go/dlp v1.21.0/go.mod h1:Y9HOVtPoArpL9sI1O33aN/vK9QRwDERU9PEJJfM8DvE= cloud.google.com/go/dlp v1.22.1 h1:aZvDXCSNmPjhawF/thQa/GNIoW16JGNlI5L5N/HNXGU= cloud.google.com/go/dlp v1.22.1/go.mod h1:Gc7tGo1UJJTBRt4OvNQhm8XEQ0i9VidAiGXBVtsftjM= +cloud.google.com/go/dlp v1.23.0 h1:3xWRKylXxhysaQaV+DLev1YcIywFUCc7yJEE6R7ZGDQ= +cloud.google.com/go/dlp v1.23.0/go.mod h1:vVT4RlyPMEMcVHexdPT6iMVac3seq3l6b8UPdYpgFrg= cloud.google.com/go/documentai v1.35.0/go.mod h1:ZotiWUlDE8qXSUqkJsGMQqVmfTMYATwJEYqbPXTR9kk= cloud.google.com/go/documentai v1.35.1 h1:52RfiUsoblXcE57CfKJGnITWLxRM30BcqNk/BKZl2LI= cloud.google.com/go/documentai v1.35.1/go.mod h1:WJjwUAQfwQPJORW8fjz7RODprMULDzEGLA2E6WxenFw= @@ -341,6 +367,8 @@ cloud.google.com/go/gkebackup v1.6.3 h1:djdExe/QgoKdp1gnIO1G5BoO1o/yGQOQJJEZ4QKT cloud.google.com/go/gkebackup v1.6.3/go.mod h1:JJzGsA8/suXpTDtqI7n9RZW97PXa2CIp+n8aRC/y57k= cloud.google.com/go/gkebackup v1.7.0 h1:9nDcyMJvTEmsWhJv+sIqMLRIJaEmpkpirxt+cOlaDjM= cloud.google.com/go/gkebackup v1.7.0/go.mod h1:oPHXUc6X6tg6Zf/7QmKOfXOFaVzBEgMWpLDb4LqngWA= +cloud.google.com/go/gkebackup v1.8.0 h1:eBqOt61yEChvj7I/GDPBbdCCRdUPudD1qrQYfYWV3Ok= +cloud.google.com/go/gkebackup v1.8.0/go.mod h1:FjsjNldDilC9MWKEHExnK3kKJyTDaSdO1vF0QeWSOPU= cloud.google.com/go/gkeconnect v0.12.0/go.mod h1:zn37LsFiNZxPN4iO7YbUk8l/E14pAJ7KxpoXoxt7Ly0= cloud.google.com/go/gkeconnect v0.12.1 h1:YVpR0vlHSP/wD74PXEbKua4Aamud+wiYm4TiewNjD3M= cloud.google.com/go/gkeconnect v0.12.1/go.mod h1:L1dhGY8LjINmWfR30vneozonQKRSIi5DWGIHjOqo58A= @@ -374,6 +402,8 @@ cloud.google.com/go/iap v1.10.3 h1:OWNYFHPyIBNHEAEFdVKOltYWe0g3izSrpFJW6Iidovk= cloud.google.com/go/iap v1.10.3/go.mod h1:xKgn7bocMuCFYhzRizRWP635E2LNPnIXT7DW0TlyPJ8= cloud.google.com/go/iap v1.11.1 h1:RWWGRaPe/icBqNLTk83hfLkBZOh5TPufUTyWDWRldFo= cloud.google.com/go/iap v1.11.1/go.mod h1:qFipMJ4nOIv4yDHZxn31PiS8QxJJH2FlxgH9aFauejw= +cloud.google.com/go/iap v1.11.2 h1:VIioCrYsyWiRGx7Y8RDNylpI6d4t1Qx5ZgSLUVmWWPo= +cloud.google.com/go/iap v1.11.2/go.mod h1:Bh99DMUpP5CitL9lK0BC8MYgjjYO4b3FbyhgW1VHJvg= cloud.google.com/go/ids v1.5.2/go.mod h1:P+ccDD96joXlomfonEdCnyrHvE68uLonc7sJBPVM5T0= cloud.google.com/go/ids v1.5.3 h1:wbFF7twu0XScFr+dtsVxTTttbFIRYt/SJjZiHFidtYE= cloud.google.com/go/ids v1.5.3/go.mod h1:a2MX8g18Eqs7yxD/pnEdid42SyBUm9LIzSWf8Jux9OY= @@ -411,6 +441,8 @@ cloud.google.com/go/maps v1.19.0 h1:deVm1ZFyCrUwxG11CdvtBz350VG5JUQ/LHTLnQrBgrM= cloud.google.com/go/maps v1.19.0/go.mod h1:goHUXrmzoZvQjUVd0KGhH8t3AYRm17P8b+fsyR1UAmQ= cloud.google.com/go/maps v1.20.4 h1:vShJlIzVc3MSUcvdH1j2plmDP/KyWc9e0Th73mY4Kt0= cloud.google.com/go/maps v1.20.4/go.mod h1:Act0Ws4HffrECH+pL8YYy1scdSLegov7+0c6gvKqRzI= +cloud.google.com/go/maps v1.21.0 h1:El61AfMxC1sU/RU8Wzs9dkZEgltyunKM86aKF9aDlaE= +cloud.google.com/go/maps v1.21.0/go.mod h1:cqzZ7+DWUKKbPTgqE+KuNQtiCRyg/o7WZF9zDQk+HQs= cloud.google.com/go/mediatranslation v0.9.2/go.mod h1:1xyRoDYN32THzy+QaU62vIMciX0CFexplju9t30XwUc= cloud.google.com/go/mediatranslation v0.9.3 h1:nRBjeaMLipw05Br+qDAlSCcCQAAlat4mvpafztbEVgc= cloud.google.com/go/mediatranslation v0.9.3/go.mod h1:KTrFV0dh7duYKDjmuzjM++2Wn6yw/I5sjZQVV5k3BAA= @@ -426,6 +458,8 @@ cloud.google.com/go/metastore v1.14.3 h1:jDqeCw6NGDRAPT9+2Y/EjnWAB0BfCcUfmPLOyhB cloud.google.com/go/metastore v1.14.3/go.mod h1:HlbGVOvg0ubBLVFRk3Otj3gtuzInuzO/TImOBwsKlG4= cloud.google.com/go/metastore v1.14.6 h1:X/eWwRv83ACfRPVrXlFM4DfJ7gwXRC1Tziv6w5MGxLU= cloud.google.com/go/metastore v1.14.6/go.mod h1:iDbuGwlDr552EkWA5E1Y/4hHme3cLv3ZxArKHXjS2OU= +cloud.google.com/go/metastore v1.14.7 h1:dLm59AHHZCorveCylj7c2iWhkQsmMIeWTsV+tG/BXtY= +cloud.google.com/go/metastore v1.14.7/go.mod h1:0dka99KQofeUgdfu+K/Jk1KeT9veWZlxuZdJpZPtuYU= cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= cloud.google.com/go/monitoring v1.22.1 h1:KQbnAC4IAH+5x3iWuPZT5iN9VXqKMzzOgqcYB6fqPDE= cloud.google.com/go/monitoring v1.22.1/go.mod h1:AuZZXAoN0WWWfsSvET1Cpc4/1D8LXq8KRDU87fMS6XY= @@ -479,6 +513,8 @@ cloud.google.com/go/osconfig v1.14.3 h1:cyf1PMK5c2/WOIr5r2lxjH/XBJMA9P4zC8Tm10i0 cloud.google.com/go/osconfig v1.14.3/go.mod h1:9D2MS1Etne18r/mAeW5jtto3toc9H1qu9wLNDG3NvQg= cloud.google.com/go/osconfig v1.14.5 h1:r3enRq2DarWyiE/BhHjZf1Yc/iC2YBsyvqqtEGD+upk= cloud.google.com/go/osconfig v1.14.5/go.mod h1:XH+NjBVat41I/+xgQzKOJEhuC4xI7lX2INE5SWnVr9U= +cloud.google.com/go/osconfig v1.14.6 h1:4uJrA1obzMBp1I+DF15y/MvsXKIODevuANpq3QhvX30= +cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= cloud.google.com/go/oslogin v1.14.2/go.mod h1:M7tAefCr6e9LFTrdWRQRrmMeKHbkvc4D9g6tHIjHySA= cloud.google.com/go/oslogin v1.14.3 h1:yomxnFPk+ye0zd0mJ15nn9fH4Ns7ex4xA3ll+u2q59A= cloud.google.com/go/oslogin v1.14.3/go.mod h1:fDEGODTG/W9ZGUTHTlMh8euXWC1fTcgjJ9Kcxxy14a8= @@ -545,6 +581,8 @@ cloud.google.com/go/retail v1.19.2 h1:PT6CUlazIFIOLLJnV+bPBtiSH8iusKZ+FZRzZYFt2v cloud.google.com/go/retail v1.19.2/go.mod h1:71tRFYAcR4MhrZ1YZzaJxr030LvaZiIcupH7bXfFBcY= cloud.google.com/go/retail v1.20.0 h1:SbvW4zrmY+2sN76xU9syXzOGC9496TZ6r3euIyCn7Nw= cloud.google.com/go/retail v1.20.0/go.mod h1:1CXWDZDJTOsK6lPjkv67gValP9+h1TMadTC9NpFFr9s= +cloud.google.com/go/retail v1.21.0 h1:8jgWgtAg1mk91WmaoWRTlL9CcvazPwqZ3YT9n6Gva9U= +cloud.google.com/go/retail v1.21.0/go.mod h1:LuG+QvBdLfKfO+7nnF3eA3l1j4TQw3Sg+UqlUorquRc= cloud.google.com/go/run v1.7.0/go.mod h1:IvJOg2TBb/5a0Qkc6crn5yTy5nkjcgSWQLhgO8QL8PQ= cloud.google.com/go/run v1.8.1 h1:aeVLygw0BGLH+Zbj8v3K3nEHvKlgoq+j8fcRJaYZtxY= cloud.google.com/go/run v1.8.1/go.mod h1:wR5IG8Nujk9pyyNai187K4p8jzSLeqCKCAFBrZ2Sd4c= @@ -552,6 +590,8 @@ cloud.google.com/go/run v1.9.0 h1:9WeTqeEcriXqRViXMNwczjFJjixOSBlSlk/fW3lfKPg= cloud.google.com/go/run v1.9.0/go.mod h1:Dh0+mizUbtBOpPEzeXMM22t8qYQpyWpfmUiWQ0+94DU= cloud.google.com/go/run v1.9.3 h1:BrB0Y/BlsyWKdHebDp3CpbV9knwcWqqQI4RWYElf1zQ= cloud.google.com/go/run v1.9.3/go.mod h1:Si9yDIkUGr5vsXE2QVSWFmAjJkv/O8s3tJ1eTxw3p1o= +cloud.google.com/go/run v1.10.0 h1:CDhz0PPzI/cVpmNFyHe3Yp21jNpiAqtkfRxuoLi+JU0= +cloud.google.com/go/run v1.10.0/go.mod h1:z7/ZidaHOCjdn5dV0eojRbD+p8RczMk3A7Qi2L+koHg= cloud.google.com/go/scheduler v1.11.2/go.mod h1:GZSv76T+KTssX2I9WukIYQuQRf7jk1WI+LOcIEHUUHk= cloud.google.com/go/scheduler v1.11.3 h1:p6+h8BoYJC+TvUijGBfORN6nuhOvJ3EwZ2H84CZ1ZEU= cloud.google.com/go/scheduler v1.11.3/go.mod h1:Io2+gcvUjLX1GdymwaSPJ6ZYxHN9/NNGL5kIV3Ax5+Q= @@ -594,6 +634,8 @@ cloud.google.com/go/spanner v1.76.1 h1:vYbVZuXfnFwvNcvH3lhI2PeUA+kHyqKmLC7mJWaC4 cloud.google.com/go/spanner v1.76.1/go.mod h1:YtwoE+zObKY7+ZeDCBtZ2ukM+1/iPaMfUM+KnTh/sx0= cloud.google.com/go/spanner v1.80.0 h1:4B2hoN1TF0qghiK7CYjYzjRt0/EEacIlS/UJl0k2hKA= cloud.google.com/go/spanner v1.80.0/go.mod h1:XQWUqx9r8Giw6gNh0Gu8xYfz7O+dAKouAkFCxG/mZC8= +cloud.google.com/go/spanner v1.82.0 h1:w9uO8RqEoBooBLX4nqV1RtgudyU2ZX780KTLRgeVg60= +cloud.google.com/go/spanner v1.82.0/go.mod h1:BzybQHFQ/NqGxvE/M+/iU29xgutJf7Q85/4U9RWMto0= cloud.google.com/go/speech v1.25.2/go.mod h1:KPFirZlLL8SqPaTtG6l+HHIFHPipjbemv4iFg7rTlYs= cloud.google.com/go/speech v1.26.0 h1:qvURtJs7BQzQhbxWxwai0pT79S8KLVKJ/4W8igVkt1Y= cloud.google.com/go/speech v1.26.0/go.mod h1:78bqDV2SgwFlP/M4n3i3PwLthFq6ta7qmyG6lUV7UCA= @@ -614,6 +656,8 @@ cloud.google.com/go/storagetransfer v1.12.1 h1:W3v9A7MGBN7H9sAFstyciwP/1XEQhUhZf cloud.google.com/go/storagetransfer v1.12.1/go.mod h1:hQqbfs8/LTmObJyCC0KrlBw8yBJ2bSFlaGila0qBMk4= cloud.google.com/go/storagetransfer v1.12.4 h1:2gFmZvD6G0qC57IIQ1Uga5TjvRwDyMW8lGLv9a8+tC4= cloud.google.com/go/storagetransfer v1.12.4/go.mod h1:p1xLKvpt78aQFRJ8lZGYArgFuL4wljFzitPZoYjl/8A= +cloud.google.com/go/storagetransfer v1.13.0 h1:uqKX3OgcYzR1W1YI943ZZ45id0RqA2eXXoCBSPstlbw= +cloud.google.com/go/storagetransfer v1.13.0/go.mod h1:+aov7guRxXBYgR3WCqedkyibbTICdQOiXOdpPcJCKl8= cloud.google.com/go/talent v1.7.2/go.mod h1:k1sqlDgS9gbc0gMTRuRQpX6C6VB7bGUxSPcoTRWJod8= cloud.google.com/go/talent v1.7.3 h1:mbN4dqACYBf8FIurOOTT4JXfFPkqWtOZccfMG9w03hY= cloud.google.com/go/talent v1.7.3/go.mod h1:6HhwxYxAtL6eKzcUMJ8reliQPUpay3/L6JZll4cS/vE= @@ -628,6 +672,8 @@ cloud.google.com/go/texttospeech v1.11.0 h1:YF/RdNb+jUEp22cIZCvqiFjfA5OxGE+Dxss3 cloud.google.com/go/texttospeech v1.11.0/go.mod h1:7M2ro3I2QfIEvArFk1TJ+pqXJqhszDtxUpnIv/150As= cloud.google.com/go/texttospeech v1.12.1 h1:IdYOIwagXmSjBuACNC86KTB3E/b7vgwyXzYzlLLxDhM= cloud.google.com/go/texttospeech v1.12.1/go.mod h1:f8vrD3OXAKTRr4eL0TPjZgYQhiN6ti/tKM3i1Uub5X0= +cloud.google.com/go/texttospeech v1.13.0 h1:oWWFQp0yFl4EJOr3opDkKH9304wUsZjgPjrTDS6S1a8= +cloud.google.com/go/texttospeech v1.13.0/go.mod h1:g/tW/m0VJnulGncDrAoad6WdELMTes8eb77Idz+4HCo= cloud.google.com/go/tpu v1.7.2/go.mod h1:0Y7dUo2LIbDUx0yQ/vnLC6e18FK6NrDfAhYS9wZ/2vs= cloud.google.com/go/tpu v1.7.3 h1:PszqG+pvC7u/cv51GWQIN9M++jciIBr5vVn6/MWzU8I= cloud.google.com/go/tpu v1.7.3/go.mod h1:jZJET6Hp4VKRFHf+ABHVXW4mq1az4ZYHDLBKb5mYAWE= @@ -651,6 +697,8 @@ cloud.google.com/go/video v1.23.3 h1:C2FH+6yr6LCZC4fP0gm9FwJB/SRh5Ul88O5Sc/bL83I cloud.google.com/go/video v1.23.3/go.mod h1:Kvh/BheubZxGZDXSb0iO6YX7ZNcaYHbLjnnaC8Qyy3g= cloud.google.com/go/video v1.23.5 h1:leLw8LyDCR6K7HZkbIie3d45t0Z75BdJVC3WYP+MWy0= cloud.google.com/go/video v1.23.5/go.mod h1:ZSpGFCpfTOTmb1IkmHNGC/9yI3TjIa/vkkOKBDo0Vpo= +cloud.google.com/go/video v1.24.0 h1:KTB2BEXjGm2K/JcKxQXEgx3nSoMTByepnPZa4kln064= +cloud.google.com/go/video v1.24.0/go.mod h1:h6Bw4yUbGNEa9dH4qMtUMnj6cEf+OyOv/f2tb70G6Fk= cloud.google.com/go/videointelligence v1.12.2/go.mod h1:8xKGlq0lNVyT8JgTkkCUCpyNJnYYEJVWGdqzv+UcwR8= cloud.google.com/go/videointelligence v1.12.3 h1:zNTOUQyatGQtnCJ2dR3faRtpWQOlC8wszJqwG5CtwVM= cloud.google.com/go/videointelligence v1.12.3/go.mod h1:dUA6V+NH7CVgX6TePq0IelVeBMGzvehxKPR4FGf1dtw= @@ -785,6 +833,8 @@ github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -819,6 +869,8 @@ github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswgg github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -831,6 +883,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= @@ -887,6 +941,8 @@ github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= github.com/golang/glog v1.2.3/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -1085,6 +1141,8 @@ github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11 github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= +github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= +github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -1104,6 +1162,8 @@ github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5 github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= github.com/testcontainers/testcontainers-go v0.37.0 h1:L2Qc0vkTw2EHWQ08djon0D2uw7Z/PtHS/QzZZ5Ra/hg= github.com/testcontainers/testcontainers-go v0.37.0/go.mod h1:QPzbxZhQ6Bclip9igjLFj6z0hs01bU8lrl2dHQmgFGM= +github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= +github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -1137,6 +1197,8 @@ go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//sn go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko= @@ -1169,7 +1231,6 @@ go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxt go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= @@ -1194,6 +1255,7 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -1261,7 +1323,7 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1287,7 +1349,7 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1318,7 +1380,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1326,6 +1387,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b h1:DU+gwOBXU+6bO0sEyO7o/NeMlxZxCZEvI7v+J4a1zRQ= +golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1336,7 +1399,6 @@ golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1492,6 +1554,8 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b73 google.golang.org/genproto/googleapis/bytestream v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250728155136-f173205681a0 h1:HyHZz/b6hYFm/aS2F0DOAyjK5mQw0Jh8YiZyqlXMfic= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250728155136-f173205681a0/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= @@ -1512,7 +1576,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go index 45e613b2d..9bdeeae3b 100644 --- a/pkg/gofr/container/mock_container.go +++ b/pkg/gofr/container/mock_container.go @@ -35,34 +35,6 @@ type Mocks struct { ScyllaDB *MockScyllaDB } -func newMocks(t *testing.T, ctrl *gomock.Controller) (*Mocks, *sqlMockDB) { - t.Helper() - mockDB, sqlMock, _ := sql.NewSQLMocks(t) - expectation := expectedQuery{} - sqlMockWrapper := &mockSQL{sqlMock, &expectation} - sqlDB := &sqlMockDB{mockDB, &expectation, logging.NewLogger(logging.DEBUG)} - sqlDB.finish(t) - - return &Mocks{ - Redis: NewMockRedis(ctrl), - SQL: sqlMockWrapper, - Clickhouse: NewMockClickhouse(ctrl), - Cassandra: NewMockCassandraWithContext(ctrl), - Mongo: NewMockMongo(ctrl), - KVStore: NewMockKVStore(ctrl), - DGraph: NewMockDgraph(ctrl), - ArangoDB: NewMockArangoDBProvider(ctrl), - OpenTSDB: NewMockOpenTSDB(ctrl), - SurrealDB: NewMockSurrealDB(ctrl), - Elasticsearch: NewMockElasticsearch(ctrl), - PubSub: NewMockPubSubProvider(ctrl), - Couchbase: NewMockCouchbase(ctrl), - File: file.NewMockFileSystemProvider(ctrl), - Metrics: NewMockMetrics(ctrl), - ScyllaDB: NewMockScyllaDB(ctrl), - }, sqlDB -} - type options func(c *Container, ctrl *gomock.Controller) any func WithMockHTTPService(httpServiceNames ...string) options { //nolint:revive // WithMockHTTPService returns an @@ -106,6 +78,8 @@ func setContainerMocks(c *Container, ctrl *gomock.Controller) { c.ScyllaDB = NewMockScyllaDB(ctrl) c.PubSub = NewMockPubSubProvider(ctrl) + + c.Couchbase = NewMockCouchbase(ctrl) } func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { @@ -130,12 +104,45 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { // Initialize all other mocks via helpers. setContainerMocks(container, ctrl) - container.PubSub = mocks.PubSub - - container.Couchbase = mocks.Couchbase + var httpMock *service.MockHTTP container.Services = make(map[string]service.HTTP) + for _, option := range options { + optionsAdded := option(container, ctrl) + + val, ok := optionsAdded.(*service.MockHTTP) + if ok { + httpMock = val + } + } + + // Setup expectations/mockmetrics + container.Redis.(*MockRedis).EXPECT().Close().AnyTimes() + + mockMetrics := NewMockMetrics(ctrl) + container.metricsManager = mockMetrics + + mocks := Mocks{ + Redis: container.Redis.(*MockRedis), + SQL: sqlMockWrapper, + Clickhouse: container.Clickhouse.(*MockClickhouse), + Cassandra: container.Cassandra.(*MockCassandraWithContext), + Mongo: container.Mongo.(*MockMongo), + KVStore: container.KVStore.(*MockKVStore), + File: container.File.(*file.MockFileSystemProvider), + HTTPService: httpMock, + DGraph: container.DGraph.(*MockDgraph), + OpenTSDB: container.OpenTSDB.(*MockOpenTSDB), + ArangoDB: container.ArangoDB.(*MockArangoDBProvider), + SurrealDB: container.SurrealDB.(*MockSurrealDB), + Elasticsearch: container.Elasticsearch.(*MockElasticsearch), + PubSub: container.PubSub.(*MockPubSubProvider), + Metrics: mockMetrics, + Oracle: container.Oracle.(*MockOracleDB), + ScyllaDB: container.ScyllaDB.(*MockScyllaDB), + } + container.metricsManager = mocks.Metrics // TODO: Remove this expectation from mock container (previous generalization) to the actual tests where their expectations are being set. mocks.Metrics.EXPECT().RecordHistogram(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), @@ -153,10 +160,10 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { // Setup expectations/mockmetrics container.Redis.(*MockRedis).EXPECT().Close().AnyTimes() - mockMetrics := NewMockMetrics(ctrl) + mockMetrics = NewMockMetrics(ctrl) container.metricsManager = mockMetrics - mocks := Mocks{ + mocks = Mocks{ Redis: container.Redis.(*MockRedis), SQL: sqlMockWrapper, Clickhouse: container.Clickhouse.(*MockClickhouse), @@ -174,9 +181,10 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { Metrics: mockMetrics, Oracle: container.Oracle.(*MockOracleDB), ScyllaDB: container.ScyllaDB.(*MockScyllaDB), + Couchbase: container.Couchbase.(*MockCouchbase), } - return container, mocks + return container, &mocks } type MockPubSub struct{} From a10ccefaa84cef397adef5c7597d877d330030eb Mon Sep 17 00:00:00 2001 From: Umang01-hash Date: Tue, 12 Aug 2025 20:39:22 +0530 Subject: [PATCH 22/22] update docs and remove
--- docs/datasources/couchbase/page.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/datasources/couchbase/page.md b/docs/datasources/couchbase/page.md index e6f957fc6..63adfffc9 100644 --- a/docs/datasources/couchbase/page.md +++ b/docs/datasources/couchbase/page.md @@ -19,8 +19,8 @@ type Couchbase interface { } ``` -Users can easily inject a driver that supports this interface, providing usability without compromising the extensibility to use multiple databases.
-Don't forget to serup the Couchbase cluster in Couchbase Web Console first. [Follow for more details](https://docs.couchbase.com/server/current/install/getting-started-docker.html#section_jvt_zvj_42b).
+Users can easily inject a driver that supports this interface, providing usability without compromising the extensibility to use multiple databases. +Don't forget to serup the Couchbase cluster in Couchbase Web Console first. [Follow for more details](https://docs.couchbase.com/server/current/install/getting-started-docker.html#section_jvt_zvj_42b). To begin using Couchbase in your GoFr application, you need to import the Couchbase datasource package: ```shell