diff --git a/.gitignore b/.gitignore index 2a5970e3..4935fdf5 100644 --- a/.gitignore +++ b/.gitignore @@ -186,3 +186,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +.cycle/ diff --git a/Makefile b/Makefile index ddd873f7..fb6aa917 100644 --- a/Makefile +++ b/Makefile @@ -4,29 +4,36 @@ install-tools: .PHONY: run-catalogs-write-service run-catalogs-write-service: - @./scripts/run.sh catalog_write_service + @./scripts/run.sh catalogwriteservice .PHONY: run-catalog-read-service run-catalog-read-service: - @./scripts/run.sh catalog_read_service + @./scripts/run.sh catalogreadservice .PHONY: run-order-service run-order-service: - @./scripts/run.sh order_service + @./scripts/run.sh orderservice .PHONY: build build: @./scripts/build.sh pkg - @./scripts/build.sh catalog_write_service - @./scripts/build.sh catalog_read_service - @./scripts/build.sh order_service + @./scripts/build.sh catalogwriteservice + @./scripts/build.sh catalogreadservice + @./scripts/build.sh orderservice + +.PHONY: update-dependencies +update-dependencies: + @./scripts/update-dependencies.sh pkg + @./scripts/update-dependencies.sh catalogwriteservice + @./scripts/update-dependencies.sh catalogreadservice + @./scripts/update-dependencies.sh orderservice .PHONY: install-dependencies install-dependencies: @./scripts/install-dependencies.sh pkg - @./scripts/install-dependencies.sh catalog_write_service - @./scripts/install-dependencies.sh catalog_read_service - @./scripts/install-dependencies.sh order_service + @./scripts/install-dependencies.sh catalogwriteservice + @./scripts/install-dependencies.sh catalogreadservice + @./scripts/install-dependencies.sh orderservice .PHONY: docker-compose-infra-up docker-compose-infra-up: @@ -37,33 +44,33 @@ docker-compose-infra-down: .PHONY: openapi openapi: - @./scripts/openapi.sh catalog_write_service - @./scripts/openapi.sh catalog_read_service - @./scripts/openapi.sh order_service + @./scripts/openapi.sh catalogwriteservice + @./scripts/openapi.sh catalogreadservice + @./scripts/openapi.sh orderservice # https://stackoverflow.com/questions/13616033/install-protocol-buffers-on-windows .PHONY: proto proto: - @./scripts/proto.sh catalog_write_service - @./scripts/proto.sh order_service + @./scripts/proto.sh catalogwriteservice + @./scripts/proto.sh orderservice .PHONY: unit-test unit-test: - @./scripts/test.sh catalog_read_service unit - @./scripts/test.sh catalog_write_service unit - @./scripts/test.sh order_service unit + @./scripts/test.sh catalogreadservice unit + @./scripts/test.sh catalogwriteservice unit + @./scripts/test.sh orderservice unit .PHONY: integration-test integration-test: - @./scripts/test.sh catalog_read_service integration - @./scripts/test.sh catalog_write_service integration - @./scripts/test.sh order_service integration + @./scripts/test.sh catalogreadservice integration + @./scripts/test.sh catalogwriteservice integration + @./scripts/test.sh orderservice integration .PHONY: e2e-test e2e-test: - @./scripts/test.sh catalog_read_service e2e - @./scripts/test.sh catalog_write_service e2e - @./scripts/test.sh order_service e2e + @./scripts/test.sh catalogreadservice e2e + @./scripts/test.sh catalogwriteservice e2e + @./scripts/test.sh orderservice e2e #.PHONY: load-test #load-test: @@ -73,16 +80,16 @@ e2e-test: .PHONY: format format: - @./scripts/format.sh catalog_write_service - @./scripts/format.sh catalog_read_service - @./scripts/format.sh order_service + @./scripts/format.sh catalogwriteservice + @./scripts/format.sh catalogreadservice + @./scripts/format.sh orderservice @./scripts/format.sh pkg .PHONY: lint lint: - @./scripts/lint.sh catalog_write_service - @./scripts/lint.sh catalog_read_service - @./scripts/lint.sh order_service + @./scripts/lint.sh catalogwriteservice + @./scripts/lint.sh catalogreadservice + @./scripts/lint.sh orderservice @./scripts/lint.sh pkg # https://github.com/golang-migrate/migrate/blob/856ea12df9d230b0145e23d951b7dbd6b86621cb/database/postgres/TUTORIAL.md @@ -91,22 +98,29 @@ lint: # https://github.com/golang-migrate/migrate/tree/856ea12df9d230b0145e23d951b7dbd6b86621cb/cmd/migrate#usage .PHONY: go-migrate go-migrate: - @./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c create -n create_product_table - @./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c up -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable - @./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c down -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable + @./scripts/go-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/go-migrate -c create -n create_product_table + @./scripts/go-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/go-migrate -c up -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable + @./scripts/go-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/go-migrate -c down -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable # https://github.com/pressly/goose#usage .PHONY: goose-migrate goose-migrate: - @./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c create -n create_product_table - @./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c up -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable" - @./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c down -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable" + @./scripts/goose-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/goose-migrate -c create -n create_product_table + @./scripts/goose-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/goose-migrate -c up -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable" + @./scripts/goose-migrate.sh -p ./internal/services/catalogwriteservice/db/migrations/goose-migrate -c down -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable" # https://atlasgo.io/guides/orms/gorm .PHONY: atlas atlas: - @./scripts/atlas-migrate.sh -c gorm-sync -p "./internal/services/catalog_write_service" - @./scripts/atlas-migrate.sh -c apply -p "./internal/services/catalog_write_service" -o "postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable" + @./scripts/atlas-migrate.sh -c gorm-sync -p "./internal/services/catalogwriteservice" + @./scripts/atlas-migrate.sh -c apply -p "./internal/services/catalogwriteservice" -o "postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable" + +.PHONY: cycle-check +cycle-check: + cd internal/pkg && goimportcycle -dot imports.dot dot -Tpng -o cycle/pkg.png imports.dot + cd internal/services/catalogwriteservice && goimportcycle -dot imports.dot dot -Tpng -o cycle/catalogwriteservice.png imports.dot + cd internal/services/catalogwriteservice && goimportcycle -dot imports.dot dot -Tpng -o cycle/catalogwriteservice.png imports.dot + cd internal/services/orderservice && goimportcycle -dot imports.dot dot -Tpng -o cycle/orderservice.png imports.dot #.PHONY: c4 #c4: @@ -123,6 +137,6 @@ pkg-mocks: .PHONY: services-mocks services-mocks: - cd internal/services/catalog_write_service && mockery --output mocks --all - cd internal/services/catalog_read_service && mockery --output mocks --all - cd internal/services/order_service && mockery --output mocks --all + cd internal/services/catalogwriteservice && mockery --output mocks --all + cd internal/services/catalogreadservice && mockery --output mocks --all + cd internal/services/orderservice && mockery --output mocks --all diff --git a/api/openapi/catalog_read_service/docs.go b/api/openapi/catalogreadservice/docs.go similarity index 99% rename from api/openapi/catalog_read_service/docs.go rename to api/openapi/catalogreadservice/docs.go index 1c44e7b8..c6384bf4 100644 --- a/api/openapi/catalog_read_service/docs.go +++ b/api/openapi/catalogreadservice/docs.go @@ -1,6 +1,6 @@ // Code generated by swaggo/swag. DO NOT EDIT. -package catalog_read_service +package catalogreadservice import "github.com/swaggo/swag" diff --git a/api/openapi/catalog_read_service/swagger.json b/api/openapi/catalogreadservice/swagger.json similarity index 100% rename from api/openapi/catalog_read_service/swagger.json rename to api/openapi/catalogreadservice/swagger.json diff --git a/api/openapi/catalog_read_service/swagger.yaml b/api/openapi/catalogreadservice/swagger.yaml similarity index 100% rename from api/openapi/catalog_read_service/swagger.yaml rename to api/openapi/catalogreadservice/swagger.yaml diff --git a/api/openapi/catalog_write_service/docs.go b/api/openapi/catalogwriteservice/docs.go similarity index 99% rename from api/openapi/catalog_write_service/docs.go rename to api/openapi/catalogwriteservice/docs.go index 5f8aa576..ca894145 100644 --- a/api/openapi/catalog_write_service/docs.go +++ b/api/openapi/catalogwriteservice/docs.go @@ -1,6 +1,6 @@ // Code generated by swaggo/swag. DO NOT EDIT. -package catalog_write_service +package catalogwriteservice import "github.com/swaggo/swag" diff --git a/api/openapi/catalog_write_service/swagger.json b/api/openapi/catalogwriteservice/swagger.json similarity index 100% rename from api/openapi/catalog_write_service/swagger.json rename to api/openapi/catalogwriteservice/swagger.json diff --git a/api/openapi/catalog_write_service/swagger.yaml b/api/openapi/catalogwriteservice/swagger.yaml similarity index 100% rename from api/openapi/catalog_write_service/swagger.yaml rename to api/openapi/catalogwriteservice/swagger.yaml diff --git a/api/openapi/order_service/docs.go b/api/openapi/orderservice/docs.go similarity index 99% rename from api/openapi/order_service/docs.go rename to api/openapi/orderservice/docs.go index 934905f3..119e33d6 100644 --- a/api/openapi/order_service/docs.go +++ b/api/openapi/orderservice/docs.go @@ -1,6 +1,6 @@ // Code generated by swaggo/swag. DO NOT EDIT. -package order_service +package orderservice import "github.com/swaggo/swag" diff --git a/api/openapi/order_service/swagger.json b/api/openapi/orderservice/swagger.json similarity index 100% rename from api/openapi/order_service/swagger.json rename to api/openapi/orderservice/swagger.json diff --git a/api/openapi/order_service/swagger.yaml b/api/openapi/orderservice/swagger.yaml similarity index 100% rename from api/openapi/order_service/swagger.yaml rename to api/openapi/orderservice/swagger.yaml diff --git a/api/protobuf/catalog_write_service/products.proto b/api/protobuf/catalogwriteservice/products.proto similarity index 100% rename from api/protobuf/catalog_write_service/products.proto rename to api/protobuf/catalogwriteservice/products.proto diff --git a/api/protobuf/order_service/orders.proto b/api/protobuf/orderservice/orders.proto similarity index 100% rename from api/protobuf/order_service/orders.proto rename to api/protobuf/orderservice/orders.proto diff --git a/deployments/docker-compose/.env b/deployments/docker-compose/.env index 91bcd9ee..995bdc94 100644 --- a/deployments/docker-compose/.env +++ b/deployments/docker-compose/.env @@ -22,8 +22,7 @@ PROMETHEUS_PORT=9090 PROMETHEUS_HOST_PORT=9090 KIBANA_PORT=5601 KIBANA_HOST_PORT=5601 -ELASTIC_PORT=9200 -ELASTIC_HOST_PORT=9200 + EVENTSTORE_PORT=2113 EVENTSTORE_HOST_PORT=2113 EVENTSTORE_TCP_HOST_PORT=1113 @@ -44,3 +43,45 @@ CUSTOMERS_SVC_PORT=8000 ORDERS_SVC_PORT=5000 IDENTITY_SVC_PORT=7000 +# ****************** +# Dependent Services +# ****************** +# Kafka +KAFKA_SERVICE_PORT=9092 + +# Redis +REDIS_PORT=6379 + +# Elastic +ELASTIC_PORT=9200 +ELASTIC_HOST_PORT=9200 + +# ******************** +# Telemetry Components +# ******************** +# Grafana +GRAFANA_SERVICE_PORT=3000 +GRAFANA_SERVICE_HOST=grafana + +# Jaeger +JAEGER_SERVICE_PORT=16686 +JAEGER_SERVICE_HOST=jaeger + +# Prometheus +PROMETHEUS_SERVICE_PORT=9090 +PROMETHEUS_SERVICE_HOST=prometheus +PROMETHEUS_ADDR=${PROMETHEUS_SERVICE_HOST}:${PROMETHEUS_SERVICE_PORT} + + +# OpenTelemetry Collector +OTEL_COLLECTOR_HOST=otelcol +OTEL_COLLECTOR_PORT_GRPC=4317 +OTEL_COLLECTOR_PORT_HTTP=4318 +OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_GRPC} +PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:8080/otlp-http/v1/traces + +# OpenTelemetry Resource Definitions +OTEL_RESOURCE_ATTRIBUTES="service.namespace=ecommerce" + +# Metrics Temporality +OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative diff --git a/deployments/docker-compose/docker-compose.infrastructure-collector.yaml b/deployments/docker-compose/docker-compose.infrastructure-collector.yaml new file mode 100644 index 00000000..841eeb9a --- /dev/null +++ b/deployments/docker-compose/docker-compose.infrastructure-collector.yaml @@ -0,0 +1,383 @@ +# Ref:https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/docker-compose.yaml +# https://github.com/open-telemetry/opentelemetry-demo/blob/main/docker-compose.yml +# https://www.youtube.com/watch?v=EeU-k659lpw + +version: "3.8" +name: go-ecommerce-microservices + +services: + rabbitmq: + image: rabbitmq:management + container_name: rabbitmq + pull_policy: if_not_present + restart: unless-stopped + ports: + - ${RABBITMQ_HOST_PORT:-5672}:${RABBITMQ_PORT:-5672} + - ${RABBITMQ_HOST_API_PORT:-15672}:${RABBITMQ_API_PORT:-15672} + # volumes: + # - rabbitmq:/var/lib/rabbitmq + networks: + - ecommerce + + # containers monitoring + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + restart: unless-stopped + ports: + - "9091:8080" + # network_mode: host + volumes: + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + devices: + - /dev/kmsg + networks: + - ecommerce + + # prometheus dashboard: http://localhost:9090 + # prometheus internal metrics: http://localhost:9090/metrics + # https://prometheus.io/docs/prometheus/latest/getting_started/ + # https://prometheus.io/docs/guides/go-application/ + prometheus: + image: prom/prometheus:latest + pull_policy: if_not_present + container_name: prometheus + restart: unless-stopped + user: root + ports: + - ${PROMETHEUS_HOST_PORT:-9090}:${PROMETHEUS_PORT:-9090} + command: + - --config.file=/etc/prometheus/prometheus.yml + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + networks: + - ecommerce + + otelcol: + image: otel/opentelemetry-collector-contrib:latest + container_name: otel-col + pull_policy: if_not_present + deploy: + resources: + limits: + memory: 125M + restart: unless-stopped + command: [ "--config=/etc/otelcol-config.yml", "--config=/etc/otelcol-observability.yml", "--config=/etc/otelcol-config-extras.yml" ] + volumes: + - ./otelcollector/otelcol-config.yml:/etc/otelcol-config.yml + - ./otelcollector/otelcol-observability.yml:/etc/otelcol-observability.yml + - ./otelcollector/otelcol-config-extras.yml:/etc/otelcol-config-extras.yml + ports: + - "4317:4317" # OTLP over gRPC receiver + - "4318:4318" # OTLP over HTTP receiver + - "8889:8889" # Prometheus exporter metrics + - "8888:8888" # Prometheus metrics exposed by the collector + - "13133:13133" # health_check extension + - "55679:55679" # zpages extension + - "1888:1888" # pprof extension + depends_on: + prometheus: + condition: service_healthy + tempo: + condition: service_healthy + jaeger: + condition: service_healthy + + # node_exporter will use for gathering metrics on the system level with its own /metrics endpoint like cpu, ram, ... + # https://prometheus.io/docs/guides/node-exporter/ + node_exporter: + container_name: node_exporter + pull_policy: if_not_present + restart: unless-stopped + image: prom/node-exporter + ports: + - "9100:9100" + networks: + - ecommerce + + grafana: + image: grafana/grafana + pull_policy: if_not_present + container_name: grafana + restart: unless-stopped + volumes: + - ./monitoring/grafana.yaml:/etc/grafana/provisioning/datasources/datasource.yaml + - ./monitoring/grafana-bootstrap.ini:/etc/grafana/grafana.ini + ports: + - ${GRAFANA_HOST_PORT:-3000}:${GRAFANA_PORT:-3000} + environment: + - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor + healthcheck: + interval: 5s + retries: 10 + test: wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1 + depends_on: + prometheus: + condition: service_healthy + tempo: + condition: service_healthy + networks: + - ecommerce + + # https://grafana.com/docs/tempo/latest/getting-started/ + # https://github.com/grafana/tempo/blob/main/example/docker-compose/local/docker-compose.yaml + # https://github.com/Domoryonok/tracing_demo/blob/master/grafana/docker-compose.yaml + # https://grafana.com/docs/grafana/latest/datasources/jaeger/ + # https://grafana.com/docs/tempo/latest/operations/architecture/ + tempo: + image: grafana/tempo:latest + command: ["-config.file=/etc/tempo.yaml" ] + volumes: + - ./otelcollector/tempo.yaml:/etc/tempo.yaml + - ./tempo-data:/tmp/tempo + ports: + - "3200:3200" # tempo UI + - "4322:4317" # otlp grpc + - "9411" # zipkin - export zipkin traces to tempo + - "14268" # jaeger - export jaeger traces to tempo + healthcheck: + interval: 5s + retries: 10 + test: wget --no-verbose --tries=1 --spider http://localhost:3200/status || exit 1 + networks: + - ecommerce + + postgres: + image: postgres:latest + pull_policy: if_not_present + container_name: postgres + restart: unless-stopped + ports: + - ${POSTGRES_HOST_PORT:-5432}:${POSTGRES_PORT:-5432} + #https://docs.docker.com/compose/environment-variables/env-file/#parameter-expansion + environment: + - POSTGRES_USER=${POSTGRES_USER:-postgres} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} + networks: + - ecommerce + + # https://developer.redis.com/howtos/quick-start + # redis-stack is a image with redis modules enabled like JSON module + redis: + image: redis/redis-stack:latest + pull_policy: if_not_present + restart: unless-stopped + container_name: redis + ports: + - "6379:6379" + networks: + - ecommerce + + mongo: + image: mongo + pull_policy: if_not_present + container_name: mongo + restart: unless-stopped + # https://docs.docker.com/compose/environment-variables/env-file/#parameter-expansion + environment: + - MONGO_INITDB_ROOT_USERNAME=${MONGO_USER:-admin} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASS:-admin} + ports: + - ${MONGO_HOST_PORT:-27017}:${MONGO_PORT:-27017} + networks: + - ecommerce + + # https://www.jaegertracing.io/docs/1.38/apis/#opentelemetry-protocol-stable + # https://deploy-preview-1892--opentelemetry.netlify.app/blog/2022/jaeger-native-otlp/ + # https://www.jaegertracing.io/docs/1.49/deployment/ + # https://www.jaegertracing.io/docs/1.49/getting-started/ + # https://opentelemetry.io/docs/instrumentation/go/exporters/ + # https://opentelemetry.io/docs/specs/otlp/ + jaeger: + image: jaegertracing/all-in-one:latest + container_name: jaeger + pull_policy: if_not_present + restart: unless-stopped + command: + - "--memory.max-traces" + - "10000" + - "--query.base-path" + - "/jaeger/ui" + - "--prometheus.server-url" + - "http://${PROMETHEUS_ADDR}" + environment: + - COLLECTOR_ZIPKIN_HOST_PORT=:9411 + - COLLECTOR_OTLP_ENABLED=true + # Store metrics in PROMETHEUS storage instead of in-memory storage + - METRICS_STORAGE_TYPE=prometheus + - PROMETHEUS_SERVER_URL=http:${PROMETHEUS_ADDR} + # # Jaeger uses Elasticsearch as span storage instead of in-memory storage + # - SPAN_STORAGE_TYPE=elasticsearch + # - ES_SERVER_URLS=http://elastic_search:${ELASTIC_PORT:-9200} + # - ES_VERSION=8 + ports: + - "16686:16686" # Jaeger UI port + - "4320:4317" # OTLP gRPC default port - for prevent duplicate expose this port that will expose also by `otel-collector` we not expose it on `4317` - `4320` could use by the app through otlptracegrpc + - "4321:4318" # OTLP Http default port - for prevent duplicate expose this port that will expose also by `otel-collector` we not expose it on `4318` - `4321` could use by the app through otlptracehttp + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:16686" ] + interval: 10s + retries: 3 + timeout: 10s + networks: + - ecommerce + + zipkin: + image: openzipkin/zipkin:latest + pull_policy: if_not_present + restart: unless-stopped + container_name: zipkin + ports: + - "9411:9411" + networks: + - ecommerce + + # https://developers.eventstore.com/server/v21.10/installation.html#insecure-single-node + # https://hub.docker.com/r/eventstore/eventstore/tags + # https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to + # EVENTSTORE_MEM_DB=true, it tells the EventStoreDB container to use an in-memory database, which means that any data stored in EventStoreDB will not be persisted between container restarts. Once the container is stopped or restarted, all data will be lost. + eventstore: + image: eventstore/eventstore:latest + pull_policy: if_not_present + container_name: eventstore + restart: unless-stopped + environment: + - EVENTSTORE_CLUSTER_SIZE=1 + - EVENTSTORE_RUN_PROJECTIONS=All + - EVENTSTORE_START_STANDARD_PROJECTIONS=false + - EVENTSTORE_EXT_TCP_PORT=1113 + - EVENTSTORE_HTTP_PORT=2113 + - EVENTSTORE_INSECURE=true + - EVENTSTORE_ENABLE_EXTERNAL_TCP=true + - EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true + - EVENTSTORE_MEM_DB=true + ports: + - ${EVENTSTORE_TCP_HOST_PORT:-1113}:${EVENTSTORE_TCP_PORT:-1113} + - ${EVENTSTORE_HOST_PORT:-2113}:${EVENTSTORE_PORT:-2113} + volumes: + - type: volume + source: eventstore-volume-data + target: /var/lib/eventstore + - type: volume + source: eventstore-volume-logs + target: /var/log/eventstore + networks: + - ecommerce + + # # https://stackoverflow.com/questions/67791781/how-to-configure-apm-server-to-docker-compose-file + # # https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html + # apm-server: + # image: docker.elastic.co/apm/apm-server:latest + # cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] + # cap_drop: ["ALL"] + # ports: + # - 8200:8200 + # command: > + # apm-server -e + # -E apm-server.rum.enabled=true + # -E setup.kibana.host=kibana:5601 + # -E setup.template.settings.index.number_of_replicas=0 + # -E apm-server.kibana.enabled=true + # -E apm-server.kibana.host=kibana:5601 + # -E output.elasticsearch.hosts=["elasticsearch:9200"] + + # elasticsearch: + # container_name: elastic_search + # restart: unless-stopped + # image: elasticsearch:8.5.2 + # environment: + # - discovery.type=single-node + # - bootstrap.memory_lock=true + # - xpack.monitoring.enabled=true + # - xpack.watcher.enabled=false + # - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + # ulimits: + # memlock: + # soft: -1 + # hard: -1 + # volumes: + # - elastic-data:/usr/share/elasticsearch/data + # ports: + # - ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200} + # - 9300:9300 + # networks: + # - ecommerce + + # kibana: + # image: kibana:8.5.2 + # container_name: kibana + # restart: unless-stopped + # environment: + # - ELASTICSEARCH_HOSTS=http://elastic_search:9200 + # ports: + # - ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601} + # networks: + # - ecommerce + # depends_on: + # - elasticsearch + + # zookeeper: + # image: confluentinc/cp-zookeeper:7.0.1 + # hostname: zookeeper + # container_name: zookeeper + # restart: unless-stopped + # ports: + # - "2181:2181" + # environment: + # ZOOKEEPER_CLIENT_PORT: 2181 + # ZOOKEEPER_TICK_TIME: 2000 + # networks: + # - ecommerce + + # kafka: + # image: confluentinc/cp-kafka:7.0.1 + # hostname: kafka + # container_name: kafka + # restart: unless-stopped + # depends_on: + # - zookeeper + # ports: + # - "9092:9092" + # environment: + # KAFKA_BROKER_ID: 1 + # KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + # ADVERTISED_HOST_NAME: kafka + # KAFKA_ADVERTISED_HOSTNAME: 127.0.0.1 + # networks: + # - ecommerce + + # kafka-ui: + # image: provectuslabs/kafka-ui + # container_name: kafka-ui + # ports: + # - "8080:8080" + # restart: always + # environment: + # - KAFKA_CLUSTERS_0_NAME=local + # - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092 + # - KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181 + + # kafdrop: + # image: obsidiandynamics/kafdrop + # container_name: kafdrop + # ports: + # - '9000:9000' + # environment: + # - 'KAFKA_BROKERCONNECT=' + # - 'JVM_OPTS=-Xms32M -Xmx64M' + # - SERVER_SERVLET_CONTEXTPATH=/ + +volumes: + eventstore-volume-data: + eventstore-volume-logs: + elastic-data: + +networks: + ecommerce: + name: ecommerce diff --git a/deployments/docker-compose/docker-compose.infrastructure.yaml b/deployments/docker-compose/docker-compose.infrastructure.yaml index ca68fdd6..4b24e5c7 100644 --- a/deployments/docker-compose/docker-compose.infrastructure.yaml +++ b/deployments/docker-compose/docker-compose.infrastructure.yaml @@ -1,3 +1,8 @@ +# Ref:https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/docker-compose.yaml +# https://github.com/open-telemetry/opentelemetry-demo/blob/main/docker-compose.yml +# https://github.com/grafana/tempo/blob/main/example/docker-compose/local/docker-compose.yaml +# https://github.com/build-on-aws/instrumenting-java-apps-using-opentelemetry/blob/main/docker-compose.yaml + version: "3.8" name: go-ecommerce-microservices @@ -6,7 +11,7 @@ services: image: rabbitmq:management container_name: rabbitmq pull_policy: if_not_present - restart: on-failure + restart: unless-stopped ports: - ${RABBITMQ_HOST_PORT:-5672}:${RABBITMQ_PORT:-5672} - ${RABBITMQ_HOST_API_PORT:-15672}:${RABBITMQ_API_PORT:-15672} @@ -15,46 +20,102 @@ services: networks: - ecommerce + # prometheus dashboard: http://localhost:9090 + # prometheus internal metrics: http://localhost:9090/metrics + # https://prometheus.io/docs/prometheus/latest/getting_started/ + # https://prometheus.io/docs/guides/go-application/ prometheus: image: prom/prometheus:latest pull_policy: if_not_present container_name: prometheus - restart: on-failure + restart: unless-stopped user: root ports: - ${PROMETHEUS_HOST_PORT:-9090}:${PROMETHEUS_PORT:-9090} command: - --config.file=/etc/prometheus/prometheus.yml + - --web.enable-remote-write-receiver + - --enable-feature=exemplar-storage volumes: - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + healthcheck: + interval: 5s + retries: 10 + test: wget --no-verbose --tries=1 --spider http://localhost:9090/status || exit 1 networks: - ecommerce - node_exporter: - container_name: node_exporter +# # node_exporter will use for gathering metrics on the system level with its own /metrics endpoint (http://localhost:9100/metrics) like cpu, ram, ... +# # https://prometheus.io/docs/guides/node-exporter/ +# node_exporter: +# container_name: node_exporter +# pull_policy: if_not_present +# restart: unless-stopped +# image: prom/node-exporter +# ports: +# - "9100:9100" +# networks: +# - ecommerce + + grafana: + image: grafana/grafana:latest pull_policy: if_not_present - restart: on-failure - image: prom/node-exporter + container_name: grafana + restart: unless-stopped + volumes: + - ./monitoring/grafana.yaml:/etc/grafana/provisioning/datasources/datasource.yaml + - ./monitoring/grafana-bootstrap.ini:/etc/grafana/grafana.ini ports: - - "9101:9100" + - ${GRAFANA_HOST_PORT:-3000}:${GRAFANA_PORT:-3000} + environment: + - GF_FEATURE_TOGGLES_ENABLE=traceqlEditor + healthcheck: + interval: 5s + retries: 10 + test: wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1 + depends_on: + prometheus: + condition: service_healthy + tempo: + condition: service_healthy networks: - ecommerce - grafana: - container_name: grafana - pull_policy: if_not_present - restart: on-failure - image: grafana/grafana + # https://grafana.com/docs/tempo/latest/getting-started/ + # https://github.com/grafana/tempo/blob/main/example/docker-compose/local/docker-compose.yaml + # https://github.com/Domoryonok/tracing_demo/blob/master/grafana/docker-compose.yaml + # https://grafana.com/docs/grafana/latest/datasources/jaeger/ + # https://grafana.com/docs/tempo/latest/operations/architecture/ + tempo: + image: grafana/tempo:latest + command: ["-config.file=/etc/tempo.yaml" ] + volumes: + - ./otelcollector/tempo.yaml:/etc/tempo.yaml ports: - - ${GRAFANA_HOST_PORT:-3000}:${GRAFANA_PORT:-3000} + - "3200:3200" # tempo UI + - "4322:4317" # otlp grpc + - "9411" # zipkin - export zipkin traces to tempo + - "14268" # jaeger - export jaeger traces to tempo + healthcheck: + interval: 5s + retries: 10 + test: wget --no-verbose --tries=1 --spider http://localhost:3200/status || exit 1 networks: - ecommerce +# # k6-tracing: +# # image: ghcr.io/grafana/xk6-client-tracing:latest +# # environment: +# # - ENDPOINT=tempo:4317 +# # restart: always +# # depends_on: +# # - tempo + postgres: image: postgres:latest pull_policy: if_not_present container_name: postgres - restart: on-failure + restart: unless-stopped ports: - ${POSTGRES_HOST_PORT:-5432}:${POSTGRES_PORT:-5432} #https://docs.docker.com/compose/environment-variables/env-file/#parameter-expansion @@ -69,72 +130,18 @@ services: redis: image: redis/redis-stack:latest pull_policy: if_not_present - restart: on-failure + restart: unless-stopped container_name: redis ports: - "6379:6379" networks: - ecommerce - # zookeeper: - # image: confluentinc/cp-zookeeper:7.0.1 - # hostname: zookeeper - # container_name: zookeeper - # restart: on-failure - # ports: - # - "2181:2181" - # environment: - # ZOOKEEPER_CLIENT_PORT: 2181 - # ZOOKEEPER_TICK_TIME: 2000 - # networks: - # - ecommerce - - # kafka: - # image: confluentinc/cp-kafka:7.0.1 - # hostname: kafka - # container_name: kafka - # restart: on-failure - # depends_on: - # - zookeeper - # ports: - # - "9092:9092" - # environment: - # KAFKA_BROKER_ID: 1 - # KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' - # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT - # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 - # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - # ADVERTISED_HOST_NAME: kafka - # KAFKA_ADVERTISED_HOSTNAME: 127.0.0.1 - # networks: - # - ecommerce - - # kafka-ui: - # image: provectuslabs/kafka-ui - # container_name: kafka-ui - # ports: - # - "8080:8080" - # restart: always - # environment: - # - KAFKA_CLUSTERS_0_NAME=local - # - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092 - # - KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181 - - # kafdrop: - # image: obsidiandynamics/kafdrop - # container_name: kafdrop - # ports: - # - '9000:9000' - # environment: - # - 'KAFKA_BROKERCONNECT=' - # - 'JVM_OPTS=-Xms32M -Xmx64M' - # - SERVER_SERVLET_CONTEXTPATH=/ - mongo: - image: mongo + image: mongo:latest pull_policy: if_not_present container_name: mongo - restart: on-failure + restart: unless-stopped # https://docs.docker.com/compose/environment-variables/env-file/#parameter-expansion environment: - MONGO_INITDB_ROOT_USERNAME=${MONGO_USER:-admin} @@ -144,46 +151,55 @@ services: networks: - ecommerce + # https://www.jaegertracing.io/docs/1.38/apis/#opentelemetry-protocol-stable + # https://deploy-preview-1892--opentelemetry.netlify.app/blog/2022/jaeger-native-otlp/ + # https://www.jaegertracing.io/docs/1.49/deployment/ + # https://www.jaegertracing.io/docs/1.49/getting-started/ + # https://opentelemetry.io/docs/instrumentation/go/exporters/ + # https://opentelemetry.io/docs/specs/otlp/ jaeger: - container_name: jaeger - pull_policy: if_not_present - restart: on-failure image: jaegertracing/all-in-one:latest - ports: - - "16686:16686" - - "14268:14268" - - "14250:14250" - networks: - - ecommerce - - zipkin: - image: openzipkin/zipkin:latest + container_name: jaeger pull_policy: if_not_present - restart: on-failure - container_name: zipkin + restart: unless-stopped + command: + - "--memory.max-traces" + - "10000" + - "--query.base-path" + - "/jaeger/ui" + - "--prometheus.server-url" + - "http://${PROMETHEUS_ADDR}" + environment: + - COLLECTOR_ZIPKIN_HOST_PORT=:9411 + - COLLECTOR_OTLP_ENABLED=true + # Store metrics in PROMETHEUS storage instead of in-memory storage + - METRICS_STORAGE_TYPE=prometheus + - PROMETHEUS_SERVER_URL=http:${PROMETHEUS_ADDR} +# # Jaeger uses Elasticsearch as span storage instead of in-memory storage +# - SPAN_STORAGE_TYPE=elasticsearch +# - ES_SERVER_URLS=http://elastic_search:${ELASTIC_PORT:-9200} +# - ES_VERSION=8 ports: - - "9411:9411" + - "16686:16686" # Jaeger UI port + - "4320:4317" # OTLP gRPC default port - for prevent duplicate expose this port that will expose also by `otel-collector` we not expose it on `4317` - `4320` could use by the app through otlptracegrpc + - "4321:4318" # OTLP Http default port - for prevent duplicate expose this port that will expose also by `otel-collector` we not expose it on `4318` - `4321` could use by the app through otlptracehttp + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:16686" ] + interval: 10s + retries: 3 + timeout: 10s networks: - ecommerce - otel-collector: - image: otel/opentelemetry-collector-contrib-dev:latest - pull_policy: if_not_present - command: ["--config=/etc/otel-collector-config.yaml", ""] - volumes: - - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ports: - - "1888:1888" # pprof extension - - "8888:8888" # Prometheus metrics exposed by the collector - - "8889:8889" # Prometheus exporter metrics - - "13133:13133" # health_check extension - - "4317:4317" # OTLP gRPC receiver - - "55679:55679" # zpages extension - depends_on: - - jaeger - - zipkin - networks: - - ecommerce +# zipkin: +# image: openzipkin/zipkin:latest +# pull_policy: if_not_present +# restart: unless-stopped +# container_name: zipkin +# ports: +# - "9411:9411" +# networks: +# - ecommerce # https://developers.eventstore.com/server/v21.10/installation.html#insecure-single-node # https://hub.docker.com/r/eventstore/eventstore/tags @@ -193,7 +209,7 @@ services: image: eventstore/eventstore:latest pull_policy: if_not_present container_name: eventstore - restart: on-failure + restart: unless-stopped environment: - EVENTSTORE_CLUSTER_SIZE=1 - EVENTSTORE_RUN_PROJECTIONS=All @@ -217,45 +233,135 @@ services: networks: - ecommerce - # elasticsearch: - # container_name: elastic_search - # restart: on-failure - # image: elasticsearch:8.5.2 - # environment: - # - discovery.type=single-node - # - bootstrap.memory_lock=true - # - xpack.monitoring.enabled=true - # - xpack.watcher.enabled=false - # - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - # ulimits: - # memlock: - # soft: -1 - # hard: -1 - # volumes: - # - elastic-data:/usr/share/elasticsearch/data - # ports: - # - ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200} - # - 9300:9300 - # networks: - # - ecommerce +# # containers monitoring - Analyzes resource usage and performance characteristics of running containers. +# cadvisor: +# image: gcr.io/cadvisor/cadvisor:latest +# restart: unless-stopped +# ports: +# - "9091:8080" +# # network_mode: host +# volumes: +# - /:/rootfs:ro +# - /var/run:/var/run:ro +# - /sys:/sys:ro +# - /var/lib/docker/:/var/lib/docker:ro +# - /dev/disk/:/dev/disk:ro +# devices: +# - /dev/kmsg +# networks: +# - ecommerce + +# # # https://stackoverflow.com/questions/67791781/how-to-configure-apm-server-to-docker-compose-file +# # # https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html +# # apm-server: +# # image: docker.elastic.co/apm/apm-server:latest +# # cap_add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"] +# # cap_drop: ["ALL"] +# # ports: +# # - 8200:8200 +# # command: > +# # apm-server -e +# # -E apm-server.rum.enabled=true +# # -E setup.kibana.host=kibana:5601 +# # -E setup.template.settings.index.number_of_replicas=0 +# # -E apm-server.kibana.enabled=true +# # -E apm-server.kibana.host=kibana:5601 +# # -E output.elasticsearch.hosts=["elasticsearch:9200"] + +# # elasticsearch: +# # container_name: elastic_search +# # restart: unless-stopped +# # image: elasticsearch:8.5.2 +# # environment: +# # - discovery.type=single-node +# # - bootstrap.memory_lock=true +# # - xpack.monitoring.enabled=true +# # - xpack.watcher.enabled=false +# # - "ES_JAVA_OPTS=-Xms512m -Xmx512m" +# # ulimits: +# # memlock: +# # soft: -1 +# # hard: -1 +# # volumes: +# # - elastic-data:/usr/share/elasticsearch/data +# # ports: +# # - ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200} +# # - 9300:9300 +# # networks: +# # - ecommerce + +# # kibana: +# # image: kibana:8.5.2 +# # container_name: kibana +# # restart: unless-stopped +# # environment: +# # - ELASTICSEARCH_HOSTS=http://elastic_search:${ELASTIC_PORT:-9200} +# # ports: +# # - ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601} +# # networks: +# # - ecommerce +# # depends_on: +# # - elasticsearch + + +# # zookeeper: +# # image: confluentinc/cp-zookeeper:7.0.1 +# # hostname: zookeeper +# # container_name: zookeeper +# # restart: unless-stopped +# # ports: +# # - "2181:2181" +# # environment: +# # ZOOKEEPER_CLIENT_PORT: 2181 +# # ZOOKEEPER_TICK_TIME: 2000 +# # networks: +# # - ecommerce + +# # kafka: +# # image: confluentinc/cp-kafka:7.0.1 +# # hostname: kafka +# # container_name: kafka +# # restart: unless-stopped +# # depends_on: +# # - zookeeper +# # ports: +# # - "9092:9092" +# # environment: +# # KAFKA_BROKER_ID: 1 +# # KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' +# # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT +# # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 +# # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 +# # ADVERTISED_HOST_NAME: kafka +# # KAFKA_ADVERTISED_HOSTNAME: 127.0.0.1 +# # networks: +# # - ecommerce + +# # kafka-ui: +# # image: provectuslabs/kafka-ui +# # container_name: kafka-ui +# # ports: +# # - "8080:8080" +# # restart: always +# # environment: +# # - KAFKA_CLUSTERS_0_NAME=local +# # - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092 +# # - KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181 - # kibana: - # image: kibana:8.5.2 - # container_name: kibana - # restart: on-failure - # environment: - # - ELASTICSEARCH_HOSTS=http://elastic_search:9200 - # ports: - # - ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601} - # networks: - # - ecommerce - # depends_on: - # - elasticsearch +# # kafdrop: +# # image: obsidiandynamics/kafdrop +# # container_name: kafdrop +# # ports: +# # - '9000:9000' +# # environment: +# # - 'KAFKA_BROKERCONNECT=' +# # - 'JVM_OPTS=-Xms32M -Xmx64M' +# # - SERVER_SERVLET_CONTEXTPATH=/ volumes: eventstore-volume-data: eventstore-volume-logs: - elastic-data: +# elastic-data: networks: ecommerce: diff --git a/deployments/docker-compose/monitoring/grafana-bootstrap.ini b/deployments/docker-compose/monitoring/grafana-bootstrap.ini new file mode 100644 index 00000000..21b3cdbd --- /dev/null +++ b/deployments/docker-compose/monitoring/grafana-bootstrap.ini @@ -0,0 +1,2 @@ +[feature_toggles] +enable = tempoSearch tempoBackendSearch diff --git a/deployments/docker-compose/monitoring/grafana.yaml b/deployments/docker-compose/monitoring/grafana.yaml new file mode 100644 index 00000000..e1001431 --- /dev/null +++ b/deployments/docker-compose/monitoring/grafana.yaml @@ -0,0 +1,46 @@ +apiVersion: 1 + +datasources: + # showing Prometheus as one of dashboard tabs + - name: Prometheus + type: prometheus + uid: prometheus + url: http://prometheus:9090 + isDefault: true + access: proxy + editable: true + jsonData: + httpMethod: GET + + # showing Jaeger as one of dashboard tabs + - name: Jaeger + type: jaeger + url: http://jaeger:16686 + access: proxy + editable: true + + # showing Tempo as one of dashboard tabs + # https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml#L16 + - name: Tempo + type: tempo + access: proxy + orgId: 1 + url: http://tempo:3200 + basicAuth: false + isDefault: false + version: 1 + editable: false + apiVersion: 1 + uid: tempo + +# # showing Loki as one of dashboard tabs +# - name: loki +# type: loki +# uid: my-loki +# access: proxy +# orgId: 1 +# url: http://loki:3100 +# basicAuth: false +# isDefault: false +# version: 1 +# editable: true diff --git a/deployments/docker-compose/monitoring/prometheus.yml b/deployments/docker-compose/monitoring/prometheus.yml index 3ed6cff7..39e05870 100644 --- a/deployments/docker-compose/monitoring/prometheus.yml +++ b/deployments/docker-compose/monitoring/prometheus.yml @@ -1,26 +1,72 @@ +# prometheus dashboard: http://localhost:9090 +# scrap metrics our app(host.docker.internal:7000/metrics) and default internal metrics in prometheus(localhost:9090/metrics) and internal metrics in grafana(localhost:3000/metrics) + global: - scrape_interval: 10s - evaluation_interval: 10s + scrape_interval: 5s + evaluation_interval: 5s scrape_configs: - - job_name: 'prometheus' + # http://localhost:9090/metrics + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + + # http://localhost:3000/metrics + # localhost doesn't work here because inner prometheus container localhost will infer to localhost inner the container which is different with system localhost. for access to actual system localhost we should use `host.docker.internal` + # instead of `localhost` to access node_exporter container inner prometheus container we can use our container network name to access this container because they are in same docker-compose and same default network + - job_name: grafana + static_configs: + - targets: [ "grafana:3000" ] + + # http://localhost:9100/metrics + # localhost doesn't work here because inner prometheus container localhost will infer to localhost inner the container which is different with system localhost. for access to actual system localhost we should use `host.docker.internal` + # instead of `localhost` to access node_exporter container inner prometheus container we can use our container network name to access this container because they are in same docker-compose and same default network + # node_exporter will use for gathering metrics on the system level with its own /metrics endpoint like cpu, ram, ... + - job_name: node + scrape_interval: 1s static_configs: - - targets: [ 'localhost:9090' ] + - targets: [ 'node_exporter:9100' ] - - job_name: 'system' + # http://localhost:7000/metrics + # localhost doesn't work here because inner prometheus container localhost will infer to localhost inner the container which is different with system localhost. for access to actual system localhost we should use `host.docker.internal` + # instead of `localhost` to access node_exporter container inner prometheus container we can use our container network name to access this container because they are in same docker-compose and same default network + - job_name: product_write_service + scrape_interval: 1s static_configs: - - targets: [ 'host.docker.internal:9101' ] + - targets: [ "host.docker.internal:7000" ] - - job_name: 'product_write_service' + # http://localhost:7001/metrics + # localhost doesn't work here because inner prometheus container localhost will infer to localhost inner the container which is different with system localhost. for access to actual system localhost we should use `host.docker.internal` + # instead of `localhost` to access node_exporter container inner prometheus container we can use our container network name to access this container because they are in same docker-compose and same default network + - job_name: product_read_service + scrape_interval: 1s static_configs: - - targets: [ 'host.docker.internal:8002' ] + - targets: [ "host.docker.internal:7001" ] - - job_name: 'product_read_service' + # http://localhost:8000/metrics + # localhost doesn't work here because inner prometheus container localhost will infer to localhost inner the container which is different with system localhost. for access to actual system localhost we should use `host.docker.internal` + # instead of `localhost` to access node_exporter container inner prometheus container we can use our container network name to access this container because they are in same docker-compose and same default network + - job_name: orderservice + scrape_interval: 1s static_configs: - - targets: [ 'host.docker.internal:8003' ] + - targets: [ "host.docker.internal:8000" ] - # - job_name: 'otel-collector' - # scrape_interval: 10s - # static_configs: - # - targets: ['otel-collector:8889'] - # - targets: ['otel-collector:8888'] \ No newline at end of file + # Example job for cadvisor + - job_name: 'cadvisor' + static_configs: + - targets: ['cadvisor:8080'] + +# # scrap exported metrics by otel-collector +# # OpenTelemetry collector +# - job_name: 'otel-collector' +# scrape_interval: 10s +# static_configs: +# - targets: ['otel-collector:8889'] +# - targets: ['otel-collector:8888'] + + # internal metrics by tempo + # https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/prometheus.yaml + - job_name: 'tempo' + static_configs: + - targets: [ 'tempo:3200' ] diff --git a/deployments/docker-compose/otel-collector-config.yaml b/deployments/docker-compose/otel-collector-config.yaml deleted file mode 100644 index b46e0393..00000000 --- a/deployments/docker-compose/otel-collector-config.yaml +++ /dev/null @@ -1,51 +0,0 @@ -receivers: - otlp: - protocols: - grpc: - -exporters: - prometheus: - endpoint: "0.0.0.0:8889" - const_labels: - label1: value1 - - logging: - - zipkin: - endpoint: "http://zipkin:9411/api/v2/spans" - format: proto - - jaeger: - endpoint: jaeger:14250 - tls: - insecure: true - - elasticsearch: - endpoints: - - "http://elastic_search:9200" - -processors: - batch: - -extensions: - health_check: - pprof: - endpoint: :1888 - zpages: - endpoint: :55679 - -service: - extensions: [pprof, zpages, health_check] - pipelines: - traces: - receivers: [otlp] - processors: [batch] - exporters: [logging, zipkin, jaeger] - metrics: - receivers: [otlp] - processors: [batch] - exporters: [logging, prometheus] - logs: - receivers: [otlp] - processors: [batch] - exporters: [logging, elasticsearch] diff --git a/deployments/docker-compose/otelcollector/otelcol-config-extras.yml b/deployments/docker-compose/otelcollector/otelcol-config-extras.yml new file mode 100644 index 00000000..f557025e --- /dev/null +++ b/deployments/docker-compose/otelcollector/otelcol-config-extras.yml @@ -0,0 +1,4 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 +# extra settings to be merged into OpenTelemetry Collector configuration +# do not delete this file diff --git a/deployments/docker-compose/otelcollector/otelcol-config.yml b/deployments/docker-compose/otelcollector/otelcol-config.yml new file mode 100644 index 00000000..68533ee2 --- /dev/null +++ b/deployments/docker-compose/otelcollector/otelcol-config.yml @@ -0,0 +1,54 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# Ref: https://github.com/open-telemetry/opentelemetry-demo/blob/main/src/otelcollector/otelcol-observability.yml +# https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/otel-collector-config.yaml +# https://www.youtube.com/watch?v=EeU-k659lpw + +receivers: + otlp: + protocols: + grpc: + http: + cors: + allowed_origins: + - "http://*" + - "https://*" + +exporters: + logging: + +processors: + batch: + transform: + metric_statements: + - context: metric + statements: + # FIXME: remove this when this is issue is resolved: https://github.com/open-telemetry/opentelemetry-java/issues/4834 + - set(description, "") where name == "queueSize" + # FIXME: remove this when the following 2 issues are resolved + # Java: https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/9478 + # Go: https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4301 + - set(description, "") where name == "rpc.server.duration" + +connectors: + spanmetrics: + +extensions: + health_check: + pprof: + endpoint: :1888 + zpages: + endpoint: :55679 + +service: + extensions: [pprof, zpages, health_check] + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [logging, zipkin, otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [logging, prometheus] diff --git a/deployments/docker-compose/otelcollector/otelcol-observability.yml b/deployments/docker-compose/otelcollector/otelcol-observability.yml new file mode 100644 index 00000000..32f0b51e --- /dev/null +++ b/deployments/docker-compose/otelcollector/otelcol-observability.yml @@ -0,0 +1,49 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +exporters: + otlp: + endpoint: "jaeger:4317" + tls: + insecure: true + prometheus: + endpoint: "otelcol:9464" + resource_to_telemetry_conversion: + enabled: true + enable_open_metrics: true + + # #https://www.elastic.co/guide/en/apm/guide/current/open-telemetry-direct.html#open-telemetry-proxy-apm + # otlp/elastic: + # # Elastic APM server https endpoint without the "https://" prefix + # endpoint: "http://elastic-apm-server:8200" + # headers: + # # Elastic APM Server secret token + # Authorization: "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + + # # https://uptrace:dev/opentelemetry/prometheus-metrics:html#prometheus-receiver + # otlp/uptrace: + # endpoint: otlp.uptrace.dev:4317 + # headers: + # # Copy your project DSN here + # uptrace-dsn: 'https://@uptrace.dev/ + + + logging: + + zipkin: + endpoint: "http://zipkin-all-in-one:9411/api/v2/spans" + format: proto + + otlp: + endpoint: "jaeger-all-in-one:4317" + tls: + insecure: true + +service: + pipelines: + traces: + exporters: [otlp, logging, spanmetrics] + metrics: + exporters: [prometheus, logging] + diff --git a/deployments/docker-compose/otelcollector/tempo.yaml b/deployments/docker-compose/otelcollector/tempo.yaml new file mode 100644 index 00000000..743ef05d --- /dev/null +++ b/deployments/docker-compose/otelcollector/tempo.yaml @@ -0,0 +1,57 @@ +# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/tempo.yaml + +server: + http_listen_port: 3200 + +query_frontend: + search: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + trace_by_id: + duration_slo: 5s + +distributor: + receivers: # this configuration will listen on all ports and protocols that tempo is capable of. + jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can + protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver + thrift_http: # + grpc: # for a production deployment you should only enable the receivers you need! + thrift_binary: + thrift_compact: + zipkin: + otlp: + protocols: + http: + grpc: + opencensus: + +ingester: + max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally + +compactor: + compaction: + block_retention: 1h # overall Tempo trace retention. set for demo purposes + +metrics_generator: + registry: + external_labels: + source: tempo + cluster: docker-compose + storage: + path: /tmp/tempo/generator/wal + remote_write: + - url: http://prometheus:9090/api/v1/write + send_exemplars: true + +storage: + trace: + backend: local # backend configuration to use + wal: + path: /tmp/tempo/wal # where to store the the wal locally + local: + path: /tmp/tempo/blocks + +overrides: + defaults: + metrics_generator: + processors: [service-graphs, span-metrics] # enables metrics generator diff --git a/deployments/docker-compose/traefik.yaml b/deployments/docker-compose/traefik.yaml index ba35cfd1..9233b9bc 100644 --- a/deployments/docker-compose/traefik.yaml +++ b/deployments/docker-compose/traefik.yaml @@ -141,9 +141,9 @@ - + - + diff --git a/internal/services/catalog_read_service/Dockerfile b/internal/pkg/.env similarity index 100% rename from internal/services/catalog_read_service/Dockerfile rename to internal/pkg/.env diff --git a/internal/pkg/bun/postgres/bun_postgres.go b/internal/pkg/bun/postgres/bun_postgres.go index 81d26438..8996a85c 100644 --- a/internal/pkg/bun/postgres/bun_postgres.go +++ b/internal/pkg/bun/postgres/bun_postgres.go @@ -37,7 +37,7 @@ func NewBunDB(cfg *bun2.BunConfig) (*bun.DB, error) { //pgconn := pgdriver.NewConnector( // pgdriver.WithNetwork("tcp"), // pgdriver.WithAddr("localhost:5437"), - // pgdriver.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}), + // pgdriver.WithTLSConfig(&tls.config{InsecureSkipVerify: true}), // pgdriver.WithUser("test"), // pgdriver.WithPassword("test"), // pgdriver.WithDatabase("test"), diff --git a/internal/services/catalog_write_service/config/config.development.json b/internal/pkg/config.test.json similarity index 71% rename from internal/services/catalog_write_service/config/config.development.json rename to internal/pkg/config.test.json index 1a49c752..c2193758 100644 --- a/internal/services/catalog_write_service/config/config.development.json +++ b/internal/pkg/config.test.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "catalogs_write_service", + "serviceName": "test", "deliveryType": "http" }, "grpcOptions": { - "name": "catalogs_write_service", + "name": "test", "port": ":6003", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "catalogs_write_service", + "name": "test", "port": ":7000", "development": true, "timeout": 30, @@ -48,27 +48,35 @@ "httpPort": 15672 } }, - "openTelemetryOptions": { + "tracingOptions": { "enable": true, - "serviceName": "catalogs-write-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice", + "serviceName": "test", + "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test", "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4317" }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" - }, - "otelMetricsOptions": { - "port": ":3001", - "metricsRoutePath": "/metrics", - "name": "catalogs-write-service" } }, + "metricsOptions": { + "host": "localhost", + "port": ":3001", + "metricsRoutePath": "metrics", + "serviceName": "test", + "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test" + }, "eventStoreDbOptions": { - "connectionString": "esdb://localhost:2113?tls=false" + "host": "localhost", + "httpPort": 2113, + "tcpPort": 1113, + "subscription": { + "subscriptionId": "orders-subscription", + "prefix": ["order-"] + } }, "migrationOptions": { "host": "localhost", diff --git a/internal/pkg/config/config_helper.go b/internal/pkg/config/config_helper.go index 9a83a632..2054bd76 100644 --- a/internal/pkg/config/config_helper.go +++ b/internal/pkg/config/config_helper.go @@ -4,10 +4,11 @@ import ( "fmt" "os" "path/filepath" + "strings" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/caarlos0/env/v8" @@ -15,30 +16,45 @@ import ( "github.com/spf13/viper" ) -func BindConfig[T any](environments ...environemnt.Environment) (T, error) { +func BindConfig[T any](environments ...environment.Environment) (T, error) { return BindConfigKey[T]("", environments...) } -func BindConfigKey[T any](configKey string, environments ...environemnt.Environment) (T, error) { +func BindConfigKey[T any]( + configKey string, + environments ...environment.Environment, +) (T, error) { var configPath string - environment := environemnt.Environment("") + currentEnv := environment.Environment("") if len(environments) > 0 { - environment = environments[0] + currentEnv = environments[0] } else { - environment = constants.Dev + currentEnv = constants.Dev } + cfg := typeMapper.GenericInstanceByT[T]() + + // this should set before reading config values from json file + // https://github.com/mcuadros/go-defaults + defaults.SetDefaults(cfg) + // https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d // when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string - // load `config path` from environment variable or viper internal registry + // load `config path` from env variable or viper internal registry configPathFromEnv := viper.GetString(constants.ConfigPath) + if configPathFromEnv != "" { configPath = configPathFromEnv } else { // https://stackoverflow.com/questions/31873396/is-it-possible-to-get-the-current-root-of-package-structure-as-a-string-in-golan // https://stackoverflow.com/questions/18537257/how-to-get-the-directory-of-the-currently-running-file - d, err := getConfigRootPath() + appRootPath := viper.GetString(constants.AppRootPath) + if appRootPath == "" { + appRootPath = environment.GetProjectRootWorkingDirectory() + } + + d, err := searchForConfigFileDir(appRootPath, currentEnv) if err != nil { return *new(T), err } @@ -46,14 +62,8 @@ func BindConfigKey[T any](configKey string, environments ...environemnt.Environm configPath = d } - cfg := typeMapper.GenericInstanceByT[T]() - - // this should set before reading config values from json file - // https://github.com/mcuadros/go-defaults - defaults.SetDefaults(cfg) - // https://github.com/spf13/viper/issues/390#issuecomment-718756752 - viper.SetConfigName(fmt.Sprintf("config.%s", environment)) + viper.SetConfigName(fmt.Sprintf("config.%s", currentEnv)) viper.AddConfigPath(configPath) viper.SetConfigType(constants.Json) @@ -62,6 +72,7 @@ func BindConfigKey[T any](configKey string, environments ...environemnt.Environm } if len(configKey) == 0 { + // load configs from config file to config object if err := viper.Unmarshal(cfg); err != nil { return *new(T), errors.WrapIf(err, "viper.Unmarshal") } @@ -81,23 +92,61 @@ func BindConfigKey[T any](configKey string, environments ...environemnt.Environm return cfg, nil } -func getConfigRootPath() (string, error) { - // Get the current working directory - // Getwd gives us the current working directory that we are running our app with `go run` command. in goland we can specify this working directory for the project - wd, err := os.Getwd() - if err != nil { - return "", err - } - fmt.Println(fmt.Sprintf("Current working directory is: %s", wd)) - - // Get the absolute path of the executed project directory - absCurrentDir, err := filepath.Abs(wd) - if err != nil { - return "", err +// searchForConfigFileDir searches for the first directory within the specified root directory and its subdirectories +// that contains a file named "config.%s.json" where "%s" is replaced with the provided environment string. +// It returns the path of the first directory that contains the config file or an error if no such directory is found. +// +// Parameters: +// +// rootDir: The root directory to start the search from. +// environment: The environment string to replace "%s" in the config file name. +// +// Returns: +// +// string: The path of the directory containing the config file, or an empty string if not found. +// error: An error indicating any issues encountered during the search. +func searchForConfigFileDir( + rootDir string, + env environment.Environment, +) (string, error) { + var result string + + // Walk the directory tree starting from rootDir + err := filepath.Walk( + rootDir, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Check if the file is named "config.%s.json" (replace %s with the env) + if !info.IsDir() && + strings.EqualFold( + info.Name(), + fmt.Sprintf("config.%s.json", env), + ) || + strings.EqualFold( + info.Name(), + fmt.Sprintf("config.%s.yaml", env), + ) || + strings.EqualFold( + info.Name(), + fmt.Sprintf("config.%s.yml", env), + ) { + // Get the directory name containing the config file + dir := filepath.Dir(path) + result = dir + + return filepath.SkipDir // Skip further traversal + } + + return nil + }, + ) + + if result != "" { + return result, nil } - // Get the path to the "config" folder within the project directory - configPath := filepath.Join(absCurrentDir, "config") - - return configPath, nil + return "", errors.WrapIf(err, "No directory with config file found") } diff --git a/internal/pkg/config/configfx.go b/internal/pkg/config/configfx.go index b17ea753..d4b1eddd 100644 --- a/internal/pkg/config/configfx.go +++ b/internal/pkg/config/configfx.go @@ -1,7 +1,7 @@ package config import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "go.uber.org/fx" ) @@ -10,16 +10,16 @@ import ( // https://uber-go.github.io/fx/modules.html var Module = fx.Module( "configfx", - fx.Provide(func() environemnt.Environment { - return environemnt.ConfigAppEnv() + fx.Provide(func() environment.Environment { + return environment.ConfigAppEnv() }), ) -var ModuleFunc = func(e environemnt.Environment) fx.Option { +var ModuleFunc = func(e environment.Environment) fx.Option { return fx.Module( "configfx", - fx.Provide(func() environemnt.Environment { - return environemnt.ConfigAppEnv(e) + fx.Provide(func() environment.Environment { + return environment.ConfigAppEnv(e) }), ) } diff --git a/internal/pkg/config/environemnt/environment.go b/internal/pkg/config/environment/environment.go similarity index 63% rename from internal/pkg/config/environemnt/environment.go rename to internal/pkg/config/environment/environment.go index 651d1f1d..f48eaf64 100644 --- a/internal/pkg/config/environemnt/environment.go +++ b/internal/pkg/config/environment/environment.go @@ -1,10 +1,9 @@ -package environemnt +package environment import ( "log" "os" "path/filepath" - "strings" "syscall" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" @@ -34,7 +33,7 @@ func ConfigAppEnv(environments ...Environment) Environment { viper.AutomaticEnv() // https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d - // load environment variables form .env files to system environment variables, it just find `.env` file in our current `executing working directory` in our app for example `catalogs_service` + // load environment variables form .env files to system environment variables, it just finds `.env` file in our current `executing working directory` in our app for example `catalogs_service` err := loadEnvFilesRecursive() if err != nil { log.Printf(".env file cannot be found, err: %v", err) @@ -42,6 +41,8 @@ func ConfigAppEnv(environments ...Environment) Environment { setRootWorkingDirectoryEnvironment() + FixProjectRootWorkingDirectoryPath() + manualEnv := os.Getenv(constants.AppEnv) if manualEnv != "" { @@ -59,6 +60,10 @@ func (env Environment) IsProduction() bool { return env == Production } +func (env Environment) IsTest() bool { + return env == Test +} + func (env Environment) GetEnvironmentName() string { return string(env) } @@ -67,6 +72,7 @@ func EnvString(key, fallback string) string { if value, ok := syscall.Getenv(key); ok { return value } + return fallback } @@ -101,37 +107,8 @@ func loadEnvFilesRecursive() error { } func setRootWorkingDirectoryEnvironment() { - // https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d - // when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string - // viper will get it from `os env` or a .env file - pn := viper.GetString(constants.PROJECT_NAME_ENV) - if pn == "" { - log.Fatalf( - "can't find environment variable `%s` in the system environment variables or a .env file", - constants.PROJECT_NAME_ENV, - ) - } - - // set root working directory of our app in the viper - // https://stackoverflow.com/a/47785436/581476 - wd, _ := os.Getwd() - - for !strings.HasSuffix(wd, pn) { - wd = filepath.Dir(wd) - } - - absoluteRootWorkingDirectory, _ := filepath.Abs(wd) + absoluteRootWorkingDirectory := GetProjectRootWorkingDirectory() // when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string viper.Set(constants.AppRootPath, absoluteRootWorkingDirectory) - - configPath := viper.GetString(constants.ConfigPath) - - // check for existing `CONFIG_PATH` variable in system environment variables - we can set it directly in .env file or system environments - if configPath == "" { - configPath := filepath.Join(absoluteRootWorkingDirectory, "config") - - // when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string - viper.Set(constants.ConfigPath, configPath) - } } diff --git a/internal/pkg/config/environment/helpers.go b/internal/pkg/config/environment/helpers.go new file mode 100644 index 00000000..1af013b9 --- /dev/null +++ b/internal/pkg/config/environment/helpers.go @@ -0,0 +1,90 @@ +package environment + +import ( + "log" + "os" + "path/filepath" + "strings" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" + + "emperror.dev/errors" + "github.com/spf13/viper" +) + +func FixProjectRootWorkingDirectoryPath() { + currentWD, _ := os.Getwd() + log.Printf("Current working directory is: `%s`", currentWD) + + rootDir := GetProjectRootWorkingDirectory() + // change working directory + _ = os.Chdir(rootDir) + newWD, _ := os.Getwd() + + log.Printf("New fixed working directory is: `%s`", newWD) +} + +func GetProjectRootWorkingDirectory() string { + var rootWorkingDirectory string + // https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d + // when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string + // viper will get it from `os env` or a .env file + pn := viper.GetString(constants.PROJECT_NAME_ENV) + if pn != "" { + rootWorkingDirectory = getProjectRootDirectoryFromProjectName(pn) + } else { + wd, _ := os.Getwd() + dir, err := searchRootDirectory(wd) + if err != nil { + log.Fatal(err) + } + rootWorkingDirectory = dir + } + + absoluteRootWorkingDirectory, _ := filepath.Abs(rootWorkingDirectory) + + return absoluteRootWorkingDirectory +} + +func getProjectRootDirectoryFromProjectName(pn string) string { + // set root working directory of our app in the viper + // https://stackoverflow.com/a/47785436/581476 + wd, _ := os.Getwd() + + for !strings.HasSuffix(wd, pn) { + wd = filepath.Dir(wd) + } + + return wd +} + +func searchRootDirectory( + dir string, +) (string, error) { + // List files and directories in the current directory + files, err := os.ReadDir(dir) + if err != nil { + return "", errors.WrapIf(err, "Error reading directory") + } + + for _, file := range files { + if !file.IsDir() { + fileName := file.Name() + if strings.EqualFold( + fileName, + "go.mod", + ) { + return dir, nil + } + } + } + + // If no config file found in this directory, recursively search its parent + parentDir := filepath.Dir(dir) + if parentDir == dir { + // We've reached the root directory, and no go.mod file was found + return "", errors.WrapIf(err, "No go.mod file found") + } + + return searchRootDirectory(parentDir) +} diff --git a/internal/pkg/core/core_fx.go b/internal/pkg/core/core_fx.go index e39f7242..fd3df78a 100644 --- a/internal/pkg/core/core_fx.go +++ b/internal/pkg/core/core_fx.go @@ -1,9 +1,7 @@ package core import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" "go.uber.org/fx" ) @@ -13,9 +11,9 @@ import ( var Module = fx.Module( "corefx", fx.Provide( - json.NewDefaultSerializer, - serializer.NewDefaultEventSerializer, - serializer.NewDefaultMetadataSerializer, + json.NewDefaultJsonSerializer, + json.NewDefaultEventJsonSerializer, + json.NewDefaultMessageJsonSerializer, + json.NewDefaultMetadataJsonSerializer, ), - fx.Invoke(defaultLogger.SetupDefaultLogger), ) diff --git a/internal/pkg/core/cqrs/command.go b/internal/pkg/core/cqrs/command.go new file mode 100644 index 00000000..cbb90905 --- /dev/null +++ b/internal/pkg/core/cqrs/command.go @@ -0,0 +1,33 @@ +package cqrs + +type command struct { + TypeInfo + Request +} + +type Command interface { + isCommand() + + Request + TypeInfo +} + +func NewCommandByT[T any]() Command { + c := &command{ + TypeInfo: NewTypeInfoT[T](), + Request: NewRequest(), + } + + return c +} + +func (c *command) isCommand() { +} + +func IsCommand(obj interface{}) bool { + if _, ok := obj.(Command); ok { + return true + } + + return false +} diff --git a/internal/pkg/core/cqrs/command_handler.go b/internal/pkg/core/cqrs/command_handler.go new file mode 100644 index 00000000..d0b815e1 --- /dev/null +++ b/internal/pkg/core/cqrs/command_handler.go @@ -0,0 +1,15 @@ +package cqrs + +import ( + "context" + + "github.com/mehdihadeli/go-mediatr" +) + +type CommandHandler[TCommand Command, TResponse any] interface { + Handle(ctx context.Context, command TCommand) (TResponse, error) +} + +type CommandHandlerVoid[TCommand Command] interface { + Handle(ctx context.Context, command TCommand) (*mediatr.Unit, error) +} diff --git a/internal/pkg/core/cqrs/command_test.go b/internal/pkg/core/cqrs/command_test.go new file mode 100644 index 00000000..32cb7cf7 --- /dev/null +++ b/internal/pkg/core/cqrs/command_test.go @@ -0,0 +1,54 @@ +package cqrs + +import ( + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/brianvoe/gofakeit/v6" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/assert" +) + +func Test_Command(t *testing.T) { + command := &CreateProductTest{ + Command: NewCommandByT[*CreateProductTest](), + ProductID: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + } + + isImplementedCommand := typemapper.ImplementedInterfaceT[Command](command) + assert.True(t, isImplementedCommand) + + var i interface{} = command + _, isCommand := i.(Command) + _, isTypeInfo := i.(TypeInfo) + _, isQuery := i.(Query) + _, isRequest := i.(Request) + + assert.True(t, isCommand) + assert.True(t, isTypeInfo) + assert.True(t, isRequest) + assert.False(t, isQuery) + + assert.True(t, IsCommand(command)) + assert.True(t, IsRequest(command)) + assert.False(t, IsQuery(command)) + + assert.Equal(t, command.ShortTypeName(), "*CreateProductTest") + assert.Equal(t, command.FullTypeName(), "*cqrs.CreateProductTest") +} + +type CreateProductTest struct { + Command + + Name string + ProductID uuid.UUID + Description string + Price float64 + CreatedAt time.Time +} diff --git a/internal/pkg/core/cqrs/handler.go b/internal/pkg/core/cqrs/handler.go new file mode 100644 index 00000000..39ee1a7e --- /dev/null +++ b/internal/pkg/core/cqrs/handler.go @@ -0,0 +1,14 @@ +package cqrs + +import "github.com/mehdihadeli/go-mediatr" + +// HandlerRegisterer for registering `RequestHandler` to mediatr registry, if handler implements this interface it will be registered automatically +type HandlerRegisterer interface { + RegisterHandler() error +} + +// RequestHandlerWithRegisterer for registering `RequestHandler` to mediatr registry and handling `RequestHandler` +type RequestHandlerWithRegisterer[TRequest any, TResponse any] interface { + HandlerRegisterer + mediatr.RequestHandler[TRequest, TResponse] +} diff --git a/internal/pkg/core/cqrs/helpers.go b/internal/pkg/core/cqrs/helpers.go new file mode 100644 index 00000000..2dbef123 --- /dev/null +++ b/internal/pkg/core/cqrs/helpers.go @@ -0,0 +1,22 @@ +package cqrs + +import ( + "fmt" + + "go.uber.org/fx" +) + +// when we register multiple handlers with output type `mediatr.RequestHandler` we get exception `type already provided`, so we should use tags annotation + +// AsHandler annotates the given constructor to state that +// it provides a handler to the "handlers" group. +func AsHandler(handler interface{}, handlerGroupName string) interface{} { + return fx.Annotate( + handler, + fx.As(new(HandlerRegisterer)), + fx.ResultTags(fmt.Sprintf( + `group:"%s"`, + handlerGroupName, + )), + ) +} diff --git a/internal/pkg/core/cqrs/internal_command.go b/internal/pkg/core/cqrs/internal_command.go new file mode 100644 index 00000000..adaf32ee --- /dev/null +++ b/internal/pkg/core/cqrs/internal_command.go @@ -0,0 +1,25 @@ +package cqrs + +type internalCommand struct { + Command +} + +type InternalCommand interface { + Command + isInternalCommand() +} + +func NewInternalCommandByT[T any]() InternalCommand { + return &internalCommand{Command: NewCommandByT[T]()} +} + +func (c *internalCommand) isInternalCommand() { +} + +func IsInternalCommand(obj interface{}) bool { + if _, ok := obj.(InternalCommand); ok { + return true + } + + return false +} diff --git a/internal/pkg/core/cqrs/internal_command_test.go b/internal/pkg/core/cqrs/internal_command_test.go new file mode 100644 index 00000000..e9729f96 --- /dev/null +++ b/internal/pkg/core/cqrs/internal_command_test.go @@ -0,0 +1 @@ +package cqrs diff --git a/internal/pkg/core/cqrs/notification.go b/internal/pkg/core/cqrs/notification.go new file mode 100644 index 00000000..974c03fa --- /dev/null +++ b/internal/pkg/core/cqrs/notification.go @@ -0,0 +1,30 @@ +package cqrs + +type notification struct { + TypeInfo +} + +type Notification interface { + isNotification() + + TypeInfo +} + +func NewNotificationByT[T any]() Notification { + n := ¬ification{ + TypeInfo: NewTypeInfoT[T](), + } + + return n +} + +func (c *notification) isNotification() { +} + +func IsNotification(obj interface{}) bool { + if _, ok := obj.(Notification); ok { + return true + } + + return false +} diff --git a/internal/pkg/core/cqrs/query.go b/internal/pkg/core/cqrs/query.go new file mode 100644 index 00000000..7957d848 --- /dev/null +++ b/internal/pkg/core/cqrs/query.go @@ -0,0 +1,31 @@ +package cqrs + +type query struct { + TypeInfo + Request +} + +type Query interface { + isQuery() + + Request + TypeInfo +} + +func NewQueryByT[T any]() Query { + return &query{ + TypeInfo: NewTypeInfoT[T](), + Request: NewRequest(), + } +} + +func (q *query) isQuery() { +} + +func IsQuery(obj interface{}) bool { + if _, ok := obj.(Query); ok { + return true + } + + return false +} diff --git a/internal/pkg/core/cqrs/query_handler.go b/internal/pkg/core/cqrs/query_handler.go new file mode 100644 index 00000000..818c423e --- /dev/null +++ b/internal/pkg/core/cqrs/query_handler.go @@ -0,0 +1,9 @@ +package cqrs + +import ( + "context" +) + +type QueryHandler[TQuery Query, TResponse any] interface { + Handle(ctx context.Context, query TQuery) (TResponse, error) +} diff --git a/internal/pkg/core/cqrs/query_test.go b/internal/pkg/core/cqrs/query_test.go new file mode 100644 index 00000000..63120450 --- /dev/null +++ b/internal/pkg/core/cqrs/query_test.go @@ -0,0 +1,44 @@ +package cqrs + +import ( + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/assert" +) + +func Test_Query(t *testing.T) { + query := &GetProductById{ + Query: NewQueryByT[*GetProductById](), + ProductID: uuid.NewV4(), + } + + isImplementedQuery := typemapper.ImplementedInterfaceT[Query](query) + assert.True(t, isImplementedQuery) + + var i interface{} = query + _, isQuery := i.(Query) + _, isTypeInfo := i.(TypeInfo) + _, isCommand := i.(Command) + _, isRequest := i.(Request) + + assert.True(t, isQuery) + assert.False(t, isCommand) + assert.True(t, isTypeInfo) + assert.True(t, isRequest) + + assert.True(t, IsQuery(query)) + assert.False(t, IsCommand(query)) + assert.True(t, IsRequest(query)) + + assert.Equal(t, query.ShortTypeName(), "*GetProductById") + assert.Equal(t, query.FullTypeName(), "*cqrs.GetProductById") +} + +type GetProductById struct { + Query + + ProductID uuid.UUID +} diff --git a/internal/pkg/core/cqrs/request.go b/internal/pkg/core/cqrs/request.go new file mode 100644 index 00000000..bed4bdc5 --- /dev/null +++ b/internal/pkg/core/cqrs/request.go @@ -0,0 +1,22 @@ +package cqrs + +type request struct{} + +type Request interface { + isRequest() +} + +func NewRequest() Request { + return &request{} +} + +func (r *request) isRequest() { +} + +func IsRequest(obj interface{}) bool { + if _, ok := obj.(Request); ok { + return true + } + + return false +} diff --git a/internal/pkg/core/cqrs/tx_request.go b/internal/pkg/core/cqrs/tx_request.go new file mode 100644 index 00000000..68622321 --- /dev/null +++ b/internal/pkg/core/cqrs/tx_request.go @@ -0,0 +1,9 @@ +package cqrs + +// https://www.mohitkhare.com/blog/go-naming-conventions/ + +type TxRequest interface { + Request + + isTxRequest() bool +} diff --git a/internal/pkg/core/cqrs/type_info.go b/internal/pkg/core/cqrs/type_info.go new file mode 100644 index 00000000..273aa7cc --- /dev/null +++ b/internal/pkg/core/cqrs/type_info.go @@ -0,0 +1,39 @@ +package cqrs + +import ( + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" +) + +type TypeInfo interface { + ShortTypeName() string + FullTypeName() string + Type() reflect.Type +} + +type typeInfo struct { + shortTypeName string + fullTypeName string + typ reflect.Type +} + +func NewTypeInfoT[T any]() TypeInfo { + name := typemapper.GetGenericTypeNameByT[T]() + fullName := typemapper.GetGenericFullTypeNameByT[T]() + typ := typemapper.GetGenericTypeByT[T]() + + return &typeInfo{fullTypeName: fullName, typ: typ, shortTypeName: name} +} + +func (t *typeInfo) ShortTypeName() string { + return t.shortTypeName +} + +func (t *typeInfo) FullTypeName() string { + return t.fullTypeName +} + +func (t *typeInfo) Type() reflect.Type { + return t.typ +} diff --git a/internal/pkg/core/custom_types/custom_time.go b/internal/pkg/core/customtypes/custom_time.go similarity index 96% rename from internal/pkg/core/custom_types/custom_time.go rename to internal/pkg/core/customtypes/custom_time.go index 57791650..1e03d73d 100644 --- a/internal/pkg/core/custom_types/custom_time.go +++ b/internal/pkg/core/customtypes/custom_time.go @@ -8,7 +8,7 @@ import ( "strings" "time" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/araddon/dateparse" ) diff --git a/internal/pkg/core/events/event.go b/internal/pkg/core/events/event.go index e97e19d9..a8f12405 100644 --- a/internal/pkg/core/events/event.go +++ b/internal/pkg/core/events/event.go @@ -3,13 +3,17 @@ package events import ( "time" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + uuid "github.com/satori/go.uuid" ) type IEvent interface { GetEventId() uuid.UUID - GetEventType() string GetOccurredOn() time.Time + // GetEventTypeName get short type name of the event - we use event short type name instead of full type name because this event in other receiver packages could have different package name + GetEventTypeName() string + GetEventFullTypeName() string } type Event struct { @@ -37,3 +41,11 @@ func (e *Event) GetEventType() string { func (e *Event) GetOccurredOn() time.Time { return e.OccurredOn } + +func (e *Event) GetEventTypeName() string { + return typeMapper.GetTypeName(e) +} + +func (e *Event) GetEventFullTypeName() string { + return typeMapper.GetFullTypeName(e) +} diff --git a/internal/pkg/core/messaging/bus/bus.go b/internal/pkg/core/messaging/bus/bus.go new file mode 100644 index 00000000..1539fd36 --- /dev/null +++ b/internal/pkg/core/messaging/bus/bus.go @@ -0,0 +1,12 @@ +package bus + +import ( + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" +) + +type Bus interface { + producer.Producer + consumer2.BusControl + consumer2.ConsumerConnector +} diff --git a/internal/pkg/messaging/consumer/consumer.go b/internal/pkg/core/messaging/consumer/consumer.go similarity index 92% rename from internal/pkg/messaging/consumer/consumer.go rename to internal/pkg/core/messaging/consumer/consumer.go index 22ca37d1..94cfde4f 100644 --- a/internal/pkg/messaging/consumer/consumer.go +++ b/internal/pkg/core/messaging/consumer/consumer.go @@ -3,7 +3,7 @@ package consumer import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type Consumer interface { diff --git a/internal/pkg/messaging/consumer/consumer_builder.go b/internal/pkg/core/messaging/consumer/consumer_builder.go similarity index 100% rename from internal/pkg/messaging/consumer/consumer_builder.go rename to internal/pkg/core/messaging/consumer/consumer_builder.go diff --git a/internal/pkg/messaging/consumer/consumer_connector.go b/internal/pkg/core/messaging/consumer/consumer_connector.go similarity index 95% rename from internal/pkg/messaging/consumer/consumer_connector.go rename to internal/pkg/core/messaging/consumer/consumer_connector.go index 289d6d31..e36f491c 100644 --- a/internal/pkg/messaging/consumer/consumer_connector.go +++ b/internal/pkg/core/messaging/consumer/consumer_connector.go @@ -1,7 +1,7 @@ package consumer import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type ConsumerConnector interface { diff --git a/internal/pkg/messaging/consumer/consumer_handler.go b/internal/pkg/core/messaging/consumer/consumer_handler.go similarity index 90% rename from internal/pkg/messaging/consumer/consumer_handler.go rename to internal/pkg/core/messaging/consumer/consumer_handler.go index 677c527e..dc5b7ce2 100644 --- a/internal/pkg/messaging/consumer/consumer_handler.go +++ b/internal/pkg/core/messaging/consumer/consumer_handler.go @@ -3,7 +3,7 @@ package consumer import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type ConsumerHandler interface { diff --git a/internal/pkg/messaging/consumer/consumer_handler_configuration.go b/internal/pkg/core/messaging/consumer/consumer_handler_configuration.go similarity index 100% rename from internal/pkg/messaging/consumer/consumer_handler_configuration.go rename to internal/pkg/core/messaging/consumer/consumer_handler_configuration.go diff --git a/internal/pkg/messaging/consumer/consumer_handler_configuration_builder.go b/internal/pkg/core/messaging/consumer/consumer_handler_configuration_builder.go similarity index 100% rename from internal/pkg/messaging/consumer/consumer_handler_configuration_builder.go rename to internal/pkg/core/messaging/consumer/consumer_handler_configuration_builder.go diff --git a/internal/pkg/messaging/consumer/consumer_options.go b/internal/pkg/core/messaging/consumer/consumer_options.go similarity index 100% rename from internal/pkg/messaging/consumer/consumer_options.go rename to internal/pkg/core/messaging/consumer/consumer_options.go diff --git a/internal/pkg/messaging/consumer/consumers_control.go b/internal/pkg/core/messaging/consumer/consumers_control.go similarity index 92% rename from internal/pkg/messaging/consumer/consumers_control.go rename to internal/pkg/core/messaging/consumer/consumers_control.go index 2dff9c77..d539e1f7 100644 --- a/internal/pkg/messaging/consumer/consumers_control.go +++ b/internal/pkg/core/messaging/consumer/consumers_control.go @@ -3,7 +3,7 @@ package consumer import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type BusControl interface { diff --git a/internal/pkg/messaging/message_header/message_header.go b/internal/pkg/core/messaging/messageheader/message_header.go similarity index 100% rename from internal/pkg/messaging/message_header/message_header.go rename to internal/pkg/core/messaging/messageheader/message_header.go diff --git a/internal/pkg/messaging/message_header/metadata_message_extentions.go b/internal/pkg/core/messaging/messageheader/metadata_message_extentions.go similarity index 100% rename from internal/pkg/messaging/message_header/metadata_message_extentions.go rename to internal/pkg/core/messaging/messageheader/metadata_message_extentions.go diff --git a/internal/pkg/messaging/mocks/Bus.go b/internal/pkg/core/messaging/mocks/Bus.go similarity index 92% rename from internal/pkg/messaging/mocks/Bus.go rename to internal/pkg/core/messaging/mocks/Bus.go index 3e1bd32e..59b746e6 100644 --- a/internal/pkg/messaging/mocks/Bus.go +++ b/internal/pkg/core/messaging/mocks/Bus.go @@ -5,13 +5,11 @@ package mocks import ( context "context" - consumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" metadata "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" mock "github.com/stretchr/testify/mock" - - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" ) // Bus is an autogenerated mock type for the Bus type @@ -94,11 +92,11 @@ func (_c *Bus_AddMessageProducedHandler_Call) RunAndReturn(run func(func(types.I } // ConnectConsumer provides a mock function with given fields: messageType, _a1 -func (_m *Bus) ConnectConsumer(messageType types.IMessage, _a1 consumer.Consumer) error { +func (_m *Bus) ConnectConsumer(messageType types.IMessage, _a1 consumer2.Consumer) error { ret := _m.Called(messageType, _a1) var r0 error - if rf, ok := ret.Get(0).(func(types.IMessage, consumer.Consumer) error); ok { + if rf, ok := ret.Get(0).(func(types.IMessage, consumer2.Consumer) error); ok { r0 = rf(messageType, _a1) } else { r0 = ret.Error(0) @@ -119,9 +117,9 @@ func (_e *Bus_Expecter) ConnectConsumer(messageType interface{}, _a1 interface{} return &Bus_ConnectConsumer_Call{Call: _e.mock.On("ConnectConsumer", messageType, _a1)} } -func (_c *Bus_ConnectConsumer_Call) Run(run func(messageType types.IMessage, _a1 consumer.Consumer)) *Bus_ConnectConsumer_Call { +func (_c *Bus_ConnectConsumer_Call) Run(run func(messageType types.IMessage, _a1 consumer2.Consumer)) *Bus_ConnectConsumer_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.IMessage), args[1].(consumer.Consumer)) + run(args[0].(types.IMessage), args[1].(consumer2.Consumer)) }) return _c } @@ -131,17 +129,17 @@ func (_c *Bus_ConnectConsumer_Call) Return(_a0 error) *Bus_ConnectConsumer_Call return _c } -func (_c *Bus_ConnectConsumer_Call) RunAndReturn(run func(types.IMessage, consumer.Consumer) error) *Bus_ConnectConsumer_Call { +func (_c *Bus_ConnectConsumer_Call) RunAndReturn(run func(types.IMessage, consumer2.Consumer) error) *Bus_ConnectConsumer_Call { _c.Call.Return(run) return _c } // ConnectConsumerHandler provides a mock function with given fields: messageType, consumerHandler -func (_m *Bus) ConnectConsumerHandler(messageType types.IMessage, consumerHandler consumer.ConsumerHandler) error { +func (_m *Bus) ConnectConsumerHandler(messageType types.IMessage, consumerHandler consumer2.ConsumerHandler) error { ret := _m.Called(messageType, consumerHandler) var r0 error - if rf, ok := ret.Get(0).(func(types.IMessage, consumer.ConsumerHandler) error); ok { + if rf, ok := ret.Get(0).(func(types.IMessage, consumer2.ConsumerHandler) error); ok { r0 = rf(messageType, consumerHandler) } else { r0 = ret.Error(0) @@ -162,9 +160,9 @@ func (_e *Bus_Expecter) ConnectConsumerHandler(messageType interface{}, consumer return &Bus_ConnectConsumerHandler_Call{Call: _e.mock.On("ConnectConsumerHandler", messageType, consumerHandler)} } -func (_c *Bus_ConnectConsumerHandler_Call) Run(run func(messageType types.IMessage, consumerHandler consumer.ConsumerHandler)) *Bus_ConnectConsumerHandler_Call { +func (_c *Bus_ConnectConsumerHandler_Call) Run(run func(messageType types.IMessage, consumerHandler consumer2.ConsumerHandler)) *Bus_ConnectConsumerHandler_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.IMessage), args[1].(consumer.ConsumerHandler)) + run(args[0].(types.IMessage), args[1].(consumer2.ConsumerHandler)) }) return _c } @@ -174,7 +172,7 @@ func (_c *Bus_ConnectConsumerHandler_Call) Return(_a0 error) *Bus_ConnectConsume return _c } -func (_c *Bus_ConnectConsumerHandler_Call) RunAndReturn(run func(types.IMessage, consumer.ConsumerHandler) error) *Bus_ConnectConsumerHandler_Call { +func (_c *Bus_ConnectConsumerHandler_Call) RunAndReturn(run func(types.IMessage, consumer2.ConsumerHandler) error) *Bus_ConnectConsumerHandler_Call { _c.Call.Return(run) return _c } @@ -356,7 +354,8 @@ func (_c *Bus_Stop_Call) RunAndReturn(run func() error) *Bus_Stop_Call { func NewBus(t interface { mock.TestingT Cleanup(func()) -}) *Bus { +}, +) *Bus { mock := &Bus{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/BusControl.go b/internal/pkg/core/messaging/mocks/BusControl.go similarity index 97% rename from internal/pkg/messaging/mocks/BusControl.go rename to internal/pkg/core/messaging/mocks/BusControl.go index cbe10475..bbd1337e 100644 --- a/internal/pkg/messaging/mocks/BusControl.go +++ b/internal/pkg/core/messaging/mocks/BusControl.go @@ -5,7 +5,8 @@ package mocks import ( context "context" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + mock "github.com/stretchr/testify/mock" ) @@ -143,7 +144,8 @@ func (_c *BusControl_Stop_Call) RunAndReturn(run func() error) *BusControl_Stop_ func NewBusControl(t interface { mock.TestingT Cleanup(func()) -}) *BusControl { +}, +) *BusControl { mock := &BusControl{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/Consumer.go b/internal/pkg/core/messaging/mocks/Consumer.go similarity index 96% rename from internal/pkg/messaging/mocks/Consumer.go rename to internal/pkg/core/messaging/mocks/Consumer.go index 2f4c8243..e076757a 100644 --- a/internal/pkg/messaging/mocks/Consumer.go +++ b/internal/pkg/core/messaging/mocks/Consumer.go @@ -5,11 +5,10 @@ package mocks import ( context "context" - consumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" mock "github.com/stretchr/testify/mock" - - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" ) // Consumer is an autogenerated mock type for the Consumer type @@ -179,7 +178,8 @@ func (_c *Consumer_Stop_Call) RunAndReturn(run func() error) *Consumer_Stop_Call func NewConsumer(t interface { mock.TestingT Cleanup(func()) -}) *Consumer { +}, +) *Consumer { mock := &Consumer{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerBuilderFucT.go b/internal/pkg/core/messaging/mocks/ConsumerBuilderFucT.go similarity index 100% rename from internal/pkg/messaging/mocks/ConsumerBuilderFucT.go rename to internal/pkg/core/messaging/mocks/ConsumerBuilderFucT.go diff --git a/internal/pkg/messaging/mocks/ConsumerConnector.go b/internal/pkg/core/messaging/mocks/ConsumerConnector.go similarity index 77% rename from internal/pkg/messaging/mocks/ConsumerConnector.go rename to internal/pkg/core/messaging/mocks/ConsumerConnector.go index edd3e55d..5746d453 100644 --- a/internal/pkg/messaging/mocks/ConsumerConnector.go +++ b/internal/pkg/core/messaging/mocks/ConsumerConnector.go @@ -3,10 +3,10 @@ package mocks import ( - consumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - mock "github.com/stretchr/testify/mock" + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + mock "github.com/stretchr/testify/mock" ) // ConsumerConnector is an autogenerated mock type for the ConsumerConnector type @@ -23,11 +23,11 @@ func (_m *ConsumerConnector) EXPECT() *ConsumerConnector_Expecter { } // ConnectConsumer provides a mock function with given fields: messageType, _a1 -func (_m *ConsumerConnector) ConnectConsumer(messageType types.IMessage, _a1 consumer.Consumer) error { +func (_m *ConsumerConnector) ConnectConsumer(messageType types.IMessage, _a1 consumer2.Consumer) error { ret := _m.Called(messageType, _a1) var r0 error - if rf, ok := ret.Get(0).(func(types.IMessage, consumer.Consumer) error); ok { + if rf, ok := ret.Get(0).(func(types.IMessage, consumer2.Consumer) error); ok { r0 = rf(messageType, _a1) } else { r0 = ret.Error(0) @@ -48,9 +48,9 @@ func (_e *ConsumerConnector_Expecter) ConnectConsumer(messageType interface{}, _ return &ConsumerConnector_ConnectConsumer_Call{Call: _e.mock.On("ConnectConsumer", messageType, _a1)} } -func (_c *ConsumerConnector_ConnectConsumer_Call) Run(run func(messageType types.IMessage, _a1 consumer.Consumer)) *ConsumerConnector_ConnectConsumer_Call { +func (_c *ConsumerConnector_ConnectConsumer_Call) Run(run func(messageType types.IMessage, _a1 consumer2.Consumer)) *ConsumerConnector_ConnectConsumer_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.IMessage), args[1].(consumer.Consumer)) + run(args[0].(types.IMessage), args[1].(consumer2.Consumer)) }) return _c } @@ -60,17 +60,17 @@ func (_c *ConsumerConnector_ConnectConsumer_Call) Return(_a0 error) *ConsumerCon return _c } -func (_c *ConsumerConnector_ConnectConsumer_Call) RunAndReturn(run func(types.IMessage, consumer.Consumer) error) *ConsumerConnector_ConnectConsumer_Call { +func (_c *ConsumerConnector_ConnectConsumer_Call) RunAndReturn(run func(types.IMessage, consumer2.Consumer) error) *ConsumerConnector_ConnectConsumer_Call { _c.Call.Return(run) return _c } // ConnectConsumerHandler provides a mock function with given fields: messageType, consumerHandler -func (_m *ConsumerConnector) ConnectConsumerHandler(messageType types.IMessage, consumerHandler consumer.ConsumerHandler) error { +func (_m *ConsumerConnector) ConnectConsumerHandler(messageType types.IMessage, consumerHandler consumer2.ConsumerHandler) error { ret := _m.Called(messageType, consumerHandler) var r0 error - if rf, ok := ret.Get(0).(func(types.IMessage, consumer.ConsumerHandler) error); ok { + if rf, ok := ret.Get(0).(func(types.IMessage, consumer2.ConsumerHandler) error); ok { r0 = rf(messageType, consumerHandler) } else { r0 = ret.Error(0) @@ -91,9 +91,9 @@ func (_e *ConsumerConnector_Expecter) ConnectConsumerHandler(messageType interfa return &ConsumerConnector_ConnectConsumerHandler_Call{Call: _e.mock.On("ConnectConsumerHandler", messageType, consumerHandler)} } -func (_c *ConsumerConnector_ConnectConsumerHandler_Call) Run(run func(messageType types.IMessage, consumerHandler consumer.ConsumerHandler)) *ConsumerConnector_ConnectConsumerHandler_Call { +func (_c *ConsumerConnector_ConnectConsumerHandler_Call) Run(run func(messageType types.IMessage, consumerHandler consumer2.ConsumerHandler)) *ConsumerConnector_ConnectConsumerHandler_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(types.IMessage), args[1].(consumer.ConsumerHandler)) + run(args[0].(types.IMessage), args[1].(consumer2.ConsumerHandler)) }) return _c } @@ -103,7 +103,7 @@ func (_c *ConsumerConnector_ConnectConsumerHandler_Call) Return(_a0 error) *Cons return _c } -func (_c *ConsumerConnector_ConnectConsumerHandler_Call) RunAndReturn(run func(types.IMessage, consumer.ConsumerHandler) error) *ConsumerConnector_ConnectConsumerHandler_Call { +func (_c *ConsumerConnector_ConnectConsumerHandler_Call) RunAndReturn(run func(types.IMessage, consumer2.ConsumerHandler) error) *ConsumerConnector_ConnectConsumerHandler_Call { _c.Call.Return(run) return _c } @@ -113,7 +113,8 @@ func (_c *ConsumerConnector_ConnectConsumerHandler_Call) RunAndReturn(run func(t func NewConsumerConnector(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerConnector { +}, +) *ConsumerConnector { mock := &ConsumerConnector{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerHandler.go b/internal/pkg/core/messaging/mocks/ConsumerHandler.go similarity index 95% rename from internal/pkg/messaging/mocks/ConsumerHandler.go rename to internal/pkg/core/messaging/mocks/ConsumerHandler.go index 279c5696..489023f5 100644 --- a/internal/pkg/messaging/mocks/ConsumerHandler.go +++ b/internal/pkg/core/messaging/mocks/ConsumerHandler.go @@ -5,7 +5,8 @@ package mocks import ( context "context" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + mock "github.com/stretchr/testify/mock" ) @@ -70,7 +71,8 @@ func (_c *ConsumerHandler_Handle_Call) RunAndReturn(run func(context.Context, ty func NewConsumerHandler(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerHandler { +}, +) *ConsumerHandler { mock := &ConsumerHandler{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilder.go b/internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilder.go similarity index 71% rename from internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilder.go rename to internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilder.go index 3cf46da4..938b022b 100644 --- a/internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilder.go +++ b/internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilder.go @@ -3,7 +3,8 @@ package mocks import ( - consumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + mock "github.com/stretchr/testify/mock" ) @@ -21,15 +22,15 @@ func (_m *ConsumerHandlerConfigurationBuilder) EXPECT() *ConsumerHandlerConfigur } // AddHandler provides a mock function with given fields: handler -func (_m *ConsumerHandlerConfigurationBuilder) AddHandler(handler consumer.ConsumerHandler) consumer.ConsumerHandlerConfigurationBuilder { +func (_m *ConsumerHandlerConfigurationBuilder) AddHandler(handler consumer2.ConsumerHandler) consumer2.ConsumerHandlerConfigurationBuilder { ret := _m.Called(handler) - var r0 consumer.ConsumerHandlerConfigurationBuilder - if rf, ok := ret.Get(0).(func(consumer.ConsumerHandler) consumer.ConsumerHandlerConfigurationBuilder); ok { + var r0 consumer2.ConsumerHandlerConfigurationBuilder + if rf, ok := ret.Get(0).(func(consumer2.ConsumerHandler) consumer2.ConsumerHandlerConfigurationBuilder); ok { r0 = rf(handler) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(consumer.ConsumerHandlerConfigurationBuilder) + r0 = ret.Get(0).(consumer2.ConsumerHandlerConfigurationBuilder) } } @@ -47,33 +48,33 @@ func (_e *ConsumerHandlerConfigurationBuilder_Expecter) AddHandler(handler inter return &ConsumerHandlerConfigurationBuilder_AddHandler_Call{Call: _e.mock.On("AddHandler", handler)} } -func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) Run(run func(handler consumer.ConsumerHandler)) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { +func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) Run(run func(handler consumer2.ConsumerHandler)) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(consumer.ConsumerHandler)) + run(args[0].(consumer2.ConsumerHandler)) }) return _c } -func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) Return(_a0 consumer.ConsumerHandlerConfigurationBuilder) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { +func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) Return(_a0 consumer2.ConsumerHandlerConfigurationBuilder) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { _c.Call.Return(_a0) return _c } -func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) RunAndReturn(run func(consumer.ConsumerHandler) consumer.ConsumerHandlerConfigurationBuilder) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { +func (_c *ConsumerHandlerConfigurationBuilder_AddHandler_Call) RunAndReturn(run func(consumer2.ConsumerHandler) consumer2.ConsumerHandlerConfigurationBuilder) *ConsumerHandlerConfigurationBuilder_AddHandler_Call { _c.Call.Return(run) return _c } // Build provides a mock function with given fields: -func (_m *ConsumerHandlerConfigurationBuilder) Build() *consumer.ConsumerHandlersConfiguration { +func (_m *ConsumerHandlerConfigurationBuilder) Build() *consumer2.ConsumerHandlersConfiguration { ret := _m.Called() - var r0 *consumer.ConsumerHandlersConfiguration - if rf, ok := ret.Get(0).(func() *consumer.ConsumerHandlersConfiguration); ok { + var r0 *consumer2.ConsumerHandlersConfiguration + if rf, ok := ret.Get(0).(func() *consumer2.ConsumerHandlersConfiguration); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*consumer.ConsumerHandlersConfiguration) + r0 = ret.Get(0).(*consumer2.ConsumerHandlersConfiguration) } } @@ -97,12 +98,12 @@ func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) Run(run func()) *Consu return _c } -func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) Return(_a0 *consumer.ConsumerHandlersConfiguration) *ConsumerHandlerConfigurationBuilder_Build_Call { +func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) Return(_a0 *consumer2.ConsumerHandlersConfiguration) *ConsumerHandlerConfigurationBuilder_Build_Call { _c.Call.Return(_a0) return _c } -func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) RunAndReturn(run func() *consumer.ConsumerHandlersConfiguration) *ConsumerHandlerConfigurationBuilder_Build_Call { +func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) RunAndReturn(run func() *consumer2.ConsumerHandlersConfiguration) *ConsumerHandlerConfigurationBuilder_Build_Call { _c.Call.Return(run) return _c } @@ -112,7 +113,8 @@ func (_c *ConsumerHandlerConfigurationBuilder_Build_Call) RunAndReturn(run func( func NewConsumerHandlerConfigurationBuilder(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerHandlerConfigurationBuilder { +}, +) *ConsumerHandlerConfigurationBuilder { mock := &ConsumerHandlerConfigurationBuilder{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go b/internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go similarity index 94% rename from internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go rename to internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go index 9addc220..86a40be7 100644 --- a/internal/pkg/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go +++ b/internal/pkg/core/messaging/mocks/ConsumerHandlerConfigurationBuilderFunc.go @@ -3,7 +3,8 @@ package mocks import ( - consumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + mock "github.com/stretchr/testify/mock" ) @@ -58,7 +59,8 @@ func (_c *ConsumerHandlerConfigurationBuilderFunc_Execute_Call) RunAndReturn(run func NewConsumerHandlerConfigurationBuilderFunc(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerHandlerConfigurationBuilderFunc { +}, +) *ConsumerHandlerConfigurationBuilderFunc { mock := &ConsumerHandlerConfigurationBuilderFunc{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerHandlerFunc.go b/internal/pkg/core/messaging/mocks/ConsumerHandlerFunc.go similarity index 100% rename from internal/pkg/messaging/mocks/ConsumerHandlerFunc.go rename to internal/pkg/core/messaging/mocks/ConsumerHandlerFunc.go diff --git a/internal/pkg/messaging/mocks/ConsumerPipeline.go b/internal/pkg/core/messaging/mocks/ConsumerPipeline.go similarity index 92% rename from internal/pkg/messaging/mocks/ConsumerPipeline.go rename to internal/pkg/core/messaging/mocks/ConsumerPipeline.go index 9484d083..bd884afe 100644 --- a/internal/pkg/messaging/mocks/ConsumerPipeline.go +++ b/internal/pkg/core/messaging/mocks/ConsumerPipeline.go @@ -5,8 +5,9 @@ package mocks import ( context "context" - pipeline "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + mock "github.com/stretchr/testify/mock" ) @@ -72,7 +73,8 @@ func (_c *ConsumerPipeline_Handle_Call) RunAndReturn(run func(context.Context, t func NewConsumerPipeline(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerPipeline { +}, +) *ConsumerPipeline { mock := &ConsumerPipeline{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilder.go b/internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilder.go similarity index 72% rename from internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilder.go rename to internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilder.go index f9134409..9e52fcaf 100644 --- a/internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilder.go +++ b/internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilder.go @@ -3,7 +3,8 @@ package mocks import ( - pipeline "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" + pipeline2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + mock "github.com/stretchr/testify/mock" ) @@ -21,15 +22,15 @@ func (_m *ConsumerPipelineConfigurationBuilder) EXPECT() *ConsumerPipelineConfig } // AddPipeline provides a mock function with given fields: _a0 -func (_m *ConsumerPipelineConfigurationBuilder) AddPipeline(_a0 pipeline.ConsumerPipeline) pipeline.ConsumerPipelineConfigurationBuilder { +func (_m *ConsumerPipelineConfigurationBuilder) AddPipeline(_a0 pipeline2.ConsumerPipeline) pipeline2.ConsumerPipelineConfigurationBuilder { ret := _m.Called(_a0) - var r0 pipeline.ConsumerPipelineConfigurationBuilder - if rf, ok := ret.Get(0).(func(pipeline.ConsumerPipeline) pipeline.ConsumerPipelineConfigurationBuilder); ok { + var r0 pipeline2.ConsumerPipelineConfigurationBuilder + if rf, ok := ret.Get(0).(func(pipeline2.ConsumerPipeline) pipeline2.ConsumerPipelineConfigurationBuilder); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(pipeline.ConsumerPipelineConfigurationBuilder) + r0 = ret.Get(0).(pipeline2.ConsumerPipelineConfigurationBuilder) } } @@ -47,33 +48,33 @@ func (_e *ConsumerPipelineConfigurationBuilder_Expecter) AddPipeline(_a0 interfa return &ConsumerPipelineConfigurationBuilder_AddPipeline_Call{Call: _e.mock.On("AddPipeline", _a0)} } -func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) Run(run func(_a0 pipeline.ConsumerPipeline)) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { +func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) Run(run func(_a0 pipeline2.ConsumerPipeline)) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(pipeline.ConsumerPipeline)) + run(args[0].(pipeline2.ConsumerPipeline)) }) return _c } -func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) Return(_a0 pipeline.ConsumerPipelineConfigurationBuilder) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { +func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) Return(_a0 pipeline2.ConsumerPipelineConfigurationBuilder) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { _c.Call.Return(_a0) return _c } -func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) RunAndReturn(run func(pipeline.ConsumerPipeline) pipeline.ConsumerPipelineConfigurationBuilder) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { +func (_c *ConsumerPipelineConfigurationBuilder_AddPipeline_Call) RunAndReturn(run func(pipeline2.ConsumerPipeline) pipeline2.ConsumerPipelineConfigurationBuilder) *ConsumerPipelineConfigurationBuilder_AddPipeline_Call { _c.Call.Return(run) return _c } // Build provides a mock function with given fields: -func (_m *ConsumerPipelineConfigurationBuilder) Build() *pipeline.ConsumerPipelineConfiguration { +func (_m *ConsumerPipelineConfigurationBuilder) Build() *pipeline2.ConsumerPipelineConfiguration { ret := _m.Called() - var r0 *pipeline.ConsumerPipelineConfiguration - if rf, ok := ret.Get(0).(func() *pipeline.ConsumerPipelineConfiguration); ok { + var r0 *pipeline2.ConsumerPipelineConfiguration + if rf, ok := ret.Get(0).(func() *pipeline2.ConsumerPipelineConfiguration); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*pipeline.ConsumerPipelineConfiguration) + r0 = ret.Get(0).(*pipeline2.ConsumerPipelineConfiguration) } } @@ -97,12 +98,12 @@ func (_c *ConsumerPipelineConfigurationBuilder_Build_Call) Run(run func()) *Cons return _c } -func (_c *ConsumerPipelineConfigurationBuilder_Build_Call) Return(_a0 *pipeline.ConsumerPipelineConfiguration) *ConsumerPipelineConfigurationBuilder_Build_Call { +func (_c *ConsumerPipelineConfigurationBuilder_Build_Call) Return(_a0 *pipeline2.ConsumerPipelineConfiguration) *ConsumerPipelineConfigurationBuilder_Build_Call { _c.Call.Return(_a0) return _c } -func (_c *ConsumerPipelineConfigurationBuilder_Build_Call) RunAndReturn(run func() *pipeline.ConsumerPipelineConfiguration) *ConsumerPipelineConfigurationBuilder_Build_Call { +func (_c *ConsumerPipelineConfigurationBuilder_Build_Call) RunAndReturn(run func() *pipeline2.ConsumerPipelineConfiguration) *ConsumerPipelineConfigurationBuilder_Build_Call { _c.Call.Return(run) return _c } diff --git a/internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go b/internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go similarity index 94% rename from internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go rename to internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go index cc2c887b..c18b03bc 100644 --- a/internal/pkg/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go +++ b/internal/pkg/core/messaging/mocks/ConsumerPipelineConfigurationBuilderFunc.go @@ -3,7 +3,8 @@ package mocks import ( - pipeline "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + mock "github.com/stretchr/testify/mock" ) @@ -58,7 +59,8 @@ func (_c *ConsumerPipelineConfigurationBuilderFunc_Execute_Call) RunAndReturn(ru func NewConsumerPipelineConfigurationBuilderFunc(t interface { mock.TestingT Cleanup(func()) -}) *ConsumerPipelineConfigurationBuilderFunc { +}, +) *ConsumerPipelineConfigurationBuilderFunc { mock := &ConsumerPipelineConfigurationBuilderFunc{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/IMessage.go b/internal/pkg/core/messaging/mocks/IMessage.go similarity index 96% rename from internal/pkg/messaging/mocks/IMessage.go rename to internal/pkg/core/messaging/mocks/IMessage.go index 95db2f29..c7e7b3b6 100644 --- a/internal/pkg/messaging/mocks/IMessage.go +++ b/internal/pkg/core/messaging/mocks/IMessage.go @@ -104,7 +104,7 @@ func (_c *IMessage_GetCreated_Call) RunAndReturn(run func() time.Time) *IMessage } // GetEventTypeName provides a mock function with given fields: -func (_m *IMessage) GetEventTypeName() string { +func (_m *IMessage) GetMessageTypeName() string { ret := _m.Called() var r0 string @@ -117,14 +117,14 @@ func (_m *IMessage) GetEventTypeName() string { return r0 } -// IMessage_GetEventTypeName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventTypeName' +// IMessage_GetEventTypeName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMessageTypeName' type IMessage_GetEventTypeName_Call struct { *mock.Call } // GetEventTypeName is a helper method to define mock.On call func (_e *IMessage_Expecter) GetEventTypeName() *IMessage_GetEventTypeName_Call { - return &IMessage_GetEventTypeName_Call{Call: _e.mock.On("GetEventTypeName")} + return &IMessage_GetEventTypeName_Call{Call: _e.mock.On("GetMessageTypeName")} } func (_c *IMessage_GetEventTypeName_Call) Run(run func()) *IMessage_GetEventTypeName_Call { @@ -223,7 +223,8 @@ func (_c *IMessage_SetEventTypeName_Call) RunAndReturn(run func(string)) *IMessa func NewIMessage(t interface { mock.TestingT Cleanup(func()) -}) *IMessage { +}, +) *IMessage { mock := &IMessage{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/MessageConsumeContext.go b/internal/pkg/core/messaging/mocks/MessageConsumeContext.go similarity index 98% rename from internal/pkg/messaging/mocks/MessageConsumeContext.go rename to internal/pkg/core/messaging/mocks/MessageConsumeContext.go index dce06413..fd21aec7 100644 --- a/internal/pkg/messaging/mocks/MessageConsumeContext.go +++ b/internal/pkg/core/messaging/mocks/MessageConsumeContext.go @@ -3,12 +3,12 @@ package mocks import ( - metadata "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - mock "github.com/stretchr/testify/mock" - time "time" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + metadata "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" + + mock "github.com/stretchr/testify/mock" ) // MessageConsumeContext is an autogenerated mock type for the MessageConsumeContext type @@ -361,7 +361,8 @@ func (_c *MessageConsumeContext_Metadata_Call) RunAndReturn(run func() metadata. func NewMessageConsumeContext(t interface { mock.TestingT Cleanup(func()) -}) *MessageConsumeContext { +}, +) *MessageConsumeContext { mock := &MessageConsumeContext{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/Producer.go b/internal/pkg/core/messaging/mocks/Producer.go similarity index 98% rename from internal/pkg/messaging/mocks/Producer.go rename to internal/pkg/core/messaging/mocks/Producer.go index ea8b9614..4cf3b961 100644 --- a/internal/pkg/messaging/mocks/Producer.go +++ b/internal/pkg/core/messaging/mocks/Producer.go @@ -5,10 +5,10 @@ package mocks import ( context "context" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" metadata "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - mock "github.com/stretchr/testify/mock" - types "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + mock "github.com/stretchr/testify/mock" ) // Producer is an autogenerated mock type for the Producer type @@ -151,7 +151,8 @@ func (_c *Producer_PublishMessageWithTopicName_Call) RunAndReturn(run func(conte func NewProducer(t interface { mock.TestingT Cleanup(func()) -}) *Producer { +}, +) *Producer { mock := &Producer{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/mocks/ProducerBuilderFuc.go b/internal/pkg/core/messaging/mocks/ProducerBuilderFuc.go similarity index 100% rename from internal/pkg/messaging/mocks/ProducerBuilderFuc.go rename to internal/pkg/core/messaging/mocks/ProducerBuilderFuc.go diff --git a/internal/pkg/messaging/otel/tracing/consts.go b/internal/pkg/core/messaging/otel/tracing/consts.go similarity index 100% rename from internal/pkg/messaging/otel/tracing/consts.go rename to internal/pkg/core/messaging/otel/tracing/consts.go diff --git a/internal/pkg/messaging/otel/tracing/consumer/consumer.go b/internal/pkg/core/messaging/otel/tracing/consumer/consumer.go similarity index 77% rename from internal/pkg/messaging/otel/tracing/consumer/consumer.go rename to internal/pkg/core/messaging/otel/tracing/consumer/consumer.go index 250faf88..a82fc5c8 100644 --- a/internal/pkg/messaging/otel/tracing/consumer/consumer.go +++ b/internal/pkg/core/messaging/otel/tracing/consumer/consumer.go @@ -5,11 +5,12 @@ import ( "fmt" "time" + messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/messageheader" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/message_header" - messageTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants" tracingHeaders "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/tracing_headers" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -35,14 +36,14 @@ func StartConsumerSpan( // If there's a span context in the message, use that as the parent context. // extracts the tracing from the header and puts it into the context - carrier := messageTracing.NewMessageCarrier(meta) + carrier := tracing.NewMessageCarrier(meta) parentSpanContext := otel.GetTextMapPropagator().Extract(ctx, carrier) opts := getTraceOptions(meta, payload, consumerTracingOptions) // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - // SpanName = Destination Name + Operation Name - ctx, span := messageTracing.MessagingTracer.Start( + // SpanName = Destination ShortTypeName + Operation ShortTypeName + ctx, span := tracing.MessagingTracer.Start( parentSpanContext, fmt.Sprintf("%s %s", consumerTracingOptions.Destination, "receive"), opts...) @@ -57,15 +58,15 @@ func StartConsumerSpan( } func FinishConsumerSpan(span trace.Span, err error) error { - messageName := tracing.GetSpanAttribute(span, messageTracing.MessageName).Value.AsString() + messageName := utils.GetSpanAttribute(span, tracing.MessageName).Value.AsString() if err != nil { span.AddEvent(fmt.Sprintf("failed to consume message '%s' from the broker", messageName)) - _ = messageTracing.TraceMessagingErrFromSpan(span, err) + _ = utils.TraceErrStatusFromSpan(span, err) } span.SetAttributes( - attribute.Key(tracing.SpanId).String(span.SpanContext().SpanID().String()), // current span id + attribute.Key(constants.SpanId).String(span.SpanContext().SpanID().String()), // current span id ) span.AddEvent(fmt.Sprintf("message '%s' consumed from the broker succesfully", messageName)) @@ -87,14 +88,14 @@ func getTraceOptions( semconv.MessageIDKey.String(messageHeader.GetMessageId(*meta)), semconv.MessagingMessageConversationID(correlationId), semconv.MessagingOperationReceive, - attribute.Key(tracing.TraceId).String(tracingHeaders.GetTracingTraceId(*meta)), - attribute.Key(tracing.Traceparent).String(tracingHeaders.GetTracingTraceparent(*meta)), - attribute.Key(tracing.ParentSpanId).String(tracingHeaders.GetTracingParentSpanId(*meta)), - attribute.Key(tracing.Timestamp).Int64(time.Now().UnixMilli()), - attribute.Key(messageTracing.MessageType).String(messageHeader.GetMessageType(*meta)), - attribute.Key(messageTracing.MessageName).String(messageHeader.GetMessageName(*meta)), - attribute.Key(messageTracing.Payload).String(payload), - attribute.String(messageTracing.Headers, meta.ToJson()), + attribute.Key(constants.TraceId).String(tracingHeaders.GetTracingTraceId(*meta)), + attribute.Key(constants.Traceparent).String(tracingHeaders.GetTracingTraceparent(*meta)), + attribute.Key(constants.ParentSpanId).String(tracingHeaders.GetTracingParentSpanId(*meta)), + attribute.Key(constants.Timestamp).Int64(time.Now().UnixMilli()), + attribute.Key(tracing.MessageType).String(messageHeader.GetMessageType(*meta)), + attribute.Key(tracing.MessageName).String(messageHeader.GetMessageName(*meta)), + attribute.Key(tracing.Payload).String(payload), + attribute.String(tracing.Headers, meta.ToJson()), semconv.MessagingDestinationName(consumerTracingOptions.Destination), semconv.MessagingSystemKey.String(consumerTracingOptions.MessagingSystem), } diff --git a/internal/pkg/messaging/otel/tracing/consumer/consumer_tracing_options.go b/internal/pkg/core/messaging/otel/tracing/consumer/consumer_tracing_options.go similarity index 100% rename from internal/pkg/messaging/otel/tracing/consumer/consumer_tracing_options.go rename to internal/pkg/core/messaging/otel/tracing/consumer/consumer_tracing_options.go diff --git a/internal/pkg/messaging/otel/tracing/message_carrier.go b/internal/pkg/core/messaging/otel/tracing/message_carrier.go similarity index 100% rename from internal/pkg/messaging/otel/tracing/message_carrier.go rename to internal/pkg/core/messaging/otel/tracing/message_carrier.go diff --git a/internal/pkg/messaging/otel/tracing/producer/producer.go b/internal/pkg/core/messaging/otel/tracing/producer/producer.go similarity index 65% rename from internal/pkg/messaging/otel/tracing/producer/producer.go rename to internal/pkg/core/messaging/otel/tracing/producer/producer.go index 381a7cba..7dc50a07 100644 --- a/internal/pkg/messaging/otel/tracing/producer/producer.go +++ b/internal/pkg/core/messaging/otel/tracing/producer/producer.go @@ -5,11 +5,12 @@ import ( "fmt" "time" + messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/messageheader" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/message_header" - messageTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -36,19 +37,24 @@ func StartProducerSpan( // If there's a span context in the message, use that as the parent context. // extracts the tracing from the header and puts it into the context - carrier := messageTracing.NewMessageCarrier(meta) + carrier := tracing.NewMessageCarrier(meta) parentSpanContext := otel.GetTextMapPropagator().Extract(ctx, carrier) opts := getTraceOptions(meta, message, payload, producerTracingOptions) // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md#span-name - // SpanName = Destination Name + Operation Name - ctx, span := messageTracing.MessagingTracer.Start( + // SpanName = Destination ShortTypeName + Operation ShortTypeName + ctx, span := tracing.MessagingTracer.Start( parentSpanContext, fmt.Sprintf("%s %s", producerTracingOptions.Destination, "send"), opts...) - span.AddEvent(fmt.Sprintf("start publishing message '%s' to the broker", messageHeader.GetMessageName(*meta))) + span.AddEvent( + fmt.Sprintf( + "start publishing message '%s' to the broker", + messageHeader.GetMessageName(*meta), + ), + ) // Injects current span context, so consumers can use it to propagate span. // injects the tracing from the context into the header map @@ -59,18 +65,33 @@ func StartProducerSpan( } func FinishProducerSpan(span trace.Span, err error) error { - messageName := tracing.GetSpanAttribute(span, messageTracing.MessageName).Value.AsString() + messageName := utils.GetSpanAttribute( + span, + tracing.MessageName, + ).Value.AsString() if err != nil { - span.AddEvent(fmt.Sprintf("failed to publsih message '%s' to the broker", messageName)) - _ = messageTracing.TraceMessagingErrFromSpan(span, err) + span.AddEvent( + fmt.Sprintf( + "failed to publsih message '%s' to the broker", + messageName, + ), + ) + _ = utils.TraceErrStatusFromSpan(span, err) } span.SetAttributes( - attribute.Key(tracing.TraceId).String(span.SpanContext().TraceID().String()), - attribute.Key(tracing.SpanId).String(span.SpanContext().SpanID().String()), // current span id + attribute.Key(constants.TraceId). + String(span.SpanContext().TraceID().String()), + attribute.Key(constants.SpanId). + String(span.SpanContext().SpanID().String()), // current span id ) - span.AddEvent(fmt.Sprintf("message '%s' published to the broker succesfully", messageName)) + span.AddEvent( + fmt.Sprintf( + "message '%s' published to the broker succesfully", + messageName, + ), + ) span.End() return err @@ -89,17 +110,22 @@ func getTraceOptions( attrs := []attribute.KeyValue{ semconv.MessageIDKey.String(message.GeMessageId()), semconv.MessagingMessageConversationID(correlationId), - attribute.Key(messageTracing.MessageType).String(message.GetEventTypeName()), - attribute.Key(messageTracing.MessageName).String(messageHeader.GetMessageName(*meta)), - attribute.Key(messageTracing.Payload).String(payload), - attribute.String(messageTracing.Headers, meta.ToJson()), - attribute.Key(tracing.Timestamp).Int64(time.Now().UnixMilli()), + attribute.Key(tracing.MessageType). + String(message.GetMessageTypeName()), + attribute.Key(tracing.MessageName). + String(messageHeader.GetMessageName(*meta)), + attribute.Key(tracing.Payload).String(payload), + attribute.String(tracing.Headers, meta.ToJson()), + attribute.Key(constants.Timestamp).Int64(time.Now().UnixMilli()), semconv.MessagingDestinationName(producerTracingOptions.Destination), - semconv.MessagingSystemKey.String(producerTracingOptions.MessagingSystem), + semconv.MessagingSystemKey.String( + producerTracingOptions.MessagingSystem, + ), semconv.MessagingOperationKey.String("send"), } - if producerTracingOptions.OtherAttributes != nil && len(producerTracingOptions.OtherAttributes) > 0 { + if producerTracingOptions.OtherAttributes != nil && + len(producerTracingOptions.OtherAttributes) > 0 { attrs = append(attrs, producerTracingOptions.OtherAttributes...) } @@ -110,11 +136,21 @@ func getTraceOptions( return opts } -func addAfterBaggage(ctx context.Context, message types.IMessage, meta *metadata.Metadata) context.Context { +func addAfterBaggage( + ctx context.Context, + message types.IMessage, + meta *metadata.Metadata, +) context.Context { correlationId := messageHeader.GetCorrelationId(*meta) - correlationIdBag, _ := baggage.NewMember(string(semconv.MessagingMessageConversationIDKey), correlationId) - messageIdBag, _ := baggage.NewMember(string(semconv.MessageIDKey), message.GeMessageId()) + correlationIdBag, _ := baggage.NewMember( + string(semconv.MessagingMessageConversationIDKey), + correlationId, + ) + messageIdBag, _ := baggage.NewMember( + string(semconv.MessageIDKey), + message.GeMessageId(), + ) b, _ := baggage.New(correlationIdBag, messageIdBag) ctx = baggage.ContextWithBaggage(ctx, b) diff --git a/internal/pkg/messaging/otel/tracing/producer/producer_tracing_options.go b/internal/pkg/core/messaging/otel/tracing/producer/producer_tracing_options.go similarity index 100% rename from internal/pkg/messaging/otel/tracing/producer/producer_tracing_options.go rename to internal/pkg/core/messaging/otel/tracing/producer/producer_tracing_options.go diff --git a/internal/pkg/messaging/otel/tracing/trace.go b/internal/pkg/core/messaging/otel/tracing/trace.go similarity index 100% rename from internal/pkg/messaging/otel/tracing/trace.go rename to internal/pkg/core/messaging/otel/tracing/trace.go diff --git a/internal/pkg/core/messaging/persistmessage/message_persistence_service.go b/internal/pkg/core/messaging/persistmessage/message_persistence_service.go new file mode 100644 index 00000000..26c37226 --- /dev/null +++ b/internal/pkg/core/messaging/persistmessage/message_persistence_service.go @@ -0,0 +1,41 @@ +package persistmessage + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + + uuid "github.com/satori/go.uuid" +) + +type MessagePersistenceService interface { + Add(ctx context.Context, storeMessage *StoreMessage) error + Update(ctx context.Context, storeMessage *StoreMessage) error + ChangeState( + ctx context.Context, + messageID uuid.UUID, + status MessageStatus, + ) error + GetAllActive(ctx context.Context) ([]*StoreMessage, error) + GetByFilter( + ctx context.Context, + predicate func(*StoreMessage) bool, + ) ([]*StoreMessage, error) + GetById(ctx context.Context, id uuid.UUID) (*StoreMessage, error) + Remove(ctx context.Context, storeMessage *StoreMessage) (bool, error) + CleanupMessages(ctx context.Context) error + Process(messageID string, ctx context.Context) error + ProcessAll(ctx context.Context) error + AddPublishMessage( + messageEnvelope types.MessageEnvelope, + ctx context.Context, + ) error + AddReceivedMessage( + messageEnvelope types.MessageEnvelope, + ctx context.Context, + ) error + //AddInternalMessage( + // internalCommand InternalMessage, + // ctx context.Context, + //) error +} diff --git a/internal/pkg/core/messaging/persistmessage/store_message.go b/internal/pkg/core/messaging/persistmessage/store_message.go new file mode 100644 index 00000000..8343df8f --- /dev/null +++ b/internal/pkg/core/messaging/persistmessage/store_message.go @@ -0,0 +1,61 @@ +package persistmessage + +import ( + "time" + + uuid "github.com/satori/go.uuid" +) + +type MessageDeliveryType int + +const ( + Outbox MessageDeliveryType = 1 + Inbox MessageDeliveryType = 2 + Internal MessageDeliveryType = 4 +) + +type MessageStatus int + +const ( + Stored MessageStatus = 1 + Processed MessageStatus = 2 +) + +type StoreMessage struct { + ID uuid.UUID `gorm:"primaryKey"` + DataType string + Data string + CreatedAt time.Time `gorm:"default:current_timestamp"` + RetryCount int + MessageStatus MessageStatus + DeliveryType MessageDeliveryType +} + +func NewStoreMessage( + id uuid.UUID, + dataType string, + data string, + deliveryType MessageDeliveryType, +) StoreMessage { + return StoreMessage{ + ID: id, + DataType: dataType, + Data: data, + CreatedAt: time.Now(), + MessageStatus: Stored, + RetryCount: 0, + DeliveryType: deliveryType, + } +} + +func (sm *StoreMessage) ChangeState(messageStatus MessageStatus) { + sm.MessageStatus = messageStatus +} + +func (sm *StoreMessage) IncreaseRetry() { + sm.RetryCount++ +} + +func (sm *StoreMessage) TableName() string { + return "store_messages" +} diff --git a/internal/pkg/messaging/pipeline/consumer_pipeline.go b/internal/pkg/core/messaging/pipeline/consumer_pipeline.go similarity index 83% rename from internal/pkg/messaging/pipeline/consumer_pipeline.go rename to internal/pkg/core/messaging/pipeline/consumer_pipeline.go index 05c2165b..ea0a6d3e 100644 --- a/internal/pkg/messaging/pipeline/consumer_pipeline.go +++ b/internal/pkg/core/messaging/pipeline/consumer_pipeline.go @@ -3,11 +3,11 @@ package pipeline import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) // ConsumerHandlerFunc is a continuation for the next task to execute in the pipeline -type ConsumerHandlerFunc func() error +type ConsumerHandlerFunc func(ctx context.Context) error // ConsumerPipeline is a Pipeline for wrapping the inner consumer handler. type ConsumerPipeline interface { diff --git a/internal/pkg/messaging/pipeline/consumer_pipeline_configuration.go b/internal/pkg/core/messaging/pipeline/consumer_pipeline_configuration.go similarity index 100% rename from internal/pkg/messaging/pipeline/consumer_pipeline_configuration.go rename to internal/pkg/core/messaging/pipeline/consumer_pipeline_configuration.go diff --git a/internal/pkg/messaging/pipeline/consumer_pipeline_configuration_builder.go b/internal/pkg/core/messaging/pipeline/consumer_pipeline_configuration_builder.go similarity index 100% rename from internal/pkg/messaging/pipeline/consumer_pipeline_configuration_builder.go rename to internal/pkg/core/messaging/pipeline/consumer_pipeline_configuration_builder.go diff --git a/internal/pkg/messaging/producer/producer.go b/internal/pkg/core/messaging/producer/producer.go similarity index 95% rename from internal/pkg/messaging/producer/producer.go rename to internal/pkg/core/messaging/producer/producer.go index 5589fd13..fa55a319 100644 --- a/internal/pkg/messaging/producer/producer.go +++ b/internal/pkg/core/messaging/producer/producer.go @@ -3,8 +3,8 @@ package producer import ( "context" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" ) type Producer interface { diff --git a/internal/pkg/messaging/producer/producer_builder.go b/internal/pkg/core/messaging/producer/producer_builder.go similarity index 100% rename from internal/pkg/messaging/producer/producer_builder.go rename to internal/pkg/core/messaging/producer/producer_builder.go diff --git a/internal/pkg/messaging/types/message.go b/internal/pkg/core/messaging/types/message.go similarity index 56% rename from internal/pkg/messaging/types/message.go rename to internal/pkg/core/messaging/types/message.go index 7203ffda..114b0b4b 100644 --- a/internal/pkg/messaging/types/message.go +++ b/internal/pkg/core/messaging/types/message.go @@ -2,14 +2,16 @@ package types import ( "time" + + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" ) type IMessage interface { GeMessageId() string GetCreated() time.Time - GetEventTypeName() string - SetEventTypeName(string) - IsMessage() bool + // GetMessageTypeName get short type name of the message - we use message short type name instead of full type name because this message in other receiver packages could have different package name + GetMessageTypeName() string + GetMessageFullTypeName() string } type Message struct { @@ -27,13 +29,6 @@ func NewMessageWithTypeName(messageId string, eventTypeName string) *Message { return &Message{MessageId: messageId, Created: time.Now(), EventType: eventTypeName} } -func (m *Message) IsMessage() bool { - if m == nil { - return false - } - return true -} - func (m *Message) GeMessageId() string { return m.MessageId } @@ -42,10 +37,10 @@ func (m *Message) GetCreated() time.Time { return m.Created } -func (m *Message) GetEventTypeName() string { - return m.EventType +func (m *Message) GetMessageTypeName() string { + return typeMapper.GetTypeName(m) } -func (m *Message) SetEventTypeName(eventTypeName string) { - m.EventType = eventTypeName +func (m *Message) GetMessageFullTypeName() string { + return typeMapper.GetFullTypeName(m) } diff --git a/internal/pkg/messaging/types/message_consume_context.go b/internal/pkg/core/messaging/types/message_consume_context.go similarity index 100% rename from internal/pkg/messaging/types/message_consume_context.go rename to internal/pkg/core/messaging/types/message_consume_context.go diff --git a/internal/pkg/core/messaging/types/message_envelope.go b/internal/pkg/core/messaging/types/message_envelope.go new file mode 100644 index 00000000..f2a1ded1 --- /dev/null +++ b/internal/pkg/core/messaging/types/message_envelope.go @@ -0,0 +1,37 @@ +package types + +type MessageEnvelope struct { + Message IMessage + Headers map[string]interface{} +} + +func NewMessageEnvelope( + message IMessage, + headers map[string]interface{}, +) *MessageEnvelope { + if headers == nil { + headers = make(map[string]interface{}) + } + + return &MessageEnvelope{ + Message: message, + Headers: headers, + } +} + +type MessageEnvelopeT[T IMessage] struct { + *MessageEnvelope + Message T +} + +func NewMessageEnvelopeT[T IMessage]( + message T, + headers map[string]interface{}, +) *MessageEnvelopeT[T] { + messageEnvelope := NewMessageEnvelope(message, headers) + + return &MessageEnvelopeT[T]{ + MessageEnvelope: messageEnvelope, + Message: message, + } +} diff --git a/internal/pkg/messaging/types/mock_IMessage_test.go b/internal/pkg/core/messaging/types/mock_IMessage_test.go similarity index 98% rename from internal/pkg/messaging/types/mock_IMessage_test.go rename to internal/pkg/core/messaging/types/mock_IMessage_test.go index 0b1b265b..7f030c23 100644 --- a/internal/pkg/messaging/types/mock_IMessage_test.go +++ b/internal/pkg/core/messaging/types/mock_IMessage_test.go @@ -104,7 +104,7 @@ func (_c *MockIMessage_GetCreated_Call) RunAndReturn(run func() time.Time) *Mock } // GetEventTypeName provides a mock function with given fields: -func (_m *MockIMessage) GetEventTypeName() string { +func (_m *MockIMessage) GetMessageTypeName() string { ret := _m.Called() var r0 string @@ -117,14 +117,14 @@ func (_m *MockIMessage) GetEventTypeName() string { return r0 } -// MockIMessage_GetEventTypeName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventTypeName' +// MockIMessage_GetEventTypeName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMessageTypeName' type MockIMessage_GetEventTypeName_Call struct { *mock.Call } // GetEventTypeName is a helper method to define mock.On call func (_e *MockIMessage_Expecter) GetEventTypeName() *MockIMessage_GetEventTypeName_Call { - return &MockIMessage_GetEventTypeName_Call{Call: _e.mock.On("GetEventTypeName")} + return &MockIMessage_GetEventTypeName_Call{Call: _e.mock.On("GetMessageTypeName")} } func (_c *MockIMessage_GetEventTypeName_Call) Run(run func()) *MockIMessage_GetEventTypeName_Call { @@ -223,7 +223,8 @@ func (_c *MockIMessage_SetEventTypeName_Call) RunAndReturn(run func(string)) *Mo func NewMockIMessage(t interface { mock.TestingT Cleanup(func()) -}) *MockIMessage { +}, +) *MockIMessage { mock := &MockIMessage{} mock.Mock.Test(t) diff --git a/internal/pkg/messaging/utils/utils.go b/internal/pkg/core/messaging/utils/utils.go similarity index 97% rename from internal/pkg/messaging/utils/utils.go rename to internal/pkg/core/messaging/utils/utils.go index 534a973b..10a21e27 100644 --- a/internal/pkg/messaging/utils/utils.go +++ b/internal/pkg/core/messaging/utils/utils.go @@ -3,8 +3,8 @@ package utils import ( "reflect" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/ahmetb/go-linq/v3" "github.com/iancoleman/strcase" diff --git a/internal/pkg/core/mocks/EventSerializer.go b/internal/pkg/core/mocks/EventSerializer.go index 4cf217fa..c7de4adf 100644 --- a/internal/pkg/core/mocks/EventSerializer.go +++ b/internal/pkg/core/mocks/EventSerializer.go @@ -5,6 +5,8 @@ package mocks import ( reflect "reflect" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" + mock "github.com/stretchr/testify/mock" serializer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" @@ -343,15 +345,15 @@ func (_c *EventSerializer_Serialize_Call) RunAndReturn(run func(interface{}) (*s } // Serializer provides a mock function with given fields: -func (_m *EventSerializer) Serializer() serializer.Serializer { +func (_m *EventSerializer) Serializer() contratcs.Serializer { ret := _m.Called() - var r0 serializer.Serializer - if rf, ok := ret.Get(0).(func() serializer.Serializer); ok { + var r0 contratcs.Serializer + if rf, ok := ret.Get(0).(func() contratcs.Serializer); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(serializer.Serializer) + r0 = ret.Get(0).(contratcs.Serializer) } } @@ -375,12 +377,12 @@ func (_c *EventSerializer_Serializer_Call) Run(run func()) *EventSerializer_Seri return _c } -func (_c *EventSerializer_Serializer_Call) Return(_a0 serializer.Serializer) *EventSerializer_Serializer_Call { +func (_c *EventSerializer_Serializer_Call) Return(_a0 contratcs.Serializer) *EventSerializer_Serializer_Call { _c.Call.Return(_a0) return _c } -func (_c *EventSerializer_Serializer_Call) RunAndReturn(run func() serializer.Serializer) *EventSerializer_Serializer_Call { +func (_c *EventSerializer_Serializer_Call) RunAndReturn(run func() contratcs.Serializer) *EventSerializer_Serializer_Call { _c.Call.Return(run) return _c } @@ -390,7 +392,8 @@ func (_c *EventSerializer_Serializer_Call) RunAndReturn(run func() serializer.Se func NewEventSerializer(t interface { mock.TestingT Cleanup(func()) -}) *EventSerializer { +}, +) *EventSerializer { mock := &EventSerializer{} mock.Mock.Test(t) diff --git a/internal/pkg/core/mocks/IDomainEvent.go b/internal/pkg/core/mocks/IDomainEvent.go index 72c220e3..b8a34b51 100644 --- a/internal/pkg/core/mocks/IDomainEvent.go +++ b/internal/pkg/core/mocks/IDomainEvent.go @@ -165,14 +165,14 @@ func (_m *IDomainEvent) GetEventType() string { return r0 } -// IDomainEvent_GetEventType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventType' +// IDomainEvent_GetEventType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventTypeName' type IDomainEvent_GetEventType_Call struct { *mock.Call } // GetEventType is a helper method to define mock.On call func (_e *IDomainEvent_Expecter) GetEventType() *IDomainEvent_GetEventType_Call { - return &IDomainEvent_GetEventType_Call{Call: _e.mock.On("GetEventType")} + return &IDomainEvent_GetEventType_Call{Call: _e.mock.On("GetEventTypeName")} } func (_c *IDomainEvent_GetEventType_Call) Run(run func()) *IDomainEvent_GetEventType_Call { @@ -283,7 +283,8 @@ func (_c *IDomainEvent_WithAggregate_Call) RunAndReturn(run func(uuid.UUID, int6 func NewIDomainEvent(t interface { mock.TestingT Cleanup(func()) -}) *IDomainEvent { +}, +) *IDomainEvent { mock := &IDomainEvent{} mock.Mock.Test(t) diff --git a/internal/pkg/core/mocks/IEvent.go b/internal/pkg/core/mocks/IEvent.go index 57d4e505..02f5c2a3 100644 --- a/internal/pkg/core/mocks/IEvent.go +++ b/internal/pkg/core/mocks/IEvent.go @@ -80,14 +80,14 @@ func (_m *IEvent) GetEventType() string { return r0 } -// IEvent_GetEventType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventType' +// IEvent_GetEventType_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEventTypeName' type IEvent_GetEventType_Call struct { *mock.Call } // GetEventType is a helper method to define mock.On call func (_e *IEvent_Expecter) GetEventType() *IEvent_GetEventType_Call { - return &IEvent_GetEventType_Call{Call: _e.mock.On("GetEventType")} + return &IEvent_GetEventType_Call{Call: _e.mock.On("GetEventTypeName")} } func (_c *IEvent_GetEventType_Call) Run(run func()) *IEvent_GetEventType_Call { @@ -153,7 +153,8 @@ func (_c *IEvent_GetOccurredOn_Call) RunAndReturn(run func() time.Time) *IEvent_ func NewIEvent(t interface { mock.TestingT Cleanup(func()) -}) *IEvent { +}, +) *IEvent { mock := &IEvent{} mock.Mock.Test(t) diff --git a/internal/pkg/core/serializer/contratcs/event_serializer.go b/internal/pkg/core/serializer/contratcs/event_serializer.go new file mode 100644 index 00000000..ce36a2f1 --- /dev/null +++ b/internal/pkg/core/serializer/contratcs/event_serializer.go @@ -0,0 +1,17 @@ +package contratcs + +import ( + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" +) + +type EventSerializer interface { + Serialize(event domain.IDomainEvent) (*EventSerializationResult, error) + SerializeObject(event interface{}) (*EventSerializationResult, error) + Deserialize(data []byte, eventType string, contentType string) (domain.IDomainEvent, error) + DeserializeObject(data []byte, eventType string, contentType string) (interface{}, error) + DeserializeType(data []byte, eventType reflect.Type, contentType string) (domain.IDomainEvent, error) + ContentType() string + Serializer() Serializer +} diff --git a/internal/pkg/core/serializer/contratcs/event_serrialization_result.go b/internal/pkg/core/serializer/contratcs/event_serrialization_result.go new file mode 100644 index 00000000..f528235a --- /dev/null +++ b/internal/pkg/core/serializer/contratcs/event_serrialization_result.go @@ -0,0 +1,6 @@ +package contratcs + +type EventSerializationResult struct { + Data []byte + ContentType string +} diff --git a/internal/pkg/core/serializer/contratcs/message_serializer.go b/internal/pkg/core/serializer/contratcs/message_serializer.go new file mode 100644 index 00000000..ee11561f --- /dev/null +++ b/internal/pkg/core/serializer/contratcs/message_serializer.go @@ -0,0 +1,18 @@ +package contratcs + +import ( + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" +) + +type MessageSerializer interface { + Serialize(message types.IMessage) (*EventSerializationResult, error) + SerializeObject(message interface{}) (*EventSerializationResult, error) + SerializeEnvelop(messageEnvelop types.MessageEnvelope) (*EventSerializationResult, error) + Deserialize(data []byte, messageType string, contentType string) (types.IMessage, error) + DeserializeObject(data []byte, messageType string, contentType string) (interface{}, error) + DeserializeType(data []byte, messageType reflect.Type, contentType string) (types.IMessage, error) + ContentType() string + Serializer() Serializer +} diff --git a/internal/pkg/core/serializer/contratcs/metadata_serializer.go b/internal/pkg/core/serializer/contratcs/metadata_serializer.go new file mode 100644 index 00000000..7f3d3966 --- /dev/null +++ b/internal/pkg/core/serializer/contratcs/metadata_serializer.go @@ -0,0 +1,8 @@ +package contratcs + +import "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" + +type MetadataSerializer interface { + Serialize(meta metadata.Metadata) ([]byte, error) + Deserialize(bytes []byte) (metadata.Metadata, error) +} diff --git a/internal/pkg/core/serializer/serilizer.go b/internal/pkg/core/serializer/contratcs/serilizer.go similarity index 95% rename from internal/pkg/core/serializer/serilizer.go rename to internal/pkg/core/serializer/contratcs/serilizer.go index 47825d26..24e1651c 100644 --- a/internal/pkg/core/serializer/serilizer.go +++ b/internal/pkg/core/serializer/contratcs/serilizer.go @@ -1,4 +1,4 @@ -package serializer +package contratcs type Serializer interface { Marshal(v interface{}) ([]byte, error) diff --git a/internal/pkg/core/serializer/event_serializer.go b/internal/pkg/core/serializer/event_serializer.go deleted file mode 100644 index 3a4995c0..00000000 --- a/internal/pkg/core/serializer/event_serializer.go +++ /dev/null @@ -1,154 +0,0 @@ -package serializer - -import ( - "reflect" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/events" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" - - "emperror.dev/errors" -) - -type EventSerializer interface { - Serialize(event interface{}) (*EventSerializationResult, error) - Deserialize(data []byte, eventType string, contentType string) (interface{}, error) - DeserializeType(data []byte, eventType reflect.Type, contentType string) (interface{}, error) - DeserializeMessage(data []byte, eventType string, contentType string) (interface{}, error) - DeserializeEvent(data []byte, eventType string, contentType string) (interface{}, error) - ContentType() string - Serializer() Serializer -} - -type EventSerializationResult struct { - Data []byte - ContentType string -} - -type DefaultEventSerializer struct { - serializer Serializer -} - -func NewDefaultEventSerializer(serializer Serializer) EventSerializer { - return &DefaultEventSerializer{serializer: serializer} -} - -func (s *DefaultEventSerializer) Serializer() Serializer { - return s.serializer -} - -func (s *DefaultEventSerializer) Serialize( - event interface{}, -) (*EventSerializationResult, error) { - if event == nil { - return &EventSerializationResult{Data: nil, ContentType: s.ContentType()}, nil - } - - // here we just get type name instead of full type name - eventType := typeMapper.GetTypeName(event) - - data, err := s.serializer.Marshal(event) - if err != nil { - return nil, errors.WrapIff(err, "event.GetJsonData type: %s", eventType) - } - - result := &EventSerializationResult{Data: data, ContentType: s.ContentType()} - - return result, nil -} - -func (s *DefaultEventSerializer) Deserialize( - data []byte, - eventType string, - contentType string, -) (interface{}, error) { - if data == nil { - return nil, nil - } - - targetEventPointer := typeMapper.InstanceByTypeName(eventType) - - if contentType != s.ContentType() { - return nil, errors.Errorf("contentType: %s is not supported", contentType) - } - - if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { - return nil, errors.WrapIff(err, "event.GetJsonData type: %s", eventType) - } - - return targetEventPointer, nil -} - -func (s *DefaultEventSerializer) DeserializeType( - data []byte, - eventType reflect.Type, - contentType string, -) (interface{}, error) { - if data == nil { - return nil, nil - } - - targetEventPointer := typeMapper.InstanceByType(eventType) - - if contentType != s.ContentType() { - return nil, errors.Errorf("contentType: %s is not supported", contentType) - } - - if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { - return nil, errors.WrapIff(err, "event.GetJsonData type: %s", eventType) - } - - return targetEventPointer, nil -} - -func (s *DefaultEventSerializer) DeserializeMessage( - data []byte, - eventType string, - contentType string, -) (interface{}, error) { - if data == nil { - return nil, nil - } - - targetEventPointer := typeMapper.InstanceByTypeNameAndImplementedInterface[types.IMessage]( - eventType, - ) - - if contentType != s.ContentType() { - return nil, errors.Errorf("contentType: %s is not supported", contentType) - } - - if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { - return nil, errors.WrapIff(err, "event.GetJsonData type: %s", eventType) - } - - return targetEventPointer, nil -} - -func (s *DefaultEventSerializer) DeserializeEvent( - data []byte, - eventType string, - contentType string, -) (interface{}, error) { - if data == nil { - return nil, nil - } - - targetEventPointer := typeMapper.InstanceByTypeNameAndImplementedInterface[events.IEvent]( - eventType, - ) - - if contentType != s.ContentType() { - return nil, errors.Errorf("contentType: %s is not supported", contentType) - } - - if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { - return nil, errors.WrapIff(err, "event.GetJsonData type: %s", eventType) - } - - return targetEventPointer, nil -} - -func (s *DefaultEventSerializer) ContentType() string { - return "application/json" -} diff --git a/internal/pkg/core/serializer/json/event_serializer.go b/internal/pkg/core/serializer/json/event_serializer.go new file mode 100644 index 00000000..91e5d430 --- /dev/null +++ b/internal/pkg/core/serializer/json/event_serializer.go @@ -0,0 +1,118 @@ +package json + +import ( + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "emperror.dev/errors" +) + +type DefaultEventJsonSerializer struct { + serializer contratcs.Serializer +} + +func NewDefaultEventJsonSerializer(serializer contratcs.Serializer) contratcs.EventSerializer { + return &DefaultEventJsonSerializer{serializer: serializer} +} + +func (s *DefaultEventJsonSerializer) Serialize(event domain.IDomainEvent) (*contratcs.EventSerializationResult, error) { + return s.SerializeObject(event) +} + +func (s *DefaultEventJsonSerializer) SerializeObject(event interface{}) (*contratcs.EventSerializationResult, error) { + if event == nil { + return &contratcs.EventSerializationResult{Data: nil, ContentType: s.ContentType()}, nil + } + + // we use event short type name instead of full type name because this event in other receiver packages could have different package name + eventType := typeMapper.GetTypeName(event) + + data, err := s.serializer.Marshal(event) + if err != nil { + return nil, errors.WrapIff(err, "error in Marshaling: `%s`", eventType) + } + + result := &contratcs.EventSerializationResult{Data: data, ContentType: s.ContentType()} + + return result, nil +} + +func (s *DefaultEventJsonSerializer) Deserialize( + data []byte, + eventType string, + contentType string, +) (domain.IDomainEvent, error) { + if data == nil { + return nil, nil + } + + targetEventPointer := typeMapper.EmptyInstanceByTypeNameAndImplementedInterface[domain.IDomainEvent]( + eventType, + ) + + if targetEventPointer == nil { + return nil, errors.Errorf("event type `%s` is not impelemted IDomainEvent or can't be instansiated", eventType) + } + + if contentType != s.ContentType() { + return nil, errors.Errorf("contentType: %s is not supported", contentType) + } + + if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { + return nil, errors.WrapIff(err, "error in Unmarshaling: `%s`", eventType) + } + + return targetEventPointer.(domain.IDomainEvent), nil +} + +func (s *DefaultEventJsonSerializer) DeserializeObject( + data []byte, + eventType string, + contentType string, +) (interface{}, error) { + if data == nil { + return nil, nil + } + + targetEventPointer := typeMapper.InstanceByTypeName(eventType) + + if targetEventPointer == nil { + return nil, errors.Errorf("event type `%s` can't be instansiated", eventType) + } + + if contentType != s.ContentType() { + return nil, errors.Errorf("contentType: %s is not supported", contentType) + } + + if err := s.serializer.Unmarshal(data, targetEventPointer); err != nil { + return nil, errors.WrapIff(err, "error in Unmarshaling: `%s`", eventType) + } + + return targetEventPointer, nil +} + +func (s *DefaultEventJsonSerializer) DeserializeType( + data []byte, + eventType reflect.Type, + contentType string, +) (domain.IDomainEvent, error) { + if data == nil { + return nil, nil + } + + // we use event short type name instead of full type name because this event in other receiver packages could have different package name + eventTypeName := typeMapper.GetTypeName(eventType) + + return s.Deserialize(data, eventTypeName, contentType) +} + +func (s *DefaultEventJsonSerializer) ContentType() string { + return "application/json" +} + +func (s *DefaultEventJsonSerializer) Serializer() contratcs.Serializer { + return s.serializer +} diff --git a/internal/pkg/core/serializer/json/json_serializer.go b/internal/pkg/core/serializer/json/json_serializer.go index 7afabcb0..930260f0 100644 --- a/internal/pkg/core/serializer/json/json_serializer.go +++ b/internal/pkg/core/serializer/json/json_serializer.go @@ -3,7 +3,7 @@ package json import ( "log" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" "github.com/TylerBrock/colorjson" "github.com/goccy/go-json" @@ -12,76 +12,77 @@ import ( type jsonSerializer struct{} -func NewDefaultSerializer() serializer.Serializer { +func NewDefaultJsonSerializer() contratcs.Serializer { return &jsonSerializer{} } // https://www.sohamkamani.com/golang/json/#decoding-json-to-maps---unstructured-data // https://developpaper.com/mapstructure-of-go/ // https://github.com/goccy/go-json + func (s *jsonSerializer) Marshal(v interface{}) ([]byte, error) { - return json.Marshal(v) + return Marshal(v) } // Unmarshal is a wrapper around json.Unmarshal. // To unmarshal JSON into an interface value, Unmarshal stores in a map[string]interface{} func (s *jsonSerializer) Unmarshal(data []byte, v interface{}) error { - // https://pkg.go.dev/encoding/json#Unmarshal - err := json.Unmarshal(data, v) - if err != nil { - return err - } - log.Printf("deserialize structure object") - - return nil + return Unmarshal(data, v) } // UnmarshalFromJson is a wrapper around json.Unmarshal. func (s *jsonSerializer) UnmarshalFromJson(data string, v interface{}) error { - err := s.Unmarshal([]byte(data), v) - if err != nil { - return err - } - - return nil + return UnmarshalFromJSON(data, v) } // DecodeWithMapStructure is a wrapper around mapstructure.Decode. // Decode takes an input structure or map[string]interface{} and uses reflection to translate it to the output structure. output must be a pointer to a map or struct. // https://pkg.go.dev/github.com/mitchellh/mapstructure#section-readme -func (s *jsonSerializer) DecodeWithMapStructure(input interface{}, output interface{}) error { - // https://developpaper.com/mapstructure-of-go/ - return mapstructure.Decode(input, output) +func (s *jsonSerializer) DecodeWithMapStructure( + input interface{}, + output interface{}, +) error { + return DecodeWithMapStructure(input, output) } -func (s *jsonSerializer) UnmarshalToMap(data []byte, v *map[string]interface{}) error { - // https://developpaper.com/mapstructure-of-go/ - err := json.Unmarshal(data, v) - if err != nil { - return err - } - return nil +func (s *jsonSerializer) UnmarshalToMap( + data []byte, + v *map[string]interface{}, +) error { + return UnmarshalToMap(data, v) } -func (s *jsonSerializer) UnmarshalToMapFromJson(data string, v *map[string]interface{}) error { - return s.UnmarshalToMap([]byte(data), v) +func (s *jsonSerializer) UnmarshalToMapFromJson( + data string, + v *map[string]interface{}, +) error { + return UnmarshalToMapFromJson(data, v) } // PrettyPrint print input object as a formatted json string func (s *jsonSerializer) PrettyPrint(data interface{}) string { + return PrettyPrint(data) +} + +// ColoredPrettyPrint print input object as a formatted json string with color +func (s *jsonSerializer) ColoredPrettyPrint(data interface{}) string { + return ColoredPrettyPrint(data) +} + +func PrettyPrint(data interface{}) string { // https://gosamples.dev/pretty-print-json/ val, err := json.MarshalIndent(data, "", " ") if err != nil { return "" } + return string(val) } -// ColoredPrettyPrint print input object as a formatted json string with color -func (s *jsonSerializer) ColoredPrettyPrint(data interface{}) string { +func ColoredPrettyPrint(data interface{}) string { // https://github.com/TylerBrock/colorjson var obj map[string]interface{} - err := json.Unmarshal([]byte(s.PrettyPrint(data)), &obj) + err := json.Unmarshal([]byte(PrettyPrint(data)), &obj) if err != nil { return "" } @@ -92,5 +93,65 @@ func (s *jsonSerializer) ColoredPrettyPrint(data interface{}) string { if err != nil { return "" } + return string(val) } + +func Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +// Unmarshal is a wrapper around json.Unmarshal. +// To unmarshal JSON into an interface value, Unmarshal stores in a map[string]interface{} +func Unmarshal(data []byte, v interface{}) error { + // https://pkg.go.dev/encoding/json#Unmarshal + err := json.Unmarshal(data, v) + if err != nil { + return err + } + + log.Printf("deserialize structure object") + + return nil +} + +// UnmarshalFromJSON is a wrapper around json.Unmarshal. +func UnmarshalFromJSON(data string, v interface{}) error { + err := Unmarshal([]byte(data), v) + if err != nil { + return err + } + + return nil +} + +// DecodeWithMapStructure is a wrapper around mapstructure.Decode. +// Decode takes an input structure or map[string]interface{} and uses reflection to translate it to the output structure. output must be a pointer to a map or struct. +// https://pkg.go.dev/github.com/mitchellh/mapstructure#section-readme +func DecodeWithMapStructure( + input interface{}, + output interface{}, +) error { + // https://developpaper.com/mapstructure-of-go/ + return mapstructure.Decode(input, output) +} + +func UnmarshalToMap( + data []byte, + v *map[string]interface{}, +) error { + // https://developpaper.com/mapstructure-of-go/ + err := json.Unmarshal(data, v) + if err != nil { + return err + } + + return nil +} + +func UnmarshalToMapFromJson( + data string, + v *map[string]interface{}, +) error { + return UnmarshalToMap([]byte(data), v) +} diff --git a/internal/pkg/core/serializer/json/json_serilizer_test.go b/internal/pkg/core/serializer/json/json_serilizer_test.go index 6c35f431..d74342bb 100644 --- a/internal/pkg/core/serializer/json/json_serilizer_test.go +++ b/internal/pkg/core/serializer/json/json_serilizer_test.go @@ -1,3 +1,6 @@ +//go:build unit +// +build unit + package json import ( @@ -12,7 +15,7 @@ type person struct { Age int } -var currentSerializer = NewDefaultSerializer() +var currentSerializer = NewDefaultJsonSerializer() func Test_Deserialize_Unstructured_Data_Into_Empty_Interface(t *testing.T) { // https://www.sohamkamani.com/golang/json/#decoding-json-to-maps---unstructured-data @@ -41,7 +44,7 @@ func Test_Deserialize_Unstructured_Data_Into_Empty_Interface(t *testing.T) { assert.True(t, reflect.TypeOf(jsonMap).Kind() == reflect.Map) assert.True(t, reflect.TypeOf(jsonMap) == reflect.TypeOf(map[string]interface{}(nil))) - assert.True(t, jsonMap.(map[string]interface{})["Name"] == "John") + assert.True(t, jsonMap.(map[string]interface{})["ShortTypeName"] == "John") assert.True(t, jsonMap.(map[string]interface{})["Age"] == float64(30)) } @@ -72,7 +75,7 @@ func Test_Deserialize_Unstructured_Data_Into_Map(t *testing.T) { assert.True(t, reflect.TypeOf(jsonMap).Kind() == reflect.Map) assert.True(t, reflect.TypeOf(jsonMap) == reflect.TypeOf(map[string]interface{}(nil))) - assert.True(t, jsonMap["Name"] == "John") + assert.True(t, jsonMap["ShortTypeName"] == "John") assert.True(t, jsonMap["Age"] == float64(30)) } @@ -169,6 +172,6 @@ func Test_Decode_To_Map(t *testing.T) { panic(err) } - assert.True(t, jsonMap["Name"] == "John") + assert.True(t, jsonMap["ShortTypeName"] == "John") assert.True(t, jsonMap["Age"] == float64(30)) } diff --git a/internal/pkg/core/serializer/json/message_serializer.go b/internal/pkg/core/serializer/json/message_serializer.go new file mode 100644 index 00000000..9c612be0 --- /dev/null +++ b/internal/pkg/core/serializer/json/message_serializer.go @@ -0,0 +1,127 @@ +package json + +import ( + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "emperror.dev/errors" +) + +type DefaultMessageJsonSerializer struct { + serializer contratcs.Serializer +} + +func NewDefaultMessageJsonSerializer(serializer contratcs.Serializer) contratcs.MessageSerializer { + return &DefaultMessageJsonSerializer{serializer: serializer} +} + +func (m *DefaultMessageJsonSerializer) Serialize(message types.IMessage) (*contratcs.EventSerializationResult, error) { + return m.SerializeObject(message) +} + +func (m *DefaultMessageJsonSerializer) SerializeObject( + message interface{}, +) (*contratcs.EventSerializationResult, error) { + if message == nil { + return &contratcs.EventSerializationResult{Data: nil, ContentType: m.ContentType()}, nil + } + + // we use message short type name instead of full type name because this message in other receiver packages could have different package name + eventType := typeMapper.GetTypeName(message) + + data, err := m.serializer.Marshal(message) + if err != nil { + return nil, errors.WrapIff(err, "error in Marshaling: `%s`", eventType) + } + + result := &contratcs.EventSerializationResult{Data: data, ContentType: m.ContentType()} + + return result, nil +} + +func (m *DefaultMessageJsonSerializer) SerializeEnvelop( + messageEnvelop types.MessageEnvelope, +) (*contratcs.EventSerializationResult, error) { + // TODO implement me + panic("implement me") +} + +func (m *DefaultMessageJsonSerializer) Deserialize( + data []byte, + messageType string, + contentType string, +) (types.IMessage, error) { + if data == nil { + return nil, nil + } + + targetMessagePointer := typeMapper.EmptyInstanceByTypeNameAndImplementedInterface[types.IMessage]( + messageType, + ) + + if targetMessagePointer == nil { + return nil, errors.Errorf("message type `%s` is not impelemted IMessage or can't be instansiated", messageType) + } + + if contentType != m.ContentType() { + return nil, errors.Errorf("contentType: %s is not supported", contentType) + } + + if err := m.serializer.Unmarshal(data, targetMessagePointer); err != nil { + return nil, errors.WrapIff(err, "error in Unmarshaling: `%s`", messageType) + } + + return targetMessagePointer.(types.IMessage), nil +} + +func (m *DefaultMessageJsonSerializer) DeserializeObject( + data []byte, + messageType string, + contentType string, +) (interface{}, error) { + if data == nil { + return nil, nil + } + + targetMessagePointer := typeMapper.InstanceByTypeName(messageType) + + if targetMessagePointer == nil { + return nil, errors.Errorf("message type `%s` can't be instansiated", messageType) + } + + if contentType != m.ContentType() { + return nil, errors.Errorf("contentType: %s is not supported", contentType) + } + + if err := m.serializer.Unmarshal(data, targetMessagePointer); err != nil { + return nil, errors.WrapIff(err, "error in Unmarshaling: `%s`", messageType) + } + + return targetMessagePointer, nil +} + +func (m *DefaultMessageJsonSerializer) DeserializeType( + data []byte, + messageType reflect.Type, + contentType string, +) (types.IMessage, error) { + if data == nil { + return nil, nil + } + + // we use message short type name instead of full type name because this message in other receiver packages could have different package name + messageTypeName := typeMapper.GetTypeName(messageType) + + return m.Deserialize(data, messageTypeName, contentType) +} + +func (m *DefaultMessageJsonSerializer) ContentType() string { + return "application/json" +} + +func (m *DefaultMessageJsonSerializer) Serializer() contratcs.Serializer { + return m.serializer +} diff --git a/internal/pkg/core/serializer/json/metadata_serializer.go b/internal/pkg/core/serializer/json/metadata_serializer.go new file mode 100644 index 00000000..5f9f648c --- /dev/null +++ b/internal/pkg/core/serializer/json/metadata_serializer.go @@ -0,0 +1,44 @@ +package json + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" + + "emperror.dev/errors" +) + +type DefaultMetadataJsonSerializer struct { + serializer contratcs.Serializer +} + +func NewDefaultMetadataJsonSerializer(serializer contratcs.Serializer) contratcs.MetadataSerializer { + return &DefaultMetadataJsonSerializer{serializer: serializer} +} + +func (s *DefaultMetadataJsonSerializer) Serialize(meta metadata.Metadata) ([]byte, error) { + if meta == nil { + return nil, nil + } + + marshal, err := s.serializer.Marshal(meta) + if err != nil { + return nil, errors.WrapIf(err, "failed to marshal metadata") + } + + return marshal, nil +} + +func (s *DefaultMetadataJsonSerializer) Deserialize(bytes []byte) (metadata.Metadata, error) { + if bytes == nil { + return nil, nil + } + + var meta metadata.Metadata + + err := s.serializer.Unmarshal(bytes, &meta) + if err != nil { + return nil, errors.WrapIf(err, "failed to unmarshal metadata") + } + + return meta, nil +} diff --git a/internal/pkg/core/serializer/metadata_serializer.go b/internal/pkg/core/serializer/metadata_serializer.go deleted file mode 100644 index 4c75ee8a..00000000 --- a/internal/pkg/core/serializer/metadata_serializer.go +++ /dev/null @@ -1,48 +0,0 @@ -package serializer - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - - "emperror.dev/errors" -) - -type MetadataSerializer interface { - Serialize(meta metadata.Metadata) ([]byte, error) - Deserialize(bytes []byte) (metadata.Metadata, error) -} - -type DefaultMetadataSerializer struct { - serializer Serializer -} - -func NewDefaultMetadataSerializer(serializer Serializer) MetadataSerializer { - return &DefaultMetadataSerializer{serializer: serializer} -} - -func (s *DefaultMetadataSerializer) Serialize(meta metadata.Metadata) ([]byte, error) { - if meta == nil { - return nil, nil - } - - marshal, err := s.serializer.Marshal(meta) - if err != nil { - return nil, errors.WrapIf(err, "failed to marshal metadata") - } - - return marshal, nil -} - -func (s *DefaultMetadataSerializer) Deserialize(bytes []byte) (metadata.Metadata, error) { - if bytes == nil { - return nil, nil - } - - var meta metadata.Metadata - - err := s.serializer.Unmarshal(bytes, &meta) - if err != nil { - return nil, errors.WrapIf(err, "failed to unmarshal metadata") - } - - return meta, nil -} diff --git a/internal/pkg/core/utils/utils.go b/internal/pkg/core/utils/utils.go index f57bde76..fe90fc73 100644 --- a/internal/pkg/core/utils/utils.go +++ b/internal/pkg/core/utils/utils.go @@ -5,7 +5,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/events" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/ahmetb/go-linq/v3" ) diff --git a/internal/pkg/web/route/endpoint.go b/internal/pkg/core/web/route/endpoint.go similarity index 100% rename from internal/pkg/web/route/endpoint.go rename to internal/pkg/core/web/route/endpoint.go diff --git a/internal/pkg/web/route/helpers.go b/internal/pkg/core/web/route/helpers.go similarity index 100% rename from internal/pkg/web/route/helpers.go rename to internal/pkg/core/web/route/helpers.go diff --git a/internal/pkg/elasticsearch/elastic_options.go b/internal/pkg/elasticsearch/elastic_options.go index 6ccf3080..7fa66b86 100644 --- a/internal/pkg/elasticsearch/elastic_options.go +++ b/internal/pkg/elasticsearch/elastic_options.go @@ -2,18 +2,18 @@ package elasticsearch import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[ElasticOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[ElasticOptions]()) type ElasticOptions struct { URL string `mapstructure:"url"` } -func provideConfig(environment environemnt.Environment) (*ElasticOptions, error) { +func provideConfig(environment environment.Environment) (*ElasticOptions, error) { return config.BindConfigKey[*ElasticOptions](optionName, environment) } diff --git a/internal/pkg/es/errors/errors.go b/internal/pkg/es/errors/errors.go index 4a2980c1..e431daf4 100644 --- a/internal/pkg/es/errors/errors.go +++ b/internal/pkg/es/errors/errors.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/aggregate_store.go b/internal/pkg/eventstroredb/aggregate_store.go index 657b7610..ccedc555 100644 --- a/internal/pkg/eventstroredb/aggregate_store.go +++ b/internal/pkg/eventstroredb/aggregate_store.go @@ -15,9 +15,9 @@ import ( expectedStreamVersion "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/stream_version" esErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/errors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/EventStore/EventStore-Client-Go/esdb" @@ -55,7 +55,9 @@ func (a *esdbAggregateStore[T]) StoreWithVersion( ctx context.Context, ) (*appendResult.AppendEventsResult, error) { ctx, span := a.tracer.Start(ctx, "esdbAggregateStore.StoreWithVersion") - span.SetAttributes(attribute2.String("AggregateID", aggregate.Id().String())) + span.SetAttributes( + attribute2.String("AggregateID", aggregate.Id().String()), + ) defer span.End() if len(aggregate.UncommittedEvents()) == 0 { @@ -97,7 +99,7 @@ func (a *esdbAggregateStore[T]) StoreWithVersion( ctx, ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIff( err, @@ -130,11 +132,18 @@ func (a *esdbAggregateStore[T]) Store( ctx, span := a.tracer.Start(ctx, "esdbAggregateStore.Store") defer span.End() - expectedVersion := expectedStreamVersion.FromInt64(aggregate.OriginalVersion()) + expectedVersion := expectedStreamVersion.FromInt64( + aggregate.OriginalVersion(), + ) - streamAppendResult, err := a.StoreWithVersion(aggregate, metadata, expectedVersion, ctx) + streamAppendResult, err := a.StoreWithVersion( + aggregate, + metadata, + expectedVersion, + ctx, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIff( err, @@ -147,7 +156,10 @@ func (a *esdbAggregateStore[T]) Store( return streamAppendResult, nil } -func (a *esdbAggregateStore[T]) Load(ctx context.Context, aggregateId uuid.UUID) (T, error) { +func (a *esdbAggregateStore[T]) Load( + ctx context.Context, + aggregateId uuid.UUID, +) (T, error) { ctx, span := a.tracer.Start(ctx, "esdbAggregateStore.Load") defer span.End() @@ -181,7 +193,7 @@ func (a *esdbAggregateStore[T]) LoadWithReadPosition( method := reflect.ValueOf(aggregate).MethodByName("NewEmptyAggregate") if !method.IsValid() { - return *new(T), tracing.TraceErrFromSpan( + return *new(T), utils.TraceErrStatusFromSpan( span, errors.New( "[esdbAggregateStore_LoadWithReadPosition:MethodByName] aggregate does not have a `NewEmptyAggregate` method", @@ -196,7 +208,7 @@ func (a *esdbAggregateStore[T]) LoadWithReadPosition( streamEvents, err := a.getStreamEvents(streamId, position, ctx) if errors.Is(err, esdb.ErrStreamNotFound) || len(streamEvents) == 0 { - return *new(T), tracing.TraceErrFromSpan( + return *new(T), utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewAggregateNotFoundError(err, aggregateId), @@ -204,8 +216,9 @@ func (a *esdbAggregateStore[T]) LoadWithReadPosition( ), ) } + if err != nil { - return *new(T), tracing.TraceErrFromSpan( + return *new(T), utils.TraceErrStatusFromSpan( span, errors.WrapIff( err, @@ -228,20 +241,25 @@ func (a *esdbAggregateStore[T]) LoadWithReadPosition( err = aggregate.LoadFromHistory(domainEvents, meta) if err != nil { - return *new(T), tracing.TraceErrFromSpan(span, err) + return *new(T), utils.TraceStatusFromSpan(span, err) } - a.log.Infow(fmt.Sprintf("Loaded aggregate with streamId {%s} and aggregateId {%s}", - streamId.String(), - aggregateId.String()), - logger.Fields{"Aggregate": aggregate, "StreamId": streamId.String()}) + a.log.Infow( + fmt.Sprintf("Loaded aggregate with streamId {%s} and aggregateId {%s}", + streamId.String(), + aggregateId.String()), + logger.Fields{"Aggregate": aggregate, "StreamId": streamId.String()}, + ) span.SetAttributes(attribute.Object("Aggregate", aggregate)) return aggregate, nil } -func (a *esdbAggregateStore[T]) Exists(ctx context.Context, aggregateId uuid.UUID) (bool, error) { +func (a *esdbAggregateStore[T]) Exists( + ctx context.Context, + aggregateId uuid.UUID, +) (bool, error) { ctx, span := a.tracer.Start(ctx, "esdbAggregateStore.Exists") span.SetAttributes(attribute2.String("AggregateID", aggregateId.String())) defer span.End() @@ -261,7 +279,12 @@ func (a *esdbAggregateStore[T]) getStreamEvents( var streamEvents []*models.StreamEvent for true { - events, err := a.eventStore.ReadEvents(streamId, position, uint64(pageSize), ctx) + events, err := a.eventStore.ReadEvents( + streamId, + position, + uint64(pageSize), + ctx, + ) if err != nil { return nil, errors.WrapIff( err, diff --git a/internal/pkg/eventstroredb/config/eventstoredb_options.go b/internal/pkg/eventstroredb/config/eventstoredb_options.go index ca1e9a71..3ee031a0 100644 --- a/internal/pkg/eventstroredb/config/eventstoredb_options.go +++ b/internal/pkg/eventstroredb/config/eventstoredb_options.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) @@ -49,7 +49,7 @@ type Subscription struct { SubscriptionId string `mapstructure:"subscriptionId" validate:"required"` } -func ProvideConfig(environment environemnt.Environment) (*EventStoreDbOptions, error) { - optionName := strcase.ToLowerCamel(typeMapper.GetTypeNameByT[EventStoreDbOptions]()) +func ProvideConfig(environment environment.Environment) (*EventStoreDbOptions, error) { + optionName := strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[EventStoreDbOptions]()) return config.BindConfigKey[*EventStoreDbOptions](optionName, environment) } diff --git a/internal/pkg/eventstroredb/errors/aggregate_not_found_error.go b/internal/pkg/eventstroredb/errors/aggregate_not_found_error.go index 54f4da05..2d8b089d 100644 --- a/internal/pkg/eventstroredb/errors/aggregate_not_found_error.go +++ b/internal/pkg/eventstroredb/errors/aggregate_not_found_error.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" uuid "github.com/satori/go.uuid" diff --git a/internal/pkg/eventstroredb/errors/append_to_stream_error.go b/internal/pkg/eventstroredb/errors/append_to_stream_error.go index bb40ca1f..060b3a16 100644 --- a/internal/pkg/eventstroredb/errors/append_to_stream_error.go +++ b/internal/pkg/eventstroredb/errors/append_to_stream_error.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/errors/delete_stream_error.go b/internal/pkg/eventstroredb/errors/delete_stream_error.go index 5e840f62..8d93cd58 100644 --- a/internal/pkg/eventstroredb/errors/delete_stream_error.go +++ b/internal/pkg/eventstroredb/errors/delete_stream_error.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/errors/read_stream_error.go b/internal/pkg/eventstroredb/errors/read_stream_error.go index 3ea35e12..68d0b7cb 100644 --- a/internal/pkg/eventstroredb/errors/read_stream_error.go +++ b/internal/pkg/eventstroredb/errors/read_stream_error.go @@ -1,7 +1,7 @@ package errors import ( - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/errors/stream_not_found_error.go b/internal/pkg/eventstroredb/errors/stream_not_found_error.go index e9d2d45c..d89d42a2 100644 --- a/internal/pkg/eventstroredb/errors/stream_not_found_error.go +++ b/internal/pkg/eventstroredb/errors/stream_not_found_error.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/errors/truncate_stream_error.go b/internal/pkg/eventstroredb/errors/truncate_stream_error.go index 479f7a39..13c8edc8 100644 --- a/internal/pkg/eventstroredb/errors/truncate_stream_error.go +++ b/internal/pkg/eventstroredb/errors/truncate_stream_error.go @@ -3,7 +3,7 @@ package errors import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) diff --git a/internal/pkg/eventstroredb/esdb_serilizer.go b/internal/pkg/eventstroredb/esdb_serilizer.go index 6e955dc0..4714db3b 100644 --- a/internal/pkg/eventstroredb/esdb_serilizer.go +++ b/internal/pkg/eventstroredb/esdb_serilizer.go @@ -6,14 +6,14 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models" appendResult "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/append_result" readPosition "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/stream_position/read_position" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/stream_position/truncatePosition" expectedStreamVersion "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/stream_version" esErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/errors" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/EventStore/EventStore-Client-Go/esdb" @@ -23,13 +23,13 @@ import ( ) type EsdbSerializer struct { - metadataSerializer serializer.MetadataSerializer - eventSerializer serializer.EventSerializer + metadataSerializer contratcs.MetadataSerializer + eventSerializer contratcs.EventSerializer } func NewEsdbSerializer( - metadataSerializer serializer.MetadataSerializer, - eventSerializer serializer.EventSerializer, + metadataSerializer contratcs.MetadataSerializer, + eventSerializer contratcs.EventSerializer, ) *EsdbSerializer { return &EsdbSerializer{ metadataSerializer: metadataSerializer, @@ -139,7 +139,7 @@ func (e *EsdbSerializer) EsdbPositionToStreamReadPosition( func (e *EsdbSerializer) ResolvedEventToStreamEvent( resolveEvent *esdb.ResolvedEvent, ) (*models.StreamEvent, error) { - deserializedEvent, err := e.eventSerializer.DeserializeEvent( + deserializedEvent, err := e.eventSerializer.Deserialize( resolveEvent.Event.Data, resolveEvent.Event.EventType, resolveEvent.Event.ContentType, @@ -160,7 +160,7 @@ func (e *EsdbSerializer) ResolvedEventToStreamEvent( return &models.StreamEvent{ EventID: id, - Event: deserializedEvent.(domain.IDomainEvent), + Event: deserializedEvent, Metadata: deserializedMeta, Version: int64(resolveEvent.Event.EventNumber), Position: e.EsdbPositionToStreamReadPosition(resolveEvent.OriginalEvent().Position).Value(), @@ -192,7 +192,7 @@ func (e *EsdbSerializer) EsdbWriteResultToAppendEventResult( } func (e *EsdbSerializer) Serialize( - data interface{}, + data domain.IDomainEvent, meta metadata.Metadata, ) (*esdb.EventData, error) { serializedData, err := e.eventSerializer.Serialize(data) @@ -215,14 +215,62 @@ func (e *EsdbSerializer) Serialize( }, nil } +func (e *EsdbSerializer) SerializeObject( + data interface{}, + meta metadata.Metadata, +) (*esdb.EventData, error) { + serializedData, err := e.eventSerializer.SerializeObject(data) + if err != nil { + return nil, err + } + + serializedMeta, err := e.metadataSerializer.Serialize(meta) + if err != nil { + return nil, err + } + + id, err := uuid.NewV4() + return &esdb.EventData{ + EventID: id, + EventType: typeMapper.GetTypeName(data), + Data: serializedData.Data, + ContentType: esdb.JsonContentType, + Metadata: serializedMeta, + }, nil +} + func (e *EsdbSerializer) Deserialize( resolveEvent *esdb.ResolvedEvent, +) (domain.IDomainEvent, metadata.Metadata, error) { + eventType := resolveEvent.Event.EventType + data := resolveEvent.Event.Data + userMeta := resolveEvent.Event.UserMetadata + + payload, err := e.eventSerializer.Deserialize( + data, + eventType, + resolveEvent.Event.ContentType, + ) + if err != nil { + return nil, nil, err + } + + meta, err := e.metadataSerializer.Deserialize(userMeta) + if err != nil { + return nil, nil, err + } + + return payload, meta, nil +} + +func (e *EsdbSerializer) DeserializeObject( + resolveEvent *esdb.ResolvedEvent, ) (interface{}, metadata.Metadata, error) { eventType := resolveEvent.Event.EventType data := resolveEvent.Event.Data userMeta := resolveEvent.Event.UserMetadata - payload, err := e.eventSerializer.DeserializeEvent( + payload, err := e.eventSerializer.Deserialize( data, eventType, resolveEvent.Event.ContentType, diff --git a/internal/pkg/eventstroredb/esdb_subscription_check_point_repository.go b/internal/pkg/eventstroredb/esdb_subscription_check_point_repository.go index 5293ebc8..1611c29c 100644 --- a/internal/pkg/eventstroredb/esdb_subscription_check_point_repository.go +++ b/internal/pkg/eventstroredb/esdb_subscription_check_point_repository.go @@ -9,7 +9,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/events" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/EventStore/EventStore-Client-Go/esdb" @@ -71,7 +71,7 @@ func (e *esdbSubscriptionCheckpointRepository) Load( return 0, errors.WrapIf(err, "stream.Recv") } - deserialized, _, err := e.esdbSerilizer.Deserialize(event) + deserialized, _, err := e.esdbSerilizer.DeserializeObject(event) if err != nil { return 0, err } @@ -98,7 +98,7 @@ func (e *esdbSubscriptionCheckpointRepository) Store( Event: events.NewEvent(typeMapper.GetTypeName(&CheckpointStored{})), } streamName := getCheckpointStreamName(subscriptionId) - eventData, err := e.esdbSerilizer.Serialize(checkpoint, nil) + eventData, err := e.esdbSerilizer.SerializeObject(checkpoint, nil) if err != nil { return errors.WrapIf(err, "esdbSerilizer.Serialize") } diff --git a/internal/pkg/eventstroredb/event_store.go b/internal/pkg/eventstroredb/event_store.go index b83fd283..d6bf7049 100644 --- a/internal/pkg/eventstroredb/event_store.go +++ b/internal/pkg/eventstroredb/event_store.go @@ -14,8 +14,8 @@ import ( expectedStreamVersion "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models/stream_version" esErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/errors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "emperror.dev/errors" "github.com/EventStore/EventStore-Client-Go/esdb" @@ -39,7 +39,12 @@ func NewEventStoreDbEventStore( serializer *EsdbSerializer, tracer trace.Tracer, ) store.EventStore { - return &eventStoreDbEventStore{log: log, client: client, serializer: serializer, tracer: tracer} + return &eventStoreDbEventStore{ + log: log, + client: client, + serializer: serializer, + tracer: tracer, + } } func (e *eventStoreDbEventStore) StreamExists( @@ -59,11 +64,11 @@ func (e *eventStoreDbEventStore) StreamExists( }, 1) if err != nil { - return false, tracing.TraceErrFromSpan( + return false, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewReadStreamError(err), - "[eventStoreDbEventStore_StreamExists:ReadStream] error in reading stream", + "error in reading stream", ), ) } @@ -104,22 +109,27 @@ func (e *eventStoreDbEventStore) AppendEvents( }, eventsData...) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewAppendToStreamError(err, streamName.String()), - "[eventStoreDbEventStore_AppendEvents:AppendToStream] error in appending to stream", + "error in appending to stream", ), ) } appendEventsResult = e.serializer.EsdbWriteResultToAppendEventResult(res) - span.SetAttributes(attribute.Object("AppendEventsResult", appendEventsResult)) + span.SetAttributes( + attribute.Object("AppendEventsResult", appendEventsResult), + ) e.log.Infow( - "[eventStoreDbEventStore_AppendEvents] events append to stream successfully", - logger.Fields{"AppendEventsResult": appendEventsResult, "StreamId": streamName.String()}, + "events append to stream successfully", + logger.Fields{ + "AppendEventsResult": appendEventsResult, + "StreamId": streamName.String(), + }, ) return appendEventsResult, nil @@ -141,11 +151,11 @@ func (e *eventStoreDbEventStore) AppendNewEvents( ctx, ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewAppendToStreamError(err, streamName.String()), - "[eventStoreDbEventStore_AppendNewEvents:AppendEvents] error in appending to stream", + "error in appending to stream", ), ) } @@ -153,8 +163,11 @@ func (e *eventStoreDbEventStore) AppendNewEvents( span.SetAttributes(attribute.Object("AppendNewEvents", appendEventsResult)) e.log.Infow( - "[eventStoreDbEventStore_AppendNewEvents] events append to stream successfully", - logger.Fields{"AppendEventsResult": appendEventsResult, "StreamId": streamName.String()}, + "events append to stream successfully", + logger.Fields{ + "AppendEventsResult": appendEventsResult, + "StreamId": streamName.String(), + }, ) return appendEventsResult, nil @@ -174,41 +187,45 @@ func (e *eventStoreDbEventStore) ReadEvents( ctx, streamName.String(), esdb.ReadStreamOptions{ - Direction: esdb.Forwards, - From: e.serializer.StreamReadPositionToStreamPosition(readPosition), + Direction: esdb.Forwards, + From: e.serializer.StreamReadPositionToStreamPosition( + readPosition, + ), ResolveLinkTos: true, }, count) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewReadStreamError(err), - "[eventStoreDbEventStore_ReadEvents:ReadStream] error in reading stream", + "error in reading stream", ), ) } defer readStream.Close() - resolvedEvents, err := e.serializer.EsdbReadStreamToResolvedEvents(readStream) + resolvedEvents, err := e.serializer.EsdbReadStreamToResolvedEvents( + readStream, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[eventStoreDbEventStore_ReadEvents.EsdbReadStreamToResolvedEvents] error in converting to resolved events", + "error in converting to resolved events", ), ) } events, err := e.serializer.ResolvedEventsToStreamEvents(resolvedEvents) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[eventStoreDbEventStore_ReadEvents.ResolvedEventsToStreamEvents] error in converting to stream events", + "error in converting to stream events", ), ) } @@ -221,7 +238,10 @@ func (e *eventStoreDbEventStore) ReadEventsWithMaxCount( readPosition readPosition.StreamReadPosition, ctx context.Context, ) ([]*models.StreamEvent, error) { - ctx, span := e.tracer.Start(ctx, "eventStoreDbEventStore.ReadEventsWithMaxCount") + ctx, span := e.tracer.Start( + ctx, + "eventStoreDbEventStore.ReadEventsWithMaxCount", + ) span.SetAttributes(attribute2.String("StreamName", streamName.String())) defer span.End() @@ -233,7 +253,10 @@ func (e *eventStoreDbEventStore) ReadEventsFromStart( count uint64, ctx context.Context, ) ([]*models.StreamEvent, error) { - ctx, span := e.tracer.Start(ctx, "eventStoreDbEventStore.ReadEventsFromStart") + ctx, span := e.tracer.Start( + ctx, + "eventStoreDbEventStore.ReadEventsFromStart", + ) span.SetAttributes(attribute2.String("StreamName", streamName.String())) defer span.End() @@ -246,7 +269,10 @@ func (e *eventStoreDbEventStore) ReadEventsBackwards( count uint64, ctx context.Context, ) ([]*models.StreamEvent, error) { - ctx, span := e.tracer.Start(ctx, "eventStoreDbEventStore.ReadEventsBackwards") + ctx, span := e.tracer.Start( + ctx, + "eventStoreDbEventStore.ReadEventsBackwards", + ) span.SetAttributes(attribute2.String("StreamName", streamName.String())) defer span.End() @@ -254,13 +280,15 @@ func (e *eventStoreDbEventStore) ReadEventsBackwards( ctx, streamName.String(), esdb.ReadStreamOptions{ - Direction: esdb.Backwards, - From: e.serializer.StreamReadPositionToStreamPosition(readPosition), + Direction: esdb.Backwards, + From: e.serializer.StreamReadPositionToStreamPosition( + readPosition, + ), ResolveLinkTos: true, }, count) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewReadStreamError(err), @@ -271,9 +299,11 @@ func (e *eventStoreDbEventStore) ReadEventsBackwards( defer readStream.Close() - resolvedEvents, err := e.serializer.EsdbReadStreamToResolvedEvents(readStream) + resolvedEvents, err := e.serializer.EsdbReadStreamToResolvedEvents( + readStream, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, @@ -284,7 +314,7 @@ func (e *eventStoreDbEventStore) ReadEventsBackwards( events, err := e.serializer.ResolvedEventsToStreamEvents(resolvedEvents) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, @@ -301,11 +331,19 @@ func (e *eventStoreDbEventStore) ReadEventsBackwardsWithMaxCount( readPosition readPosition.StreamReadPosition, ctx context.Context, ) ([]*models.StreamEvent, error) { - ctx, span := e.tracer.Start(ctx, "eventStoreDbEventStore.ReadEventsBackwardsWithMaxCount") + ctx, span := e.tracer.Start( + ctx, + "eventStoreDbEventStore.ReadEventsBackwardsWithMaxCount", + ) span.SetAttributes(attribute2.String("StreamName", streamName.String())) defer span.End() - return e.ReadEventsBackwards(streamName, readPosition, uint64(math.MaxUint64), ctx) + return e.ReadEventsBackwards( + streamName, + readPosition, + uint64(math.MaxUint64), + ctx, + ) } func (e *eventStoreDbEventStore) ReadEventsBackwardsFromEnd( @@ -313,7 +351,10 @@ func (e *eventStoreDbEventStore) ReadEventsBackwardsFromEnd( count uint64, ctx context.Context, ) ([]*models.StreamEvent, error) { - ctx, span := e.tracer.Start(ctx, "eventStoreDbEventStore.ReadEventsBackwardsWithMaxCount") + ctx, span := e.tracer.Start( + ctx, + "eventStoreDbEventStore.ReadEventsBackwardsWithMaxCount", + ) span.SetAttributes(attribute2.String("StreamName", streamName.String())) defer span.End() @@ -331,7 +372,9 @@ func (e *eventStoreDbEventStore) TruncateStream( defer span.End() streamMetadata := esdb.StreamMetadata{} - streamMetadata.SetTruncateBefore(e.serializer.StreamTruncatePositionToInt64(truncatePosition)) + streamMetadata.SetTruncateBefore( + e.serializer.StreamTruncatePositionToInt64(truncatePosition), + ) writeResult, err := e.client.SetStreamMetadata( ctx, streamName.String(), @@ -342,11 +385,11 @@ func (e *eventStoreDbEventStore) TruncateStream( }, streamMetadata) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewTruncateStreamError(err, streamName.String()), - "[eventStoreDbEventStore_TruncateStream:SetStreamMetadata] error in truncating stream", + "error in truncating stream", ), ) } @@ -355,10 +398,13 @@ func (e *eventStoreDbEventStore) TruncateStream( e.log.Infow( fmt.Sprintf( - "[eventStoreDbEventStore.TruncateStream] stream with id %s truncated successfully", + "stream with id %s truncated successfully", streamName.String(), ), - logger.Fields{"WriteResult": writeResult, "StreamId": streamName.String()}, + logger.Fields{ + "WriteResult": writeResult, + "StreamId": streamName.String(), + }, ) return e.serializer.EsdbWriteResultToAppendEventResult(writeResult), nil @@ -382,11 +428,11 @@ func (e *eventStoreDbEventStore) DeleteStream( ), }) if err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrStatusFromSpan( span, errors.WithMessage( esErrors.NewDeleteStreamError(err, streamName.String()), - "[eventStoreDbEventStore_DeleteStream:DeleteStream] error in deleting stream", + "error in deleting stream", ), ) } @@ -395,10 +441,13 @@ func (e *eventStoreDbEventStore) DeleteStream( e.log.Infow( fmt.Sprintf( - "[eventStoreDbEventStore.DeleteStream] stream with id %s deleted successfully", + "stream with id %s deleted successfully", streamName.String(), ), - logger.Fields{"DeleteResult": deleteResult, "StreamId": streamName.String()}, + logger.Fields{ + "DeleteResult": deleteResult, + "StreamId": streamName.String(), + }, ) return nil diff --git a/internal/pkg/eventstroredb/subscription_all_worker.go b/internal/pkg/eventstroredb/subscription_all_worker.go index c5382a44..32638336 100644 --- a/internal/pkg/eventstroredb/subscription_all_worker.go +++ b/internal/pkg/eventstroredb/subscription_all_worker.go @@ -10,7 +10,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts/projection" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/EventStore/EventStore-Client-Go/esdb" diff --git a/internal/pkg/fxapp/application.go b/internal/pkg/fxapp/application.go index 8cdcc063..113f1c5d 100644 --- a/internal/pkg/fxapp/application.go +++ b/internal/pkg/fxapp/application.go @@ -3,7 +3,7 @@ package fxapp import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" @@ -17,7 +17,7 @@ type application struct { options []fx.Option logger logger.Logger fxapp *fx.App - environment environemnt.Environment + environment environment.Environment } func NewApplication( @@ -25,7 +25,7 @@ func NewApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - env environemnt.Environment, + env environment.Environment, ) contracts.Application { return &application{ provides: providers, @@ -109,6 +109,6 @@ func (a *application) Logger() logger.Logger { return a.logger } -func (a *application) Environment() environemnt.Environment { +func (a *application) Environment() environment.Environment { return a.environment } diff --git a/internal/pkg/fxapp/application_builder.go b/internal/pkg/fxapp/application_builder.go index af0ca4c8..276247e0 100644 --- a/internal/pkg/fxapp/application_builder.go +++ b/internal/pkg/fxapp/application_builder.go @@ -1,7 +1,7 @@ package fxapp import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" loggerConfig "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/config" @@ -17,11 +17,11 @@ type applicationBuilder struct { decorates []interface{} options []fx.Option logger logger.Logger - environment environemnt.Environment + environment environment.Environment } -func NewApplicationBuilder(environments ...environemnt.Environment) contracts.ApplicationBuilder { - env := environemnt.ConfigAppEnv(environments...) +func NewApplicationBuilder(environments ...environment.Environment) contracts.ApplicationBuilder { + env := environment.ConfigAppEnv(environments...) var logger logger.Logger logoption, err := loggerConfig.ProvideLogConfig(env) @@ -70,6 +70,6 @@ func (a *applicationBuilder) Logger() logger.Logger { return a.logger } -func (a *applicationBuilder) Environment() environemnt.Environment { +func (a *applicationBuilder) Environment() environment.Environment { return a.environment } diff --git a/internal/pkg/fxapp/contracts/application.go b/internal/pkg/fxapp/contracts/application.go index e4036b6f..06ffd61a 100644 --- a/internal/pkg/fxapp/contracts/application.go +++ b/internal/pkg/fxapp/contracts/application.go @@ -3,7 +3,7 @@ package contracts import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "go.uber.org/fx" @@ -17,5 +17,5 @@ type Application interface { Stop(ctx context.Context) error Wait() <-chan fx.ShutdownSignal Logger() logger.Logger - Environment() environemnt.Environment + Environment() environment.Environment } diff --git a/internal/pkg/fxapp/contracts/application_builder.go b/internal/pkg/fxapp/contracts/application_builder.go index c138a8a2..279d2458 100644 --- a/internal/pkg/fxapp/contracts/application_builder.go +++ b/internal/pkg/fxapp/contracts/application_builder.go @@ -1,7 +1,7 @@ package contracts import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "go.uber.org/fx" @@ -19,5 +19,5 @@ type ApplicationBuilder interface { GetDecorates() []interface{} Options() []fx.Option Logger() logger.Logger - Environment() environemnt.Environment + Environment() environment.Environment } diff --git a/internal/pkg/fxapp/test/test_app_fx.go b/internal/pkg/fxapp/test/test_app_fx.go index 74b61770..02ec7313 100644 --- a/internal/pkg/fxapp/test/test_app_fx.go +++ b/internal/pkg/fxapp/test/test_app_fx.go @@ -4,7 +4,7 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" @@ -19,7 +19,7 @@ func CreateFxTestApp( invokes []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *fxtest.App { var opts []fx.Option diff --git a/internal/pkg/fxapp/test/test_application.go b/internal/pkg/fxapp/test/test_application.go index 706de421..b76a4060 100644 --- a/internal/pkg/fxapp/test/test_application.go +++ b/internal/pkg/fxapp/test/test_application.go @@ -2,14 +2,11 @@ package test import ( "context" - "os" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/spf13/viper" "go.uber.org/fx" "go.uber.org/fx/fxtest" ) @@ -20,7 +17,7 @@ type testApplication struct { options []fx.Option invokes []interface{} logger logger.Logger - env environemnt.Environment + env environment.Environment tb fxtest.TB fxtestApp *fxtest.App } @@ -29,7 +26,7 @@ func (a *testApplication) Logger() logger.Logger { return a.logger } -func (a *testApplication) Environment() environemnt.Environment { +func (a *testApplication) Environment() environment.Environment { return a.env } @@ -39,7 +36,7 @@ func NewTestApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - env environemnt.Environment, + env environment.Environment, ) contracts.Application { return &testApplication{ tb: tb, @@ -94,7 +91,7 @@ func (a *testApplication) Wait() <-chan fx.ShutdownSignal { } func (a *testApplication) createFxTest() *fxtest.App { - a.fixTestEnvironmentWorkingDirectory() + // a.fixTestEnvironmentWorkingDirectory() // build phase of container will do in this stage, containing provides and invokes but app not started yet and will be started in the future with `fxApp.Register` fxTestApp := CreateFxTestApp( @@ -111,15 +108,15 @@ func (a *testApplication) createFxTest() *fxtest.App { return fxTestApp } -func (a *testApplication) fixTestEnvironmentWorkingDirectory() { - currentWD, _ := os.Getwd() - a.logger.Infof("Current test working directory is: %s", currentWD) - - rootDir := viper.GetString(constants.AppRootPath) - if rootDir != "" { - _ = os.Chdir(rootDir) - - newWD, _ := os.Getwd() - a.logger.Infof("New test working directory is: %s", newWD) - } -} +//func (a *testApplication) fixTestEnvironmentWorkingDirectory() { +// currentWD, _ := os.Getwd() +// a.logger.Infof("Current test working directory is: %s", currentWD) +// +// rootDir := viper.GetString(constants.AppRootPath) +// if rootDir != "" { +// _ = os.Chdir(rootDir) +// +// newWD, _ := os.Getwd() +// a.logger.Infof("New test working directory is: %s", newWD) +// } +//} diff --git a/internal/pkg/fxapp/test/test_application_builder.go b/internal/pkg/fxapp/test/test_application_builder.go index 5213caa2..05327e3d 100644 --- a/internal/pkg/fxapp/test/test_application_builder.go +++ b/internal/pkg/fxapp/test/test_application_builder.go @@ -1,7 +1,7 @@ package test import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" @@ -16,7 +16,7 @@ type TestApplicationBuilder struct { func NewTestApplicationBuilder(tb fxtest.TB) *TestApplicationBuilder { return &TestApplicationBuilder{ TB: tb, - ApplicationBuilder: fxapp.NewApplicationBuilder(environemnt.Test), + ApplicationBuilder: fxapp.NewApplicationBuilder(environment.Test), } } @@ -27,7 +27,7 @@ func (a *TestApplicationBuilder) Build() contracts.Application { a.GetDecorates(), a.Options(), a.Logger(), - environemnt.Test, + environment.Test, ) return app diff --git a/internal/pkg/go.mod b/internal/pkg/go.mod index ffb1bcb7..c664b8e9 100644 --- a/internal/pkg/go.mod +++ b/internal/pkg/go.mod @@ -10,13 +10,15 @@ require ( github.com/ahmetb/go-linq/v3 v3.2.0 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/avast/retry-go v3.0.0+incompatible + github.com/brianvoe/gofakeit/v6 v6.25.0 github.com/caarlos0/env/v8 v8.0.0 - github.com/docker/docker v24.0.5+incompatible + github.com/docker/docker v24.0.6+incompatible github.com/docker/go-connections v0.4.0 github.com/doug-martin/goqu/v9 v9.18.0 - github.com/elastic/go-elasticsearch/v8 v8.9.0 + github.com/elastic/go-elasticsearch/v8 v8.10.0 + github.com/glebarez/sqlite v1.10.0 github.com/go-playground/validator v9.31.0+incompatible - github.com/go-resty/resty/v2 v2.7.0 + github.com/go-resty/resty/v2 v2.9.1 github.com/go-testfixtures/testfixtures/v3 v3.9.0 github.com/goccy/go-json v0.10.2 github.com/goccy/go-reflect v1.2.0 @@ -33,83 +35,100 @@ require ( github.com/labstack/echo/v4 v4.11.1 github.com/lib/pq v1.10.9 github.com/mcuadros/go-defaults v1.2.0 - github.com/mehdihadeli/go-mediatr v1.1.10 + github.com/mehdihadeli/go-mediatr v1.3.0 github.com/michaelklishin/rabbit-hole v1.5.0 github.com/mitchellh/mapstructure v1.5.0 github.com/nolleh/caption_json_formatter v0.2.2 + github.com/onsi/ginkgo/v2 v2.12.1 + github.com/onsi/gomega v1.28.0 github.com/orlangure/gnomock v0.30.0 github.com/ory/dockertest/v3 v3.10.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pressly/goose/v3 v3.15.0 - github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_golang v1.17.0 github.com/rabbitmq/amqp091-go v1.8.1 github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 - github.com/redis/go-redis/v9 v9.0.5 + github.com/redis/go-redis/v9 v9.2.1 github.com/samber/lo v1.38.1 github.com/satori/go.uuid v1.2.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.4 - github.com/testcontainers/testcontainers-go v0.23.0 - github.com/testcontainers/testcontainers-go/modules/postgres v0.23.0 - github.com/uptrace/bun v1.1.14 - github.com/uptrace/bun/dialect/pgdialect v1.1.14 - github.com/uptrace/bun/driver/pgdriver v1.1.14 + github.com/testcontainers/testcontainers-go v0.25.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.25.0 + github.com/ulule/limiter/v3 v3.11.2 + github.com/uptrace/bun v1.1.16 + github.com/uptrace/bun/dialect/pgdialect v1.1.16 + github.com/uptrace/bun/driver/pgdriver v1.1.16 + github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 + github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 go.mongodb.org/mongo-driver v1.12.1 - go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 - go.opentelemetry.io/contrib/propagators/ot v1.18.0 - go.opentelemetry.io/otel v1.17.0 - go.opentelemetry.io/otel/exporters/jaeger v1.17.0 - go.opentelemetry.io/otel/exporters/prometheus v0.40.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 - go.opentelemetry.io/otel/exporters/zipkin v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 - go.opentelemetry.io/otel/sdk v1.17.0 - go.opentelemetry.io/otel/sdk/metric v0.40.0 - go.opentelemetry.io/otel/trace v1.17.0 + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 + go.opentelemetry.io/contrib/instrumentation/host v0.45.0 + go.opentelemetry.io/contrib/propagators/ot v1.20.0 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 + go.opentelemetry.io/otel/exporters/zipkin v1.19.0 + go.opentelemetry.io/otel/metric v1.19.0 + go.opentelemetry.io/otel/sdk v1.19.0 + go.opentelemetry.io/otel/sdk/metric v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/fx v1.20.0 - go.uber.org/zap v1.25.0 - google.golang.org/grpc v1.57.0 + go.uber.org/zap v1.26.0 + google.golang.org/grpc v1.58.2 gorm.io/driver/postgres v1.5.2 - gorm.io/gorm v1.25.4 + gorm.io/gorm v1.25.5 + gorm.io/plugin/opentelemetry v0.1.4 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/ClickHouse/ch-go v0.58.2 // indirect - github.com/ClickHouse/clickhouse-go/v2 v2.13.4 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.14.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/containerd/containerd v1.7.5 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/containerd/continuity v0.4.2 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/docker/cli v24.0.5+incompatible // indirect + github.com/docker/cli v24.0.6+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.6.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -125,11 +144,12 @@ require ( github.com/jackc/puddle v1.3.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -141,7 +161,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.9 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/paulmach/orb v0.10.0 // indirect @@ -149,24 +169,31 @@ require ( github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/streadway/amqp v1.1.0 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -175,6 +202,10 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -187,10 +218,16 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect mellium.im/sasl v0.3.1 // indirect + modernc.org/libc v1.24.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.6.0 // indirect + modernc.org/sqlite v1.25.0 // indirect ) diff --git a/internal/pkg/go.sum b/internal/pkg/go.sum index fb555b42..d740df7a 100644 --- a/internal/pkg/go.sum +++ b/internal/pkg/go.sum @@ -25,8 +25,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -55,8 +55,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= -github.com/ClickHouse/clickhouse-go/v2 v2.13.4 h1:NcvYN9ONZn3vlPMfQVUBSG5LKz+1y2wk4vaaz5QZXIg= -github.com/ClickHouse/clickhouse-go/v2 v2.13.4/go.mod h1:u1AUh8E0XqN1sU1EDzbiGLTI4KWOd+lOHimNSsdyJec= +github.com/ClickHouse/clickhouse-go/v2 v2.14.1 h1:5C2hhmZEGUVdy8CPpY3iPpfBv2kRbx5iOcflU49Rzws= +github.com/ClickHouse/clickhouse-go/v2 v2.14.1/go.mod h1:PHqbMvJTQ0EI4a1vJhmbmL/Ajr+Cin2O+WJjnYctJvg= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/EventStore/EventStore-Client-Go v1.0.2 h1:onM2TIInLhWUJwUQ/5a/8blNrrbhwrtm7Tpmg13ohiw= @@ -68,8 +68,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= @@ -87,10 +87,14 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/brianvoe/gofakeit/v6 v6.25.0 h1:ZpFjktOpLZUeF8q223o0rUuXtA+m5qW5srjvVi+JkXk= +github.com/brianvoe/gofakeit/v6 v6.25.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= @@ -113,8 +117,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/containerd v1.7.5 h1:i9T9XpAWMe11BHMN7pu1BZqOGjXaKTPyz2v+KYOZgkY= -github.com/containerd/containerd v1.7.5/go.mod h1:ieJNCSzASw2shSGYLHx8NAE7WsZ/gEigo5fQ78W5Zvw= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= @@ -140,12 +144,12 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dhui/dktest v0.3.16 h1:i6gq2YQEtcrjKbeJpBkWjE8MmLZPYllcjOFbTZuPDnw= github.com/dhui/dktest v0.3.16/go.mod h1:gYaA3LRmM8Z4vJl2MA0THIigJoZrwOansEOsp+kqxp0= -github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= -github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -159,16 +163,16 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/elastic/elastic-transport-go/v8 v8.0.0-20230329154755-1a3c63de0db6/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= github.com/elastic/elastic-transport-go/v8 v8.3.0 h1:DJGxovyQLXGr62e9nDMPSxRyWION0Bh6d9eCFBriiHo= github.com/elastic/elastic-transport-go/v8 v8.3.0/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= -github.com/elastic/go-elasticsearch/v8 v8.9.0 h1:8xtmYjUkqtahl50E0Bg/wjKI7K63krJrrLipbNj/fCU= -github.com/elastic/go-elasticsearch/v8 v8.9.0/go.mod h1:NGmpvohKiRHXI0Sw4fuUGn6hYOmAXlyCphKpzVBiqDE= +github.com/elastic/go-elasticsearch/v8 v8.10.0 h1:ALg3DMxSrx07YmeMNcfPf7cFh1Ep2+Qa19EOXTbwr2k= +github.com/elastic/go-elasticsearch/v8 v8.10.0/go.mod h1:NGmpvohKiRHXI0Sw4fuUGn6hYOmAXlyCphKpzVBiqDE= 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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= @@ -178,6 +182,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= +github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= @@ -192,18 +200,23 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-resty/resty/v2 v2.9.1 h1:PIgGx4VrHvag0juCJ4dDv3MiFRlDmP0vicBucwf+gLM= +github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-testfixtures/testfixtures/v3 v3.9.0 h1:938g5V+GWLVejm3Hc+nWCuEXRlcglZDDlN/t1gWzcSY= github.com/go-testfixtures/testfixtures/v3 v3.9.0/go.mod h1:cdsKD2ApFBjdog9jRsz6EJqF+LClq/hrwE9K/1Dzo4s= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -227,6 +240,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 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/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -288,6 +303,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -302,6 +319,8 @@ github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IP github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= 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/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -392,8 +411,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C 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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -425,6 +444,9 @@ github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -448,8 +470,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= -github.com/mehdihadeli/go-mediatr v1.1.10 h1:NAzg4065c90lgYeb+Vzbd2WKH0tUFpxzL0mpx6hkU/A= -github.com/mehdihadeli/go-mediatr v1.1.10/go.mod h1:lwgZl7qVL/RKomObBblhG3uEte/r4nJDV95Vd+nGrMw= +github.com/mehdihadeli/go-mediatr v1.3.0 h1:hrb5Scp/nsiR3Y62mjZ0Tc5UX/dRJl4nDFkINBEIESA= +github.com/mehdihadeli/go-mediatr v1.3.0/go.mod h1:lsG+hyH+pEOhmZiZl0KPO72BcZiEReF03CBk4GVJB0k= github.com/michaelklishin/rabbit-hole v1.5.0 h1:Bex27BiFDsijCM9D0ezSHqyy0kehpYHuNKaPqq/a4RM= github.com/michaelklishin/rabbit-hole v1.5.0/go.mod h1:vvI1uOitYZi0O5HEGXhaWC1XT80Gy+HvFheJ+5Krlhk= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -477,15 +499,17 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= @@ -516,17 +540,20 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.15.0 h1:6tY5aDqFknY6VZkorFGgZtWygodZQxfmmEF4rqyJW9k= github.com/pressly/goose/v3 v3.15.0/go.mod h1:LlIo3zGccjb/YUgG+Svdb9Er14vefRdlDI7URCDrwYo= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= @@ -534,8 +561,9 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJu github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -556,6 +584,12 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= +github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= @@ -567,8 +601,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= @@ -605,19 +639,31 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= -github.com/testcontainers/testcontainers-go/modules/postgres v0.23.0 h1:OEGUC1YTN1RyS4xqsHmlyYkBWm9lMJcswoV4JSHJQOM= -github.com/testcontainers/testcontainers-go/modules/postgres v0.23.0/go.mod h1:YnqIhPwhjqVbJBuvSRJS6pa9Cy1PDRJcrM6T63Uw2ms= +github.com/testcontainers/testcontainers-go v0.25.0 h1:erH6cQjsaJrH+rJDU9qIf89KFdhK0Bft0aEZHlYC3Vs= +github.com/testcontainers/testcontainers-go v0.25.0/go.mod h1:4sC9SiJyzD1XFi59q8umTQYWxnkweEc5OjVtTUlJzqQ= +github.com/testcontainers/testcontainers-go/modules/postgres v0.25.0 h1:8WNK1Edo9ohRYPrDCXWdoVY2cbg/oFh9y5uWZGSBESo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.25.0/go.mod h1:XpwOhyUXheL31hz73L8be8maW1rQq8H48x5qZeHtYr0= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM= -github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8= -github.com/uptrace/bun/dialect/pgdialect v1.1.14 h1:b7+V1KDJPQSFYgkG/6YLXCl2uvwEY3kf/GSM7hTHRDY= -github.com/uptrace/bun/dialect/pgdialect v1.1.14/go.mod h1:v6YiaXmnKQ2FlhRD2c0ZfKd+QXH09pYn4H8ojaavkKk= -github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA= -github.com/uptrace/bun/driver/pgdriver v1.1.14/go.mod h1:D4FjWV9arDYct6sjMJhFoyU71SpllZRHXFRRP2Kd0Kw= +github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= +github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= +github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A= +github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8= +github.com/uptrace/bun/dialect/pgdialect v1.1.16 h1:eUPZ+YCJ69BA+W1X1ZmpOJSkv1oYtinr0zCXf7zCo5g= +github.com/uptrace/bun/dialect/pgdialect v1.1.16/go.mod h1:KQjfx/r6JM0OXfbv0rFrxAbdkPD7idK8VitnjIV9fZI= +github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7QiNOCPLqPFJjK4= +github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 h1:m5eNyOhch/7tyK6aN6eRRpNoD1vM8PNh64dA05X22Js= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3/go.mod h1:APPUXm9BbpH7NFkfpbw04raZSitzl19/3NOCu0rbI4E= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 h1:LyGS9cIZV0YVhE81zwfMhIE2l2flcj3wn5IoK4VkbWA= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3/go.mod h1:RvCYhPchLhvQ9l9C9goblbgO7BaKt597kBMf5mgKyo0= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 h1:2na5W81H38Z4qXCQCuzlcdSMiTWgPJ6XeZIArq6VIJE= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3/go.mod h1:9IVEh9mPv3NwFf99dVLX15FqVgdpZJ8RMDo/Cr0vK74= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -626,8 +672,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608= +github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= @@ -657,6 +703,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= @@ -668,32 +716,42 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 h1:pcWR7mkuO5XMK3f0KeGpr70OTR9/ikPk0D1hHEd5dp4= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0/go.mod h1:azTPpa9PvRDSNU/lQbe2CRDQoTcau5moOjS4EzhpyAY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0 h1:hhSlPVi9AQwOmbMmptPNLfRZOLgENdRM2kb7z9LFe1A= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0/go.mod h1:qtt+pEu23D7UVP+j33G4i7LopmVu8/6/IwGu3hEm100= -go.opentelemetry.io/contrib/propagators/ot v1.18.0 h1:VmzxO7BjUU6oo0ChcKuGdKaSR0vchPxwahHZl64zVUM= -go.opentelemetry.io/contrib/propagators/ot v1.18.0/go.mod h1:5VwcOJ7OjS0uPxaxuwKHwJtkt+EAC+cgjXleXMe51z4= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ERB6R034zmsHjBRLyxrAR8= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0 h1:oi5+xMN3pflqWSd4EX6FiO+Cn3KbFBBzeQmD5LMIf0c= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0/go.mod h1:pNir+S6/f0HFGfbXhobXLTFu60KtAzw8aGSUpt9A6VU= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= -go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 h1:bldpPC7XAv7f7LKTwNfRkNdzRhjtXaWybZFFa16dAb8= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0/go.mod h1:xhkNpJG3D+kmuaciNTco7cdK27Fb77J9Iqcq5CMe4Y8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0 h1:1uzNKJDqZ6y6F5J6aKWgJjRREpKiGhBvKHlWon/bqB4= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0/go.mod h1:vlqPvzDsmB4+jlERxBRXsdLCD6Q0LoBzxHqNXp3qvG4= +go.opentelemetry.io/contrib/propagators/ot v1.20.0 h1:duH7mgL6VGQH7e7QEAVOFkCQXWpCb4PjTtrhdrYrJRQ= +go.opentelemetry.io/contrib/propagators/ot v1.20.0/go.mod h1:gijQzxOq0JLj9lyZhTvqjDddGV/zaNagpPIn+2r8CEI= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0 h1:EGY0h5mGliP9o/nIkVuLI0vRiQqmsYOcbwCuotksO1o= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0/go.mod h1:JQgTGJP11yi3o4GHzIWYodhPisxANdqxF1eHwDSnJrI= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -720,8 +778,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -780,6 +838,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -817,10 +876,10 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -832,8 +891,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -846,6 +905,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -864,6 +924,7 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -890,6 +951,7 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -911,14 +973,19 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -930,11 +997,13 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -997,6 +1066,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1070,8 +1140,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1088,8 +1162,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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= @@ -1135,8 +1209,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= -gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= -gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/opentelemetry v0.1.4 h1:7p0ocWELjSSRI7NCKPW2mVe6h43YPini99sNJcbsTuc= +gorm.io/plugin/opentelemetry v0.1.4/go.mod h1:tndJHOdvPT0pyGhOb8E2209eXJCUxhC5UpKw7bGVWeI= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/internal/pkg/gorm_postgres/db.go b/internal/pkg/gorm_postgres/db.go deleted file mode 100644 index 0966a505..00000000 --- a/internal/pkg/gorm_postgres/db.go +++ /dev/null @@ -1,87 +0,0 @@ -package gormPostgres - -import ( - "database/sql" - "fmt" - - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/gromlog" - - "emperror.dev/errors" - "github.com/uptrace/bun/driver/pgdriver" - gormPostgres "gorm.io/driver/postgres" - "gorm.io/gorm" -) - -func NewGorm(cfg *GormOptions) (*gorm.DB, error) { - if cfg.DBName == "" { - return nil, errors.New("DBName is required in the config.") - } - - err := createDB(cfg) - if err != nil { - return nil, err - } - - var dataSourceName string - dataSourceName = fmt.Sprintf("host=%s port=%d user=%s dbname=%s password=%s", - cfg.Host, - cfg.Port, - cfg.User, - cfg.DBName, - cfg.Password, - ) - - gormDb, err := gorm.Open( - gormPostgres.Open(dataSourceName), - &gorm.Config{Logger: gromlog.NewGormCustomLogger(defaultLogger.Logger)}, - ) - if err != nil { - return nil, err - } - - return gormDb, nil -} - -func NewSQLDB(orm *gorm.DB) (*sql.DB, error) { return orm.DB() } - -func createDB(cfg *GormOptions) error { - // we should choose a default database in the connection, but because we don't have a database yet we specify postgres default database 'postgres' - datasource := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", - cfg.User, - cfg.Password, - cfg.Host, - cfg.Port, - "postgres", - ) - - sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(datasource))) - - var exists int - rows, err := sqldb.Query( - fmt.Sprintf("SELECT 1 FROM pg_catalog.pg_database WHERE datname='%s'", cfg.DBName), - ) - if err != nil { - return err - } - - if rows.Next() { - err = rows.Scan(&exists) - if err != nil { - return err - } - } - - if exists == 1 { - return nil - } - - _, err = sqldb.Exec(fmt.Sprintf("CREATE DATABASE %s", cfg.DBName)) - if err != nil { - return err - } - - defer sqldb.Close() - - return nil -} diff --git a/internal/pkg/gorm_postgres/gorm_options.go b/internal/pkg/gorm_postgres/gorm_options.go deleted file mode 100644 index 7b78b78b..00000000 --- a/internal/pkg/gorm_postgres/gorm_options.go +++ /dev/null @@ -1,38 +0,0 @@ -package gormPostgres - -import ( - "fmt" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" - - "github.com/iancoleman/strcase" -) - -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[GormOptions]()) - -type GormOptions struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - User string `mapstructure:"user"` - DBName string `mapstructure:"dbName"` - SSLMode bool `mapstructure:"sslMode"` - Password string `mapstructure:"password"` -} - -func (h *GormOptions) Dns() string { - datasource := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", - h.User, - h.Password, - h.Host, - h.Port, - h.DBName, - ) - - return datasource -} - -func provideConfig(environment environemnt.Environment) (*GormOptions, error) { - return config.BindConfigKey[*GormOptions](optionName, environment) -} diff --git a/internal/pkg/gorm_postgres/helpers.go b/internal/pkg/gorm_postgres/helpers.go deleted file mode 100644 index 1e19bad2..00000000 --- a/internal/pkg/gorm_postgres/helpers.go +++ /dev/null @@ -1,61 +0,0 @@ -package gormPostgres - -import ( - "context" - "fmt" - "strings" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - - "emperror.dev/errors" - "gorm.io/gorm" -) - -// Ref: https://dev.to/rafaelgfirmino/pagination-using-gorm-scopes-3k5f - -func Paginate[T any]( - ctx context.Context, - listQuery *utils.ListQuery, - db *gorm.DB, -) (*utils.ListResult[T], error) { - var items []T - var totalRows int64 - db.Model(items).WithContext(ctx).Count(&totalRows) - - // generate where query - query := db.WithContext(ctx). - Offset(listQuery.GetOffset()). - Limit(listQuery.GetLimit()). - Order(listQuery.GetOrderBy()) - - if listQuery.Filters != nil { - for _, filter := range listQuery.Filters { - column := filter.Field - action := filter.Comparison - value := filter.Value - - switch action { - case "equals": - whereQuery := fmt.Sprintf("%s = ?", column) - query = query.WithContext(ctx).Where(whereQuery, value) - break - case "contains": - whereQuery := fmt.Sprintf("%s LIKE ?", column) - query = query.WithContext(ctx).Where(whereQuery, "%"+value+"%") - break - case "in": - whereQuery := fmt.Sprintf("%s IN (?)", column) - queryArray := strings.Split(value, ",") - query = query.WithContext(ctx).Where(whereQuery, queryArray) - break - - } - } - } - - if err := query.Find(&items).Error; err != nil { - return nil, errors.WrapIf(err, "error in finding products.") - } - - return utils.NewListResult[T](items, listQuery.GetSize(), listQuery.GetPage(), totalRows), nil -} diff --git a/internal/pkg/gorm_postgres/repository/gorm_generic_repository_test.go b/internal/pkg/gorm_postgres/repository/gorm_generic_repository_test.go deleted file mode 100644 index 2846bb06..00000000 --- a/internal/pkg/gorm_postgres/repository/gorm_generic_repository_test.go +++ /dev/null @@ -1,487 +0,0 @@ -package repository - -import ( - "context" - "log" - "testing" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data/specification" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - gorm2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/gorm" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/assert" - "gorm.io/gorm" - - _ "github.com/lib/pq" // postgres driver -) - -// Product is a domain_events entity -type Product struct { - ID uuid.UUID - Name string - Weight int - IsAvailable bool -} - -// ProductGorm is DTO used to map Product entity to database -type ProductGorm struct { - ID uuid.UUID `gorm:"primaryKey;column:id"` - Name string `gorm:"column:name"` - Weight int `gorm:"column:weight"` - IsAvailable bool `gorm:"column:is_available"` -} - -func init() { - err := mapper.CreateMap[*ProductGorm, *Product]() - if err != nil { - log.Fatal(err) - } - - err = mapper.CreateMap[*Product, *ProductGorm]() - if err != nil { - log.Fatal(err) - } -} - -func Test_Add(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - - product := &ProductGorm{ - ID: uuid.NewV4(), - Name: "added_product", - Weight: 100, - IsAvailable: true, - } - - err = repository.Add(ctx, product) - if err != nil { - t.Fatal(err) - } - - p, err := repository.GetById(ctx, product.ID) - if err != nil { - return - } - - assert.NotNil(t, p) - assert.Equal(t, product.ID, p.ID) -} - -func Test_Add_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - product := &Product{ - ID: uuid.NewV4(), - Name: "added_product", - Weight: 100, - IsAvailable: true, - } - - err = repository.Add(ctx, product) - if err != nil { - t.Fatal(err) - } - - p, err := repository.GetById(ctx, product.ID) - if err != nil { - return - } - - assert.NotNil(t, p) - assert.Equal(t, product.ID, p.ID) -} - -func Test_Get_By_Id(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } - p := all.Items[0] - - testCases := []struct { - Name string - ProductId uuid.UUID - ExpectResult *ProductGorm - }{ - { - Name: "ExistingProduct", - ProductId: p.ID, - ExpectResult: p, - }, - { - Name: "NonExistingProduct", - ProductId: uuid.NewV4(), - ExpectResult: nil, - }, - } - - for _, c := range testCases { - c := c - t.Run(c.Name, func(t *testing.T) { - t.Parallel() - res, err := repository.GetById(ctx, c.ProductId) - if c.ExpectResult == nil { - assert.Error(t, err) - assert.True(t, customErrors.IsNotFoundError(err)) - assert.Nil(t, res) - } else { - assert.NoError(t, err) - assert.NotNil(t, res) - assert.Equal(t, p.ID, res.ID) - } - }) - } -} - -func Test_Get_By_Id_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } - p := all.Items[0] - - testCases := []struct { - Name string - ProductId uuid.UUID - ExpectResult *Product - }{ - { - Name: "ExistingProduct", - ProductId: p.ID, - ExpectResult: p, - }, - { - Name: "NonExistingProduct", - ProductId: uuid.NewV4(), - ExpectResult: nil, - }, - } - - for _, c := range testCases { - c := c - t.Run(c.Name, func(t *testing.T) { - t.Parallel() - res, err := repository.GetById(ctx, c.ProductId) - if c.ExpectResult == nil { - assert.Error(t, err) - assert.True(t, customErrors.IsNotFoundError(err)) - assert.Nil(t, res) - } else { - assert.NoError(t, err) - assert.NotNil(t, res) - assert.Equal(t, p.ID, res.ID) - } - }) - } -} - -func Test_Get_All(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - models, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models.Items) -} - -func Test_Get_All_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - models, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models.Items) -} - -func Test_Search(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - models, err := repository.Search(ctx, "seed_product1", utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models.Items) - assert.Equal(t, len(models.Items), 1) -} - -func Test_Search_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - - models, err := repository.Search(ctx, "seed_product1", utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models.Items) - assert.Equal(t, len(models.Items), 1) -} - -func Test_Where(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - - models, err := repository.GetByFilter(ctx, map[string]interface{}{"name": "seed_product1"}) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models) - assert.Equal(t, len(models), 1) -} - -func Test_Where_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - - models, err := repository.GetByFilter(ctx, map[string]interface{}{"name": "seed_product1"}) - if err != nil { - t.Fatal(err) - } - - assert.NotEmpty(t, models) - assert.Equal(t, len(models), 1) -} - -func Test_Update(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - product := products.Items[0] - - product.Name = "product2_updated" - err = repository.Update(ctx, product) - if err != nil { - t.Fatal(err) - } - - single, err := repository.GetById(ctx, product.ID) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) - assert.Equal(t, "product2_updated", single.Name) -} - -func Test_Update_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - product := products.Items[0] - - product.Name = "product2_updated" - err = repository.Update(ctx, product) - if err != nil { - t.Fatal(err) - } - - single, err := repository.GetById(ctx, product.ID) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) - assert.Equal(t, "product2_updated", single.Name) -} - -func Test_Delete(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - product := products.Items[0] - - err = repository.Delete(ctx, product.ID) - if err != nil { - return - } - - single, err := repository.GetById(ctx, product.ID) - assert.Nil(t, single) -} - -func Test_Delete_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - product := products.Items[0] - - err = repository.Delete(ctx, product.ID) - if err != nil { - return - } - - single, err := repository.GetById(ctx, product.ID) - assert.Nil(t, single) -} - -func Test_Count(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - count := repository.Count(ctx) - - assert.Equal(t, count, int64(2)) -} - -func Test_Count_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - count := repository.Count(ctx) - - assert.Equal(t, count, int64(2)) -} - -func Test_Find(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - entities, err := repository.Find( - ctx, - specification.And(specification.Equal("is_available", true), specification.Equal("name", "seed_product1")), - ) - if err != nil { - return - } - assert.Equal(t, len(entities), 1) -} - -func Test_Find_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - entities, err := repository.Find( - ctx, - specification.And(specification.Equal("is_available", true), specification.Equal("name", "seed_product1")), - ) - if err != nil { - return - } - assert.Equal(t, len(entities), 1) -} - -func setupGenericGormRepositoryWithDataModel( - ctx context.Context, - t *testing.T, -) (data.GenericRepositoryWithDataModel[*ProductGorm, *Product], error) { - defaultLogger.SetupDefaultLogger() - - db, err := gorm2.NewGormTestContainers(defaultLogger.Logger).Start(ctx, t) - if err != nil { - return nil, err - } - - err = seedAndMigration(ctx, db) - if err != nil { - return nil, err - } - - return NewGenericGormRepositoryWithDataModel[*ProductGorm, *Product](db), nil -} - -func setupGenericGormRepository(ctx context.Context, t *testing.T) (data.GenericRepository[*ProductGorm], error) { - defaultLogger.SetupDefaultLogger() - - db, err := gorm2.NewGormTestContainers(defaultLogger.Logger).Start(ctx, t) - - err = seedAndMigration(ctx, db) - if err != nil { - return nil, err - } - - return NewGenericGormRepository[*ProductGorm](db), nil -} - -func seedAndMigration(ctx context.Context, db *gorm.DB) error { - err := db.AutoMigrate(ProductGorm{}) - if err != nil { - return err - } - - seedProducts := []*ProductGorm{ - { - ID: uuid.NewV4(), - Name: "seed_product1", - Weight: 100, - IsAvailable: true, - }, - { - ID: uuid.NewV4(), - Name: "seed_product2", - Weight: 100, - IsAvailable: true, - }, - } - - err = db.WithContext(ctx).Create(seedProducts).Error - if err != nil { - return err - } - return nil -} diff --git a/internal/pkg/grpc/client.go b/internal/pkg/grpc/client.go index 54fc3ecd..6557565e 100644 --- a/internal/pkg/grpc/client.go +++ b/internal/pkg/grpc/client.go @@ -5,8 +5,10 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/handlers/otel" "emperror.dev/errors" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" @@ -29,6 +31,10 @@ func NewGrpcClient(config *config.GrpcOptions) (GrpcClient, error) { // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/df16f32df86b40077c9c90d06f33c4cdb6dd5afa/instrumentation/google.golang.org/grpc/otelgrpc/example_interceptor_test.go conn, err := grpc.Dial(fmt.Sprintf("%s%s", config.Host, config.Port), grpc.WithTransportCredentials(insecure.NewCredentials()), + // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/example/client/main.go#L47C3-L47C52 + // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/doc.go + grpc.WithStatsHandler(otelgrpc.NewClientHandler()), + grpc.WithStatsHandler(otel.NewClientHandler()), ) if err != nil { return nil, err @@ -57,7 +63,10 @@ func (g *grpcClient) WaitForAvailableConnection() error { return err } -func waitUntilConditionMet(conditionToMet func() bool, timeout ...time.Duration) error { +func waitUntilConditionMet( + conditionToMet func() bool, + timeout ...time.Duration, +) error { timeOutTime := 20 * time.Second if len(timeout) >= 0 && timeout != nil { timeOutTime = timeout[0] @@ -68,7 +77,9 @@ func waitUntilConditionMet(conditionToMet func() bool, timeout ...time.Duration) meet := conditionToMet() for meet == false { if timeOutExpired { - return errors.New("grpc connection could not be established in the given timeout.") + return errors.New( + "grpc connection could not be established in the given timeout.", + ) } time.Sleep(time.Second * 2) meet = conditionToMet() diff --git a/internal/pkg/grpc/config/grpc_options.go b/internal/pkg/grpc/config/grpc_options.go index 649ebed0..246ab3c8 100644 --- a/internal/pkg/grpc/config/grpc_options.go +++ b/internal/pkg/grpc/config/grpc_options.go @@ -2,21 +2,21 @@ package config import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[GrpcOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[GrpcOptions]()) type GrpcOptions struct { Port string `mapstructure:"port" env:"TcpPort"` Host string `mapstructure:"host" env:"Host"` Development bool `mapstructure:"development" env:"Development"` - Name string `mapstructure:"name" env:"Name"` + Name string `mapstructure:"name" env:"ShortTypeName"` } -func ProvideConfig(environment environemnt.Environment) (*GrpcOptions, error) { +func ProvideConfig(environment environment.Environment) (*GrpcOptions, error) { return config.BindConfigKey[*GrpcOptions](optionName, environment) } diff --git a/internal/pkg/grpc/grpc_fx.go b/internal/pkg/grpc/grpc_fx.go index de18d57a..8c388b1c 100644 --- a/internal/pkg/grpc/grpc_fx.go +++ b/internal/pkg/grpc/grpc_fx.go @@ -12,7 +12,11 @@ import ( var ( // Module provided to fxlog // https://uber-go.github.io/fx/modules.html - Module = fx.Module("grpcfx", grpcProviders, grpcInvokes) //nolint:gochecknoglobals + Module = fx.Module( + "grpcfx", + grpcProviders, + grpcInvokes, + ) //nolint:gochecknoglobals // - order is not important in provide // - provide can have parameter and will resolve if registered @@ -23,7 +27,7 @@ var ( // https://uber-go.github.io/fx/annotate.html fx.Annotate( NewGrpcServer, - fx.ParamTags(``, ``, `optional:"true"`), + fx.ParamTags(``, ``), ), NewGrpcClient, )) @@ -52,7 +56,10 @@ func registerHooks( // if (ctx.Err() == nil), context not canceled or deadlined if err := grpcServer.RunGrpcServer(nil); err != nil { // do a fatal for going to OnStop process - logger.Fatalf("(GrpcServer.RunGrpcServer) error in running server: {%v}", err) + logger.Fatalf( + "(GrpcServer.RunGrpcServer) error in running server: {%v}", + err, + ) } }() logger.Infof( diff --git a/internal/pkg/grpc/grpcErrors/custom_grpc_errors.go b/internal/pkg/grpc/grpcerrors/custom_grpc_errors.go similarity index 99% rename from internal/pkg/grpc/grpcErrors/custom_grpc_errors.go rename to internal/pkg/grpc/grpcerrors/custom_grpc_errors.go index f28fd4af..cd1066f5 100644 --- a/internal/pkg/grpc/grpcErrors/custom_grpc_errors.go +++ b/internal/pkg/grpc/grpcerrors/custom_grpc_errors.go @@ -1,4 +1,4 @@ -package grpcErrors +package grpcerrors import ( "time" diff --git a/internal/pkg/grpc/grpcErrors/grpc_error_parser.go b/internal/pkg/grpc/grpcerrors/grpc_error_parser.go similarity index 78% rename from internal/pkg/grpc/grpcErrors/grpc_error_parser.go rename to internal/pkg/grpc/grpcerrors/grpc_error_parser.go index 1c5b7686..d951ae45 100644 --- a/internal/pkg/grpc/grpcErrors/grpc_error_parser.go +++ b/internal/pkg/grpc/grpcerrors/grpc_error_parser.go @@ -1,12 +1,12 @@ -package grpcErrors +package grpcerrors import ( "context" "database/sql" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" "emperror.dev/errors" "github.com/go-playground/validator" @@ -21,14 +21,26 @@ func ParseError(err error) GrpcErr { var validatorErr validator.ValidationErrors stackTrace := errorUtils.ErrorsWithStack(err) - if err != nil { + if err != nil && customErr != nil { switch { case customErrors.IsDomainError(err, customErr.Status()): - return NewDomainGrpcError(codes.Code(customErr.Status()), customErr.Error(), stackTrace) + return NewDomainGrpcError( + codes.Code(customErr.Status()), + customErr.Error(), + stackTrace, + ) case customErrors.IsApplicationError(err, customErr.Status()): - return NewApplicationGrpcError(codes.Code(customErr.Status()), customErr.Error(), stackTrace) + return NewApplicationGrpcError( + codes.Code(customErr.Status()), + customErr.Error(), + stackTrace, + ) case customErrors.IsApiError(err, customErr.Status()): - return NewApiGrpcError(codes.Code(customErr.Status()), customErr.Error(), stackTrace) + return NewApiGrpcError( + codes.Code(customErr.Status()), + customErr.Error(), + stackTrace, + ) case customErrors.IsBadRequestError(err): return NewBadRequestGrpcError(customErr.Error(), stackTrace) case customErrors.IsNotFoundError(err): @@ -54,14 +66,22 @@ func ParseError(err error) GrpcErr { return NewInternalServerGrpcError(customErr.Error(), stackTrace) case customErrors.IsMarshalingError(err): return NewInternalServerGrpcError(customErr.Error(), stackTrace) + default: + return NewInternalServerGrpcError(err.Error(), stackTrace) + } + } else if err != nil && customErr == nil { + switch { case errors.Is(err, sql.ErrNoRows): return NewNotFoundErrorGrpcError(err.Error(), stackTrace) case errors.Is(err, context.DeadlineExceeded): - return NewGrpcError(codes.DeadlineExceeded, constants.ErrRequestTimeoutTitle, err.Error(), stackTrace) + return NewGrpcError( + codes.DeadlineExceeded, + constants.ErrRequestTimeoutTitle, + err.Error(), + stackTrace, + ) case errors.As(err, &validatorErr): return NewValidationGrpcError(validatorErr.Error(), stackTrace) - default: - return NewInternalServerGrpcError(err.Error(), stackTrace) } } diff --git a/internal/pkg/grpc/grpcErrors/grpc_errors.go b/internal/pkg/grpc/grpcerrors/grpc_errors.go similarity index 91% rename from internal/pkg/grpc/grpcErrors/grpc_errors.go rename to internal/pkg/grpc/grpcerrors/grpc_errors.go index e2fe7427..1e62d063 100644 --- a/internal/pkg/grpc/grpcErrors/grpc_errors.go +++ b/internal/pkg/grpc/grpcerrors/grpc_errors.go @@ -1,11 +1,11 @@ -package grpcErrors +package grpcerrors import ( "encoding/json" "fmt" "time" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -34,7 +34,12 @@ type GrpcErr interface { ToGrpcResponseErr() error } -func NewGrpcError(status codes.Code, title string, detail string, stackTrace string) GrpcErr { +func NewGrpcError( + status codes.Code, + title string, + detail string, + stackTrace string, +) GrpcErr { grpcErr := &grpcErr{ Status: status, Title: title, @@ -107,7 +112,7 @@ func (p *grpcErr) ToGrpcResponseErr() error { } func (p *grpcErr) ToJson() string { - defaultLogger.Logger.Error(p.Error()) + defaultLogger.GetLogger().Error(p.Error()) stackTrace := p.GetStackTrace() fmt.Println(stackTrace) diff --git a/internal/pkg/grpc/handlers/otel/config.go b/internal/pkg/grpc/handlers/otel/config.go new file mode 100644 index 00000000..070d15eb --- /dev/null +++ b/internal/pkg/grpc/handlers/otel/config.go @@ -0,0 +1,80 @@ +package otel + +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +// ref: https://github.com/bakins/otel-grpc-statshandler/blob/main/statshandler.go + +// Option applies an option value when creating a Handler. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (f optionFunc) apply(c *config) { + f(c) +} + +type config struct { + metricsProvider metric.MeterProvider + tracerProvider trace.TracerProvider + propagator propagation.TextMapPropagator + Namespace string + serviceName string + instrumentationName string +} + +var defualtConfig = config{ + metricsProvider: otel.GetMeterProvider(), + tracerProvider: otel.GetTracerProvider(), + propagator: otel.GetTextMapPropagator(), + serviceName: "application", + instrumentationName: "grpc-otel", +} + +func WithMeterProvider(m metric.MeterProvider) Option { + return optionFunc(func(c *config) { + c.metricsProvider = m + }) +} + +func WithTraceProvider(t trace.TracerProvider) Option { + return optionFunc(func(c *config) { + c.tracerProvider = t + }) +} + +func WithPropagators(p propagation.TextMapPropagator) Option { + return optionFunc(func(c *config) { + c.propagator = p + }) +} + +func SetNamespace(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.Namespace != "" { + cfg.Namespace = v + } + }) +} + +func SetServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} + +func SetInstrumentationName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.instrumentationName != "" { + cfg.instrumentationName = v + } + }) +} diff --git a/internal/pkg/grpc/handlers/otel/handler.go b/internal/pkg/grpc/handlers/otel/handler.go new file mode 100644 index 00000000..71c14707 --- /dev/null +++ b/internal/pkg/grpc/handlers/otel/handler.go @@ -0,0 +1,329 @@ +package otel + +import ( + "context" + "strings" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/stats" + "google.golang.org/grpc/status" +) + +// ref: https://github.com/bakins/otel-grpc-statshandler/blob/main/statshandler.go +// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/config.go +// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go +// https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go#L52 + +// https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions/rpc-metrics/ + +// ServerHandler implements https://pkg.go.dev/google.golang.org/grpc/stats#ServerHandler +// It records OpenTelemetry metrics and traces. +type ServerHandler struct { + handler +} + +// ClientHandler implements https://pkg.go.dev/google.golang.org/grpc/stats#ServerHandler +// It records OpenTelemetry metrics and traces. +type ClientHandler struct { + handler +} + +type gRPCContextKey struct{} + +type gRPCContext struct { + messagesReceived int64 + messagesSent int64 + attributes []attribute.KeyValue + startTime time.Time +} + +type handler struct { + tracer trace.Tracer + meter metric.Meter + propagator propagation.TextMapPropagator + rpcDuration metric.Int64Histogram + rpcRequestSize metric.Int64Histogram + rpcResponseSize metric.Int64Histogram + rpcRequestsPerRPC metric.Int64Histogram + rpcResponsesPerRPC metric.Int64Histogram + rpcTotalFailed metric.Int64Counter + rpcTotalSuccess metric.Int64Counter + spanKind trace.SpanKind + config config +} + +func newHandler(spanKind trace.SpanKind, options []Option) (handler, error) { + c := defualtConfig + + for _, o := range options { + o.apply(&c) + } + + meter := c.metricsProvider.Meter(c.instrumentationName) + + prefix := "rpc.server" + if spanKind == trace.SpanKindClient { + prefix = "rpc.client" + } + + // https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions/rpc-metrics/#rpc-server + rpcServerDuration, err := meter.Int64Histogram(prefix+".duration", + metric.WithDescription("Measures the duration of inbound RPC."), + metric.WithUnit("ms")) + if err != nil { + otel.Handle(err) + } + + rpcRequestSize, err := meter.Int64Histogram( + prefix+".request.size", + metric.WithDescription( + "Measures size of RPC request messages (uncompressed).", + ), + metric.WithUnit("bytes"), + ) + if err != nil { + return handler{}, err + } + + rpcResponseSize, err := meter.Int64Histogram( + prefix+".response.size", + metric.WithDescription( + "Measures size of RPC response messages (uncompressed)", + ), + metric.WithUnit("bytes"), + ) + if err != nil { + return handler{}, err + } + + rpcRequestsPerRPC, err := meter.Int64Histogram( + prefix+".requests_per_rpc", + metric.WithDescription( + "Measures the number of messages received per RPC. Should be 1 for all non-streaming RPCs", + ), + metric.WithUnit("count"), + ) + if err != nil { + return handler{}, err + } + + rpcResponsesPerRPC, err := meter.Int64Histogram( + prefix+".responses_per_rpc", + metric.WithDescription( + "Measures the number of messages sent per RPC. Should be 1 for all non-streaming RPCs", + ), + metric.WithUnit("count")) + if err != nil { + return handler{}, err + } + + rpcTotalFailed, err := meter.Int64Counter( + prefix+".rpc_error_total", + metric.WithDescription("The total number of error grpc requests"), + metric.WithUnit("count"), + ) + if err != nil { + return handler{}, err + } + + rpcTotalSuccess, err := meter.Int64Counter( + prefix+".rpc_success_total", + metric.WithDescription("The total number of success grpc requests"), + metric.WithUnit("count"), + ) + if err != nil { + return handler{}, err + } + + h := handler{ + tracer: c.tracerProvider.Tracer(c.instrumentationName), + meter: meter, + spanKind: spanKind, + config: c, + rpcDuration: rpcServerDuration, + rpcRequestSize: rpcRequestSize, + rpcResponseSize: rpcResponseSize, + rpcRequestsPerRPC: rpcRequestsPerRPC, + rpcResponsesPerRPC: rpcResponsesPerRPC, + rpcTotalFailed: rpcTotalFailed, + rpcTotalSuccess: rpcTotalSuccess, + } + + return h, nil +} + +func (h *handler) tagRPC( + ctx context.Context, + info *stats.RPCTagInfo, +) context.Context { + ctx = extract(ctx, h.config.propagator) + + var attributes []attribute.KeyValue + attributes = append(attributes, semconv.RPCSystemGRPC) + + parts := strings.Split(info.FullMethodName, "/") + if len(parts) == 3 { + attributes = append(attributes, semconv.RPCServiceKey.String(parts[1])) + attributes = append(attributes, semconv.RPCMethodKey.String(parts[2])) + } + + gctx := gRPCContext{attributes: attributes, startTime: time.Now()} + + return inject( + context.WithValue(ctx, gRPCContextKey{}, &gctx), + h.config.propagator, + ) +} + +func (h *handler) handleRPC(ctx context.Context, rs stats.RPCStats) { + _ = trace.SpanFromContext(ctx) + gctx, _ := ctx.Value(gRPCContextKey{}).(*gRPCContext) + + switch rs := rs.(type) { + case *stats.Begin: + case *stats.InPayload: + if gctx != nil { + // https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go#L52 + opt := metric.WithAttributes(gctx.attributes...) + h.rpcRequestSize.Record(ctx, int64(rs.Length), opt) + } + + case *stats.OutPayload: + if gctx != nil { + // https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go#L52 + opt := metric.WithAttributes(gctx.attributes...) + h.rpcResponseSize.Record(ctx, int64(rs.Length), opt) + } + case *stats.End: + if rs.Error != nil { + s, _ := status.FromError(rs.Error) + gctx.attributes = append(gctx.attributes, statusCodeAttr(s.Code())) + opt := metric.WithAttributes(gctx.attributes...) + + h.rpcTotalFailed.Add(ctx, 1, opt) + } else { + gctx.attributes = append(gctx.attributes, statusCodeAttr(codes.OK)) + opt := metric.WithAttributes(gctx.attributes...) + + h.rpcTotalSuccess.Add(ctx, 1, opt) + } + + if gctx != nil { + duration := time.Since(gctx.startTime).Milliseconds() + opt := metric.WithAttributes(gctx.attributes...) + + h.rpcDuration.Record( + ctx, + duration, + opt, + ) + + h.rpcRequestsPerRPC.Record( + ctx, + gctx.messagesReceived, + opt, + ) + + h.rpcResponsesPerRPC.Record( + ctx, + gctx.messagesSent, + opt, + ) + } + + default: + return + } +} + +func statusCodeAttr(c codes.Code) attribute.KeyValue { + return semconv.RPCGRPCStatusCodeKey.Int(int(c)) +} + +func NewServerHandler(options ...Option) stats.Handler { + h, err := newHandler(trace.SpanKindServer, options) + if err != nil { + otel.Handle(err) + } + + s := &ServerHandler{ + handler: h, + } + + return s +} + +func NewClientHandler(options ...Option) stats.Handler { + h, err := newHandler(trace.SpanKindClient, options) + if err != nil { + otel.Handle(err) + } + + c := &ClientHandler{ + handler: h, + } + + return c +} + +func (s *ServerHandler) TagRPC( + ctx context.Context, + info *stats.RPCTagInfo, +) context.Context { + return s.handler.tagRPC(ctx, info) +} + +// HandleRPC processes the RPC stats. +func (s *ServerHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { + s.handler.handleRPC(ctx, rs) +} + +// TagConn can attach some information to the given context. +func (s *ServerHandler) TagConn( + ctx context.Context, + _ *stats.ConnTagInfo, +) context.Context { + // no-op + return ctx +} + +// HandleConn processes the Conn stats. +func (s *ServerHandler) HandleConn(_ context.Context, _ stats.ConnStats) { + // no-op +} + +func (c *ClientHandler) TagRPC( + ctx context.Context, + info *stats.RPCTagInfo, +) context.Context { + return c.handler.tagRPC(ctx, info) +} + +func (c *ClientHandler) HandleRPC( + ctx context.Context, + rpcStats stats.RPCStats, +) { + c.handler.handleRPC(ctx, rpcStats) +} + +func (c *ClientHandler) TagConn( + ctx context.Context, + _ *stats.ConnTagInfo, +) context.Context { + // no-op + return ctx +} + +func (c *ClientHandler) HandleConn( + _ context.Context, + _ stats.ConnStats, +) { + // no-op +} diff --git a/internal/pkg/grpc/handlers/otel/supplier.go b/internal/pkg/grpc/handlers/otel/supplier.go new file mode 100644 index 00000000..1a9b996d --- /dev/null +++ b/internal/pkg/grpc/handlers/otel/supplier.go @@ -0,0 +1,69 @@ +package otel + +import ( + "context" + + "go.opentelemetry.io/otel/propagation" + "google.golang.org/grpc/metadata" +) + +// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/metadata_supplier.go#L27 +type metadataSupplier struct { + metadata *metadata.MD +} + +var _ propagation.TextMapCarrier = &metadataSupplier{} + +func (s *metadataSupplier) Get(key string) string { + values := s.metadata.Get(key) + if len(values) == 0 { + return "" + } + + return values[0] +} + +func (s *metadataSupplier) Set(key string, value string) { + if s.metadata != nil { + s.metadata.Set(key, value) + } +} + +func (s *metadataSupplier) Keys() []string { + out := make([]string, 0, len(*s.metadata)) + for key := range *s.metadata { + out = append(out, key) + } + + return out +} + +func extract( + ctx context.Context, + propagators propagation.TextMapPropagator, +) context.Context { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + md = metadata.MD{} + } + + return propagators.Extract(ctx, &metadataSupplier{ + metadata: &md, + }) +} + +func inject( + ctx context.Context, + propagators propagation.TextMapPropagator, +) context.Context { + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + md = metadata.MD{} + } + + propagators.Inject(ctx, &metadataSupplier{ + metadata: &md, + }) + + return metadata.NewOutgoingContext(ctx, md) +} diff --git a/internal/pkg/grpc/interceptors/grpc_error/error_interceptor.go b/internal/pkg/grpc/interceptors/grpcerror/error_interceptor.go similarity index 58% rename from internal/pkg/grpc/interceptors/grpc_error/error_interceptor.go rename to internal/pkg/grpc/interceptors/grpcerror/error_interceptor.go index 128c0ad2..aa23aef0 100644 --- a/internal/pkg/grpc/interceptors/grpc_error/error_interceptor.go +++ b/internal/pkg/grpc/interceptors/grpcerror/error_interceptor.go @@ -2,10 +2,10 @@ package grpcError import ( "context" - "fmt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/grpcErrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/grpcerrors" + "emperror.dev/errors" "google.golang.org/grpc" ) @@ -18,15 +18,16 @@ func UnaryServerInterceptor() grpc.UnaryServerInterceptor { handler grpc.UnaryHandler, ) (interface{}, error) { resp, err := handler(ctx, req) - if err != nil { - grpcErr := grpcErrors.ParseError(err) - - if grpcErr != nil { - return nil, grpcErr.ToGrpcResponseErr() - } else { - prb := grpcErrors.NewInternalServerGrpcError(err.Error(), fmt.Sprintf("%+v\n", err)) - return nil, prb.ToGrpcResponseErr() - } + + var grpcErr grpcerrors.GrpcErr + + // if error was not `grpcErr` we will convert the error to a `grpcErr` + if ok := errors.As(err, &grpcErr); !ok { + grpcErr = grpcerrors.ParseError(err) + } + + if grpcErr != nil { + return nil, grpcErr.ToGrpcResponseErr() } return resp, err @@ -42,16 +43,18 @@ func StreamServerInterceptor() grpc.StreamServerInterceptor { handler grpc.StreamHandler, ) error { err := handler(srv, ss) - if err != nil { - grpcErr := grpcErrors.ParseError(err) - - if grpcErr != nil { - return grpcErr.ToGrpcResponseErr() - } else { - prb := grpcErrors.NewInternalServerGrpcError(err.Error(), fmt.Sprintf("%+v\n", err)) - return prb.ToGrpcResponseErr() - } + + var grpcErr grpcerrors.GrpcErr + + // if error was not `grpcErr` we will convert the error to a `grpcErr` + if ok := errors.As(err, &grpcErr); !ok { + grpcErr = grpcerrors.ParseError(err) + } + + if grpcErr != nil { + return grpcErr.ToGrpcResponseErr() } + return err } } diff --git a/internal/pkg/grpc/interceptors/otel_metrics/request_status_interceptor.go b/internal/pkg/grpc/interceptors/otel_metrics/request_status_interceptor.go deleted file mode 100644 index c46e1a3d..00000000 --- a/internal/pkg/grpc/interceptors/otel_metrics/request_status_interceptor.go +++ /dev/null @@ -1,84 +0,0 @@ -package otelMetrics - -// ref:https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go - -import ( - "context" - "fmt" - - "go.opentelemetry.io/otel/attribute" - api "go.opentelemetry.io/otel/metric" - "google.golang.org/grpc" -) - -// UnaryServerInterceptor add request status metrics to the otel -func UnaryServerInterceptor(meter api.Meter, serviceName string) grpc.UnaryServerInterceptor { - return func( - ctx context.Context, - req interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, - ) (interface{}, error) { - resp, err := handler(ctx, req) - - attrs := api.WithAttributes( - attribute.Key("MetricsType").String("Grpc"), - ) - - if err != nil { - counter, err := meter.Float64Counter( - fmt.Sprintf("%s_error_grpc_requests_total", serviceName), - api.WithDescription("The total number of error grpc requests"), - ) - if err != nil { - return nil, err - } - counter.Add(ctx, 1, attrs) - } else { - counter, err := meter.Float64Counter(fmt.Sprintf("%s_success_grpc_requests_total", serviceName), api.WithDescription("The total number of success grpc requests")) - if err != nil { - return nil, err - } - counter.Add(ctx, 1, attrs) - } - - return resp, err - } -} - -// StreamServerInterceptor add request status metrics to the otel -func StreamServerInterceptor(meter api.Meter, serviceName string) grpc.StreamServerInterceptor { - return func( - srv interface{}, - ss grpc.ServerStream, - info *grpc.StreamServerInfo, - handler grpc.StreamHandler, - ) error { - err := handler(srv, ss) - - attrs := api.WithAttributes( - attribute.Key("MetricsType").String("Grpc"), - ) - - ctx := ss.Context() - - if err != nil { - counter, err := meter.Float64Counter( - fmt.Sprintf("%s_error_grpc_requests_total", serviceName), - api.WithDescription("The total number of error grpc requests"), - ) - if err != nil { - return err - } - counter.Add(ctx, 1, attrs) - } else { - counter, err := meter.Float64Counter(fmt.Sprintf("%s_success_grpc_requests_total", serviceName), api.WithDescription("The total number of success grpc requests")) - if err != nil { - return err - } - counter.Add(ctx, 1, attrs) - } - - return err - } -} diff --git a/internal/pkg/grpc/otel/tracing/consts.go b/internal/pkg/grpc/otel/tracing/consts.go deleted file mode 100644 index bb93bc15..00000000 --- a/internal/pkg/grpc/otel/tracing/consts.go +++ /dev/null @@ -1,5 +0,0 @@ -package tracing - -const ( - GrpcErrorMessage = "rpc.grpc.error_message" -) diff --git a/internal/pkg/grpc/otel/tracing/utils.go b/internal/pkg/grpc/otel/tracing/utils.go deleted file mode 100644 index c9dc8b0e..00000000 --- a/internal/pkg/grpc/otel/tracing/utils.go +++ /dev/null @@ -1,41 +0,0 @@ -package tracing - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/grpcErrors" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - "go.opentelemetry.io/otel/trace" -) - -// TraceGrpcErrFromSpan setting span with status error with error message -func TraceGrpcErrFromSpan(span trace.Span, err error) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(GrpcErrorMessage, stackTraceError)) - if customErrors.IsCustomError(err) { - grpcErr := grpcErrors.ParseError(err) - span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int(int(grpcErr.GetStatus()))) - } - span.RecordError(err) - } - - return err -} - -// TraceGrpcErrFromSpanWithCode setting span with status error with error message -func TraceGrpcErrFromSpanWithCode(span trace.Span, err error, code int) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int(code)) - span.SetAttributes(attribute.String(GrpcErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} diff --git a/internal/pkg/grpc/server.go b/internal/pkg/grpc/server.go index 6b9296f5..78a8ece2 100644 --- a/internal/pkg/grpc/server.go +++ b/internal/pkg/grpc/server.go @@ -6,8 +6,8 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/config" - grpcError "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/interceptors/grpc_error" - otelMetrics "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/interceptors/otel_metrics" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/handlers/otel" + grpcError "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/interceptors/grpcerror" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "emperror.dev/errors" @@ -15,7 +15,6 @@ import ( grpcRecovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" grpcCtxTags "github.com/grpc-ecosystem/go-grpc-middleware/tags" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" - "go.opentelemetry.io/otel/metric" googleGrpc "google.golang.org/grpc" "google.golang.org/grpc/health" "google.golang.org/grpc/health/grpc_health_v1" @@ -48,31 +47,24 @@ type grpcServer struct { func NewGrpcServer( config *config.GrpcOptions, logger logger.Logger, - meter metric.Meter, ) GrpcServer { unaryServerInterceptors := []googleGrpc.UnaryServerInterceptor{ - otelgrpc.UnaryServerInterceptor(), grpcError.UnaryServerInterceptor(), grpcCtxTags.UnaryServerInterceptor(), grpcRecovery.UnaryServerInterceptor(), } streamServerInterceptors := []googleGrpc.StreamServerInterceptor{ - otelgrpc.StreamServerInterceptor(), grpcError.StreamServerInterceptor(), } - if meter != nil { - unaryServerInterceptors = append( - unaryServerInterceptors, - otelMetrics.UnaryServerInterceptor(meter, config.Name), - ) - streamServerInterceptors = append( - streamServerInterceptors, - otelMetrics.StreamServerInterceptor(meter, config.Name), - ) - } - s := googleGrpc.NewServer( + // https://github.com/open-telemetry/opentelemetry-go-contrib/issues/2840 + // https://github.com/open-telemetry/opentelemetry-go-contrib/pull/3002 + // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/doc.go + // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/google.golang.org/grpc/otelgrpc/example/server/main.go#L143C3-L143C50 + googleGrpc.StatsHandler(otelgrpc.NewServerHandler()), + googleGrpc.StatsHandler(otel.NewServerHandler()), + googleGrpc.KeepaliveParams(keepalive.ServerParameters{ MaxConnectionIdle: maxConnectionIdle * time.Minute, Timeout: gRPCTimeout * time.Second, @@ -88,10 +80,12 @@ func NewGrpcServer( unaryServerInterceptors..., )), ) - healthServer := health.NewServer() grpc_health_v1.RegisterHealthServer(s, healthServer) - healthServer.SetServingStatus(config.Name, grpc_health_v1.HealthCheckResponse_SERVING) + healthServer.SetServingStatus( + config.Name, + grpc_health_v1.HealthCheckResponse_SERVING, + ) return &grpcServer{ server: s, @@ -130,7 +124,10 @@ func (s *grpcServer) RunGrpcServer( if err != nil { s.log.Error( - fmt.Sprintf("[grpcServer_RunGrpcServer.Serve] grpc server serve error: %+v", err), + fmt.Sprintf( + "[grpcServer_RunGrpcServer.Serve] grpc server serve error: %+v", + err, + ), ) } diff --git a/internal/pkg/health/check.go b/internal/pkg/health/contracts/check.go similarity index 89% rename from internal/pkg/health/check.go rename to internal/pkg/health/contracts/check.go index e7db842a..eeb6e081 100644 --- a/internal/pkg/health/check.go +++ b/internal/pkg/health/contracts/check.go @@ -1,4 +1,4 @@ -package health +package contracts type Check map[string]Status @@ -8,5 +8,6 @@ func (check Check) AllUp() bool { return false } } + return true } diff --git a/internal/pkg/health/health.go b/internal/pkg/health/contracts/health.go similarity index 54% rename from internal/pkg/health/health.go rename to internal/pkg/health/contracts/health.go index 2545fca1..d35731a2 100644 --- a/internal/pkg/health/health.go +++ b/internal/pkg/health/contracts/health.go @@ -1,4 +1,4 @@ -package health +package contracts import "context" @@ -6,3 +6,7 @@ type Health interface { CheckHealth(ctx context.Context) error GetHealthName() string } + +type HealthService interface { + CheckHealth(ctx context.Context) Check +} diff --git a/internal/pkg/health/health_params.go b/internal/pkg/health/contracts/health_params.go similarity index 85% rename from internal/pkg/health/health_params.go rename to internal/pkg/health/contracts/health_params.go index 0141da67..a404c377 100644 --- a/internal/pkg/health/health_params.go +++ b/internal/pkg/health/contracts/health_params.go @@ -1,4 +1,4 @@ -package health +package contracts import ( "go.uber.org/fx" diff --git a/internal/pkg/health/status.go b/internal/pkg/health/contracts/status.go similarity index 94% rename from internal/pkg/health/status.go rename to internal/pkg/health/contracts/status.go index d808dfaa..127ae9a7 100644 --- a/internal/pkg/health/status.go +++ b/internal/pkg/health/contracts/status.go @@ -1,4 +1,4 @@ -package health +package contracts const ( StatusUp = "up" diff --git a/internal/pkg/health/health_endpoint.go b/internal/pkg/health/health_endpoint.go index 94d6b785..20fe7363 100644 --- a/internal/pkg/health/health_endpoint.go +++ b/internal/pkg/health/health_endpoint.go @@ -3,32 +3,33 @@ package health import ( "net/http" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + contracts2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/labstack/echo/v4" ) type HealthCheckEndpoint struct { - service HealthService - echoServer customEcho.EchoHttpServer + service contracts2.HealthService + echoServer contracts.EchoHttpServer } func NewHealthCheckEndpoint( - service HealthService, - server customEcho.EchoHttpServer, + service contracts2.HealthService, + server contracts.EchoHttpServer, ) *HealthCheckEndpoint { return &HealthCheckEndpoint{service: service, echoServer: server} } func (s *HealthCheckEndpoint) RegisterEndpoints() { - s.echoServer.GetEchoInstance().Group("").GET("health", s.CheckHealth) + s.echoServer.GetEchoInstance().GET("health", s.checkHealth) } -func (s *HealthCheckEndpoint) CheckHealth(c echo.Context) error { +func (s *HealthCheckEndpoint) checkHealth(c echo.Context) error { check := s.service.CheckHealth(c.Request().Context()) if !check.AllUp() { return c.JSON(http.StatusServiceUnavailable, check) } - err := c.JSON(http.StatusOK, check) - return err + + return c.JSON(http.StatusOK, check) } diff --git a/internal/pkg/health/health_fx.go b/internal/pkg/health/health_fx.go index 2b86b93d..54ddd25c 100644 --- a/internal/pkg/health/health_fx.go +++ b/internal/pkg/health/health_fx.go @@ -4,7 +4,7 @@ import ( "go.uber.org/fx" ) -var Module = fx.Options( +var Module = fx.Options( //nolint:gochecknoglobals fx.Provide( NewHealthService, NewHealthCheckEndpoint, diff --git a/internal/pkg/health/service.go b/internal/pkg/health/service.go index 98a568a5..8cd48179 100644 --- a/internal/pkg/health/service.go +++ b/internal/pkg/health/service.go @@ -2,29 +2,29 @@ package health import ( "context" -) -type HealthService interface { - CheckHealth(ctx context.Context) Check -} + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" +) type healthService struct { - healthParams HealthParams + healthParams contracts.HealthParams } func NewHealthService( - healthParams HealthParams, -) HealthService { + healthParams contracts.HealthParams, +) contracts.HealthService { return &healthService{ healthParams: healthParams, } } -func (service *healthService) CheckHealth(ctx context.Context) Check { - checks := make(Check) +func (service *healthService) CheckHealth(ctx context.Context) contracts.Check { + checks := make(contracts.Check) for _, health := range service.healthParams.Healths { - checks[health.GetHealthName()] = NewStatus(health.CheckHealth(ctx)) + checks[health.GetHealthName()] = contracts.NewStatus( + health.CheckHealth(ctx), + ) } return checks diff --git a/internal/pkg/health/unhealthy.go b/internal/pkg/health/unhealthy.go index 75852493..499d703a 100644 --- a/internal/pkg/health/unhealthy.go +++ b/internal/pkg/health/unhealthy.go @@ -1,16 +1,22 @@ package health -import "context" +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" +) + +type UnhealthyHealthService struct{} func NewUnhealthyHealthService() UnhealthyHealthService { return UnhealthyHealthService{} } -type UnhealthyHealthService struct{} - -func (service UnhealthyHealthService) CheckHealth(context.Context) Check { - return Check{ - "postgres": Status{Status: StatusDown}, - "redis": Status{Status: StatusDown}, +func (service UnhealthyHealthService) CheckHealth( + context.Context, +) contracts.Check { + return contracts.Check{ + "postgres": contracts.Status{Status: contracts.StatusDown}, + "redis": contracts.Status{Status: contracts.StatusDown}, } } diff --git a/internal/pkg/http/custom_echo/constants/constants.go b/internal/pkg/http/custom_echo/constants/constants.go deleted file mode 100644 index 1a04bab6..00000000 --- a/internal/pkg/http/custom_echo/constants/constants.go +++ /dev/null @@ -1,9 +0,0 @@ -package constants - -type otel struct { - HttpErrorMessage string -} - -var Otel = otel{ - HttpErrorMessage: "http.error_message", -} diff --git a/internal/pkg/http/custom_echo/hadnlers/problem_details_handler.go b/internal/pkg/http/custom_echo/hadnlers/problem_details_handler.go deleted file mode 100644 index aa5707f3..00000000 --- a/internal/pkg/http/custom_echo/hadnlers/problem_details_handler.go +++ /dev/null @@ -1,28 +0,0 @@ -package customHadnlers - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/problemDetails" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "github.com/labstack/echo/v4" -) - -func ProblemHandlerFunc(err error, c echo.Context, logger logger.Logger) { - prb := problemDetails.ParseError(err) - - if prb != nil { - if !c.Response().Committed { - if _, err := problemDetails.WriteTo(prb, c.Response()); err != nil { - logger.Error(err) - } - } - } else { - if !c.Response().Committed { - prb := problemDetails.NewInternalServerProblemDetail(err.Error(), errorUtils.ErrorsWithStack(err)) - if _, err := problemDetails.WriteTo(prb, c.Response()); err != nil { - logger.Error(err) - } - } - } -} diff --git a/internal/pkg/http/custom_echo/middlewares/log/log_middleware.go b/internal/pkg/http/custom_echo/middlewares/log/log_middleware.go deleted file mode 100644 index bfe739e7..00000000 --- a/internal/pkg/http/custom_echo/middlewares/log/log_middleware.go +++ /dev/null @@ -1,57 +0,0 @@ -package log - -import ( - "fmt" - "time" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - - "github.com/labstack/echo/v4" -) - -func EchoLogger(logger logger.Logger) echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - start := time.Now() - - err := next(c) - if err != nil { - // handle echo error in this middleware and raise echo errorhandler func and our custom error handler (problem details handler) - c.Error(err) - } - - req := c.Request() - res := c.Response() - - fields := map[string]interface{}{ - "remote_ip": c.RealIP(), - "latency": time.Since(start).String(), - "host": req.Host, - "request": fmt.Sprintf("%s %s", req.Method, req.RequestURI), - "status": res.Status, - "size": res.Size, - "user_agent": req.UserAgent(), - } - - id := req.Header.Get(echo.HeaderXRequestID) - if id == "" { - id = res.Header().Get(echo.HeaderXRequestID) - } - fields["request_id"] = id - - n := res.Status - switch { - case n >= 500: - logger.Errorw("Echo logger middleware: Server error", fields) - case n >= 400: - logger.Errorw("Echo logger middleware: Client error", fields) - case n >= 300: - logger.Errorw("Echo logger middleware: Redirection", fields) - default: - logger.Infow("Echo logger middleware: Success", fields) - } - - return nil - } - } -} diff --git a/internal/pkg/http/custom_echo/middlewares/otel_metrics/request_status_middleware.go b/internal/pkg/http/custom_echo/middlewares/otel_metrics/request_status_middleware.go deleted file mode 100644 index 6b2139a1..00000000 --- a/internal/pkg/http/custom_echo/middlewares/otel_metrics/request_status_middleware.go +++ /dev/null @@ -1,52 +0,0 @@ -package otelMetrics - -// ref:https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go - -import ( - "fmt" - - "github.com/labstack/echo/v4" - "go.opentelemetry.io/otel/attribute" - api "go.opentelemetry.io/otel/metric" -) - -var ( - successCounter api.Float64Counter - errorCounter api.Float64Counter -) - -// Middleware adds request status metrics to the otel -// ref: https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go -func Middleware(meter api.Meter, serviceName string) echo.MiddlewareFunc { - errorCounter, _ := meter.Float64Counter( - fmt.Sprintf("%s_error_http_requests_total", serviceName), - api.WithDescription("The total number of error http requests"), - ) - successCounter, _ = meter.Float64Counter( - fmt.Sprintf("%s_success_http_requests_total", serviceName), - api.WithDescription("The total number of success http requests"), - ) - - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - err := next(c) - request := c.Request() - ctx := request.Context() - - attrs := api.WithAttributes( - attribute.Key("MetricsType").String("Http"), - ) - - if err != nil { - errorCounter.Add(ctx, 1, attrs) - } else { - successCounter.Add(ctx, 1, attrs) - } - - // update request context - c.SetRequest(request.WithContext(ctx)) - - return err - } - } -} diff --git a/internal/pkg/http/custom_echo/otel/tracing/utils.go b/internal/pkg/http/custom_echo/otel/tracing/utils.go deleted file mode 100644 index 351ff2ac..00000000 --- a/internal/pkg/http/custom_echo/otel/tracing/utils.go +++ /dev/null @@ -1,59 +0,0 @@ -package tracing - -import ( - "context" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/constants" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/problemDetails" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - "go.opentelemetry.io/otel/trace" -) - -// TraceHttpErrFromSpan setting span with status error with error message -func TraceHttpErrFromSpan(span trace.Span, err error) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(constants.Otel.HttpErrorMessage, stackTraceError)) - if customErrors.IsCustomError(err) { - httpError := problemDetails.ParseError(err) - span.SetAttributes(semconv.HTTPStatusCode(httpError.GetStatus())) - } - span.RecordError(err) - } - - return err -} - -// TraceHttpErrFromSpanWithCode setting span with status error with error message -func TraceHttpErrFromSpanWithCode(span trace.Span, err error, code int) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(semconv.HTTPStatusCode(code)) - span.SetAttributes(attribute.String(constants.Otel.HttpErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} - -func TraceHttpErrFromContext(ctx context.Context, err error) error { - // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors - span := trace.SpanFromContext(ctx) - defer span.End() - - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(constants.Otel.HttpErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} diff --git a/internal/pkg/http/custom_echo/config/echo_http_options.go b/internal/pkg/http/customecho/config/echo_http_options.go similarity index 84% rename from internal/pkg/http/custom_echo/config/echo_http_options.go rename to internal/pkg/http/customecho/config/echo_http_options.go index fbce4deb..5c8e8558 100644 --- a/internal/pkg/http/custom_echo/config/echo_http_options.go +++ b/internal/pkg/http/customecho/config/echo_http_options.go @@ -5,13 +5,13 @@ import ( "net/url" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[EchoHttpOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[EchoHttpOptions]()) type EchoHttpOptions struct { Port string `mapstructure:"port" validate:"required" env:"TcpPort"` @@ -21,7 +21,7 @@ type EchoHttpOptions struct { IgnoreLogUrls []string `mapstructure:"ignoreLogUrls"` Timeout int `mapstructure:"timeout" env:"Timeout"` Host string `mapstructure:"host" env:"Host"` - Name string `mapstructure:"name" env:"Name"` + Name string `mapstructure:"name" env:"ShortTypeName"` } func (c *EchoHttpOptions) Address() string { @@ -36,6 +36,6 @@ func (c *EchoHttpOptions) BasePathAddress() string { return path } -func ProvideConfig(environment environemnt.Environment) (*EchoHttpOptions, error) { +func ProvideConfig(environment environment.Environment) (*EchoHttpOptions, error) { return config.BindConfigKey[*EchoHttpOptions](optionName, environment) } diff --git a/internal/pkg/http/customecho/contracts/echoserver.go b/internal/pkg/http/customecho/contracts/echoserver.go new file mode 100644 index 00000000..bc4d22cf --- /dev/null +++ b/internal/pkg/http/customecho/contracts/echoserver.go @@ -0,0 +1,23 @@ +package contracts + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + "github.com/labstack/echo/v4" +) + +type EchoHttpServer interface { + RunHttpServer(configEcho ...func(echo *echo.Echo)) error + GracefulShutdown(ctx context.Context) error + ApplyVersioningFromHeader() + GetEchoInstance() *echo.Echo + Logger() logger.Logger + Cfg() *config.EchoHttpOptions + SetupDefaultMiddlewares() + RouteBuilder() *RouteBuilder + AddMiddlewares(middlewares ...echo.MiddlewareFunc) + ConfigGroup(groupName string, groupFunc func(group *echo.Group)) +} diff --git a/internal/pkg/http/custom_echo/route_builder.go b/internal/pkg/http/customecho/contracts/route_builder.go similarity index 96% rename from internal/pkg/http/custom_echo/route_builder.go rename to internal/pkg/http/customecho/contracts/route_builder.go index ba9cc52a..2bfb739a 100644 --- a/internal/pkg/http/custom_echo/route_builder.go +++ b/internal/pkg/http/customecho/contracts/route_builder.go @@ -1,4 +1,4 @@ -package customEcho +package contracts import "github.com/labstack/echo/v4" diff --git a/internal/pkg/http/custom_echo/custom_echo_fx.go b/internal/pkg/http/customecho/custom_echo_fx.go similarity index 88% rename from internal/pkg/http/custom_echo/custom_echo_fx.go rename to internal/pkg/http/customecho/custom_echo_fx.go index c86ccafc..d0baf920 100644 --- a/internal/pkg/http/custom_echo/custom_echo_fx.go +++ b/internal/pkg/http/customecho/custom_echo_fx.go @@ -5,7 +5,8 @@ import ( "errors" "net/http" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "go.uber.org/fx" @@ -42,7 +43,11 @@ var ( ) // we don't want to register any dependencies here, its func body should execute always even we don't request for that, so we should use `invoke` -func registerHooks(lc fx.Lifecycle, echoServer EchoHttpServer, logger logger.Logger) { +func registerHooks( + lc fx.Lifecycle, + echoServer contracts.EchoHttpServer, + logger logger.Logger, +) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { // https://github.com/uber-go/fx/blob/v1.20.0/app.go#L573 @@ -52,7 +57,10 @@ func registerHooks(lc fx.Lifecycle, echoServer EchoHttpServer, logger logger.Log go func() { // https://medium.com/@mokiat/proper-http-shutdown-in-go-bd3bfaade0f2 // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn’t exit and waits instead for Shutdown to return. - if err := echoServer.RunHttpServer(); !errors.Is(err, http.ErrServerClosed) { + if err := echoServer.RunHttpServer(); !errors.Is( + err, + http.ErrServerClosed, + ) { // do a fatal for going to OnStop process logger.Fatalf( "(EchoHttpServer.RunHttpServer) error in running server: {%v}", @@ -75,7 +83,8 @@ func registerHooks(lc fx.Lifecycle, echoServer EchoHttpServer, logger logger.Log // https://medium.com/@mokiat/proper-http-shutdown-in-go-bd3bfaade0f2 // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn’t exit and waits instead for Shutdown to return. if err := echoServer.GracefulShutdown(ctx); err != nil { - echoServer.Logger().Errorf("error shutting down echo server: %v", err) + echoServer.Logger(). + Errorf("error shutting down echo server: %v", err) } else { echoServer.Logger().Info("echo server shutdown gracefully") } diff --git a/internal/pkg/http/custom_echo/echo_server.go b/internal/pkg/http/customecho/echo_server.go similarity index 56% rename from internal/pkg/http/custom_echo/echo_server.go rename to internal/pkg/http/customecho/echo_server.go index fe8349e5..83b25d98 100644 --- a/internal/pkg/http/custom_echo/echo_server.go +++ b/internal/pkg/http/customecho/echo_server.go @@ -6,15 +6,18 @@ import ( "strings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/config" - customHadnlers "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/hadnlers" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/middlewares/log" - otelMetrics "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/middlewares/otel_metrics" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" + hadnlers "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/hadnlers" + ipratelimit "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/middlewares/ip_ratelimit" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/middlewares/log" + otelMetrics "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/middlewares/otel_metrics" + oteltracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/middlewares/otel_tracing" + problemdetail "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/middlewares/problem_detail" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho" "go.opentelemetry.io/otel/metric" ) @@ -23,36 +26,23 @@ type echoHttpServer struct { config *config.EchoHttpOptions log logger.Logger meter metric.Meter - routeBuilder *RouteBuilder -} - -type EchoHttpServer interface { - RunHttpServer(configEcho ...func(echo *echo.Echo)) error - GracefulShutdown(ctx context.Context) error - ApplyVersioningFromHeader() - GetEchoInstance() *echo.Echo - Logger() logger.Logger - Cfg() *config.EchoHttpOptions - SetupDefaultMiddlewares() - RouteBuilder() *RouteBuilder - AddMiddlewares(middlewares ...echo.MiddlewareFunc) - ConfigGroup(groupName string, groupFunc func(group *echo.Group)) + routeBuilder *contracts.RouteBuilder } func NewEchoHttpServer( config *config.EchoHttpOptions, logger logger.Logger, meter metric.Meter, -) EchoHttpServer { +) contracts.EchoHttpServer { e := echo.New() - e.HideBanner = false + e.HideBanner = true return &echoHttpServer{ echo: e, config: config, log: logger, meter: meter, - routeBuilder: NewRouteBuilder(e), + routeBuilder: contracts.NewRouteBuilder(e), } } @@ -82,11 +72,14 @@ func (s *echoHttpServer) Cfg() *config.EchoHttpOptions { return s.config } -func (s *echoHttpServer) RouteBuilder() *RouteBuilder { +func (s *echoHttpServer) RouteBuilder() *contracts.RouteBuilder { return s.routeBuilder } -func (s *echoHttpServer) ConfigGroup(groupName string, groupFunc func(group *echo.Group)) { +func (s *echoHttpServer) ConfigGroup( + groupName string, + groupFunc func(group *echo.Group), +) { groupFunc(s.echo.Group(groupName)) } @@ -106,48 +99,50 @@ func (s *echoHttpServer) GracefulShutdown(ctx context.Context) error { } func (s *echoHttpServer) SetupDefaultMiddlewares() { + skipper := func(c echo.Context) bool { + return strings.Contains(c.Request().URL.Path, "swagger") || + strings.Contains(c.Request().URL.Path, "metrics") || + strings.Contains(c.Request().URL.Path, "health") || + strings.Contains(c.Request().URL.Path, "favicon.ico") + } + // set error handler s.echo.HTTPErrorHandler = func(err error, c echo.Context) { - // bypass notfound favicon endpoint and its error - if c.Request().URL.Path == "/favicon.ico" { + // bypass skip endpoints and its error + if skipper(c) { return } - customHadnlers.ProblemHandlerFunc(err, c, s.log) - } - // log errors and information - s.echo.Use(log.EchoLogger(s.log)) - s.echo.Use(otelecho.Middleware(s.config.Name)) - // Because we use metrics server middleware, if it is not available, our echo will not work. - if s.meter != nil { - s.echo.Use(otelMetrics.Middleware(s.meter, s.config.Name)) + hadnlers.ProblemDetailErrorHandlerFunc(err, c, s.log) } - s.echo.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ - LogContentLength: true, - LogLatency: true, - LogError: false, - LogMethod: true, - LogRequestID: true, - LogURI: true, - LogResponseSize: true, - LogURIPath: true, - LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { - s.log.Infow( - fmt.Sprintf("[Request Middleware] REQUEST: uri: %v, status: %v\n", v.URI, v.Status), - logger.Fields{"URI": v.URI, "Status": v.Status}, - ) - return nil - }, - })) + // log errors and information + s.echo.Use( + log.EchoLogger( + s.log, + log.WithSkipper(skipper), + ), + ) + s.echo.Use( + oteltracing.HttpTrace( + oteltracing.WithSkipper(skipper), + oteltracing.WithServiceName(s.config.Name), + ), + ) + s.echo.Use( + otelMetrics.HTTPMetrics( + otelMetrics.WithServiceName(s.config.Name), + otelMetrics.WithSkipper(skipper)), + ) s.echo.Use(middleware.BodyLimit(constants.BodyLimit)) + s.echo.Use(ipratelimit.IPRateLimit()) s.echo.Use(middleware.RequestID()) s.echo.Use(middleware.GzipWithConfig(middleware.GzipConfig{ - Level: constants.GzipLevel, - Skipper: func(c echo.Context) bool { - return strings.Contains(c.Request().URL.Path, "swagger") - }, + Level: constants.GzipLevel, + Skipper: skipper, })) + // should be last middleware + s.echo.Use(problemdetail.ProblemDetail(problemdetail.WithSkipper(skipper))) } func (s *echoHttpServer) ApplyVersioningFromHeader() { diff --git a/internal/pkg/http/customecho/hadnlers/problemdetail_error_handler.go b/internal/pkg/http/customecho/hadnlers/problemdetail_error_handler.go new file mode 100644 index 00000000..bc291f20 --- /dev/null +++ b/internal/pkg/http/customecho/hadnlers/problemdetail_error_handler.go @@ -0,0 +1,29 @@ +package handlers + +import ( + problemDetails "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/problemdetails" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + "emperror.dev/errors" + "github.com/labstack/echo/v4" +) + +func ProblemDetailErrorHandlerFunc( + err error, + c echo.Context, + logger logger.Logger, +) { + var problem problemDetails.ProblemDetailErr + + // if error was not problem detail we will convert the error to a problem detail + if ok := errors.As(err, &problem); !ok { + problem = problemDetails.ParseError(err) + } + + if !c.Response().Committed && problem != nil { + // `WriteTo` will set `Response status code` to our problem details status + if _, err := problemDetails.WriteTo(problem, c.Response()); err != nil { + logger.Error(err) + } + } +} diff --git a/internal/pkg/http/customecho/middlewares/ip_ratelimit/config.go b/internal/pkg/http/customecho/middlewares/ip_ratelimit/config.go new file mode 100644 index 00000000..ddc919a8 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/ip_ratelimit/config.go @@ -0,0 +1,41 @@ +package ipratelimit + +import ( + "time" +) + +type config struct { + period time.Duration + limit int64 +} + +var defualtConfig = config{ + period: 1 * time.Hour, + limit: 1000, +} + +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +func WithPeriod(d time.Duration) Option { + return optionFunc(func(cfg *config) { + if cfg.period != 0 { + cfg.period = d + } + }) +} + +func WithLimit(v int64) Option { + return optionFunc(func(cfg *config) { + if cfg.limit != 0 { + cfg.limit = v + } + }) +} diff --git a/internal/pkg/http/customecho/middlewares/ip_ratelimit/ip_ratelimit.go b/internal/pkg/http/customecho/middlewares/ip_ratelimit/ip_ratelimit.go new file mode 100644 index 00000000..df833a22 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/ip_ratelimit/ip_ratelimit.go @@ -0,0 +1,79 @@ +package ipratelimit + +import ( + "log" + "net/http" + "strconv" + + "github.com/labstack/echo/v4" + "github.com/ulule/limiter/v3" + "github.com/ulule/limiter/v3/drivers/store/memory" +) + +// ref: https://github.com/ulule/limiter-examples/blob/master/echo/main.go + +func IPRateLimit(opts ...Option) echo.MiddlewareFunc { + config := defualtConfig + + for _, opt := range opts { + opt.apply(&config) + } + + rate := limiter.Rate{ + Period: config.period, + Limit: config.limit, + } + + var ( + ipRateLimiter *limiter.Limiter + store limiter.Store + ) + + store = memory.NewStore() + ipRateLimiter = limiter.New(store, rate) + + // 2. Return middleware handler + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) (err error) { + ip := c.RealIP() + limiterCtx, err := ipRateLimiter.Get(c.Request().Context(), ip) + if err != nil { + log.Printf( + "IPRateLimit - ipRateLimiter.Get - err: %v, %s on %s", + err, + ip, + c.Request().URL, + ) + + return c.JSON(http.StatusInternalServerError, echo.Map{ + "success": false, + "message": err, + }) + } + + h := c.Response().Header() + h.Set("X-RateLimit-Limit", strconv.FormatInt(limiterCtx.Limit, 10)) + h.Set( + "X-RateLimit-Remaining", + strconv.FormatInt(limiterCtx.Remaining, 10), + ) + h.Set("X-RateLimit-Reset", strconv.FormatInt(limiterCtx.Reset, 10)) + + if limiterCtx.Reached { + log.Printf( + "Too Many Requests from %s on %s", + ip, + c.Request().URL, + ) + + return c.JSON(http.StatusTooManyRequests, echo.Map{ + "success": false, + "message": "Too Many Requests on " + c.Request().URL.String(), + }) + } + + // log.Printf("%s request continue", c.RealIP()) + return next(c) + } + } +} diff --git a/internal/pkg/http/customecho/middlewares/log/config.go b/internal/pkg/http/customecho/middlewares/log/config.go new file mode 100644 index 00000000..5e4cfbc1 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/log/config.go @@ -0,0 +1,27 @@ +package log + +import "github.com/labstack/echo/v4/middleware" + +// config defines the config for Logger middleware. +type config struct { + // Skipper defines a function to skip middleware. + Skipper middleware.Skipper +} + +// Option specifies instrumentation configuration options. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +// WithSkipper specifies a skipper for allowing requests to skip generating spans. +func WithSkipper(skipper middleware.Skipper) Option { + return optionFunc(func(cfg *config) { + cfg.Skipper = skipper + }) +} diff --git a/internal/pkg/http/customecho/middlewares/log/log_middleware.go b/internal/pkg/http/customecho/middlewares/log/log_middleware.go new file mode 100644 index 00000000..b179269c --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/log/log_middleware.go @@ -0,0 +1,126 @@ +package log + +import ( + "fmt" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +// EchoLogger returns echo middleware which will log incoming requests. +func EchoLogger(l logger.Logger, opts ...Option) echo.MiddlewareFunc { + cfg := config{} + for _, opt := range opts { + opt.apply(&cfg) + } + + if cfg.Skipper == nil { + cfg.Skipper = middleware.DefaultSkipper + } + + requestMiddleware := middleware.RequestLoggerWithConfig( + middleware.RequestLoggerConfig{ + Skipper: cfg.Skipper, + LogRequestID: true, + LogRemoteIP: true, + LogHost: true, + LogMethod: true, + LogURI: true, + LogUserAgent: true, + LogStatus: true, + LogError: true, + LogLatency: true, + LogContentLength: true, + LogResponseSize: true, + + LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { + l.Infow( + fmt.Sprintf( + "[Request Middleware] REQUEST: uri: %v, status: %v\n", + v.URI, + v.Status, + ), + logger.Fields{ + "uri": v.URI, + "status": v.Status, + "id": v.RequestID, + "remote_ip": v.RemoteIP, + "host": v.Host, + "method": v.Method, + "user_agent": v.UserAgent, + "error": v.Error, + "latency": v.Latency.Nanoseconds(), + "latency_human": v.Latency.String(), + "bytes_in": v.ContentLength, + "bytes_out": v.ResponseSize, + }, + ) + + return nil + }, + }, + ) + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if cfg.Skipper(c) { + return requestMiddleware(next)(c) + } + + start := time.Now() + + err := requestMiddleware(next)(c) + if err != nil { + // handle echo error in this middleware and raise echo errorhandler func and our custom error handler + // when we call c.Error more than once, `c.Response().Committed` becomes true and response doesn't write to client again in our error handler + // Error will update response status with occurred error object status code + c.Error(err) + } + + req := c.Request() + res := c.Response() + + fields := map[string]interface{}{ + "remote_ip": c.RealIP(), + "latency": time.Since(start).String(), + "host": req.Host, + "request": fmt.Sprintf("%s %s", req.Method, req.RequestURI), + "status": res.Status, + "size": res.Size, + "user_agent": req.UserAgent(), + } + + id := req.Header.Get(echo.HeaderXRequestID) + if id == "" { + id = res.Header().Get(echo.HeaderXRequestID) + } + fields["request_id"] = id + + n := res.Status + switch { + case n >= 500: + l.Errorw( + "EchoServer logger middleware: Server error", + fields, + ) + case n >= 400: + l.Errorw( + "EchoServer logger middleware: Client error", + fields, + ) + case n >= 300: + l.Errorw( + "EchoServer logger middleware: Redirection", + fields, + ) + default: + l.Infow("EchoServer logger middleware: Success", fields) + } + + return nil + } + } +} diff --git a/internal/pkg/http/customecho/middlewares/otel_metrics/config.go b/internal/pkg/http/customecho/middlewares/otel_metrics/config.go new file mode 100644 index 00000000..52bb3009 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_metrics/config.go @@ -0,0 +1,110 @@ +package otelmetrics + +import ( + "github.com/labstack/echo/v4/middleware" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +type config struct { + metricsProvider metric.MeterProvider + + skipper middleware.Skipper + + namespace string + + serviceName string + + instrumentationName string + + // enableTotalMetric whether to enable a metric to count the total number of http requests. + enableTotalMetric bool + + // enableDurMetric whether to enable a metric to track the duration of each request. + enableDurMetric bool + + // enableDurMetric whether to enable a metric that tells the number of current in-flight requests. + enableInFlightMetric bool +} + +var defualtConfig = config{ + metricsProvider: otel.GetMeterProvider(), + enableTotalMetric: true, + enableDurMetric: true, + enableInFlightMetric: true, + skipper: middleware.DefaultSkipper, + serviceName: "application", + instrumentationName: "echo", +} + +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +// WithNamespace will set the metrics namespace that will be added to all metric configurations. It will be a prefix to each +// metric name. For example, if namespace is "myapp", then requests_total metric will be myapp_http_requests_total +// (after namespace there is also the subsystem prefix, "http" in this case). +func WithNamespace(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.namespace != "" { + cfg.namespace = v + } + }) +} + +func WithServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} + +func WithInstrumentationName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.instrumentationName != "" { + cfg.instrumentationName = v + } + }) +} + +// WithSkipper specifies a skipper for allowing requests to skip generating spans. +func WithSkipper(skipper middleware.Skipper) Option { + return optionFunc(func(cfg *config) { + cfg.skipper = skipper + }) +} + +// WithMeterProvider specifies a meter provider to use for creating a metrics. +// If none is specified, the global provider is used. +func WithMeterProvider(provider metric.MeterProvider) Option { + return optionFunc(func(cfg *config) { + if provider != nil { + cfg.metricsProvider = provider + } + }) +} + +func WithInFlightMetric(enabled bool) Option { + return optionFunc(func(cfg *config) { + cfg.enableInFlightMetric = enabled + }) +} + +func WithTotalMetric(enabled bool) Option { + return optionFunc(func(cfg *config) { + cfg.enableTotalMetric = enabled + }) +} + +func WithDurMetric(enabled bool) Option { + return optionFunc(func(cfg *config) { + cfg.enableDurMetric = enabled + }) +} diff --git a/internal/pkg/http/customecho/middlewares/otel_metrics/metric.go b/internal/pkg/http/customecho/middlewares/otel_metrics/metric.go new file mode 100644 index 00000000..3cf76957 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_metrics/metric.go @@ -0,0 +1,377 @@ +package otelmetrics + +// ref:https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go +// https://github.com/labstack/echo-contrib/blob/master/prometheus/prometheus.go +// https://github.com/worldline-go/tell/tree/main/metric/metricecho +// https://opentelemetry.io/docs/instrumentation/go/manual/#metrics +// https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions/http-metrics/ + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/labstack/echo/v4" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +// HTTPLabels will contain HTTP label values for each added metric. Not all labels apply to all metrics, read the +// documentation in each metric method to find out which labels are available for that metric. +type HTTPLabels struct { + // Method should be the HTTP method in the HTTP request. + Method string + // Code should be the HTTP status code in the HTTP response. If there is no response, the Code should be 0. + Code int + // Path is the request URL's path. Should not contain the query string, and ideally it should only be the route + // definition. For example `/users/{ID}` instead of `/users/100`. + Path string + Host string +} + +// HTTPMetricsRecorder is a recorder of HTTP metrics for prometheus. Use NewHTTPMetricsRecorder to initialize it. +type HTTPMetricsRecorder struct { + cfg config + mp metric.MeterProvider + meter metric.Meter + reqTotal metric.Int64Counter + reqDuration metric.Float64Histogram + reqInFlight metric.Int64UpDownCounter + resSize metric.Int64Histogram + reqSize metric.Int64Histogram + errorCounter metric.Float64Counter + successCounter metric.Float64Counter +} + +// NewHTTPMetricsRecorder creates a new HTTPMetricsRecorder. Calling this function will automatically register the new metrics to reg. +func NewHTTPMetricsRecorder(cfg config) *HTTPMetricsRecorder { + // Meter can be a global/package variable. + meter := cfg.metricsProvider.Meter(cfg.instrumentationName) + + m := HTTPMetricsRecorder{ + cfg: cfg, + mp: cfg.metricsProvider, + meter: meter, + } + + if err := m.register(); err != nil { + // possible errors here include duplicate metric or same metrics with inconsistent labels or help strings. It is + // unlikely that it will happen if not by mistake. Nonetheless, we would like to know if such case occurs, hence + // a panic + panic(err) + } + + return &m +} + +func (h *HTTPMetricsRecorder) namespacedValue(v string) string { + if h.cfg.namespace != "" { + return h.cfg.namespace + "_" + v + } + + return v +} + +func (h *HTTPMetricsRecorder) register() error { + // https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions/http-metrics/#http-server + errorCounter, err := h.meter.Float64Counter( + "http.server.total_error_request", + metric.WithUnit("count"), + metric.WithDescription("The total number of error http requests"), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.total_error_request", + err, + ) + } + + h.errorCounter = errorCounter + + successCounter, err := h.meter.Float64Counter( + "http.server.total_success_request", + metric.WithUnit("count"), + metric.WithDescription("The total number of success http requests"), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.total_success_request", + err, + ) + } + + h.successCounter = successCounter + + if h.cfg.enableTotalMetric { + reqTotal, err := h.meter.Int64Counter( + h.namespacedValue("http.server.total_request"), + metric.WithUnit("count"), + metric.WithDescription("The total number of requests"), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.total_request", + err, + ) + } + + h.reqTotal = reqTotal + } + + if h.cfg.enableDurMetric { + reqDuration, err := h.meter.Float64Histogram( + h.namespacedValue("http.server.duration"), + metric.WithUnit("s"), // Specify the unit as "seconds" + metric.WithDescription( + "The total duration of a request in seconds", + ), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.duration", + err, + ) + } + + h.reqDuration = reqDuration + } + + if h.cfg.enableInFlightMetric { + reqInFlight, err := h.meter.Int64UpDownCounter( + h.namespacedValue("http.server.request_inflight_total"), + metric.WithUnit("count"), + metric.WithDescription("The current number of in-flight requests"), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.request_inflight_total", + err, + ) + } + + h.reqInFlight = reqInFlight + } + + resSize, err := h.meter.Int64Histogram( + h.namespacedValue("http.server.response.size"), + metric.WithUnit("bytes"), + metric.WithDescription("The HTTP response sizes in bytes."), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.response.size", + err, + ) + } + + h.resSize = resSize + + reqSize, err := h.meter.Int64Histogram( + h.namespacedValue("http.server.request.size"), + metric.WithUnit("bytes"), + metric.WithDescription("The HTTP request sizes in bytes."), + ) + if err != nil { + return fmt.Errorf( + "meter %s cannot set; %w", + "http.server.request.size", + err, + ) + } + + h.reqSize = reqSize + + return nil +} + +// AddRequestToTotal adds 1 to the total number of requests. All labels should be specified. +func (h *HTTPMetricsRecorder) AddRequestToTotal( + ctx context.Context, + values HTTPLabels, +) { + if h.reqTotal == nil { + return + } + + h.reqTotal.Add(ctx, 1, + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.Int("code", values.Code), + attribute.String("type", "Http"), + ), + ) +} + +// AddRequestDuration registers a request along with its duration. All labels should be specified. +func (h *HTTPMetricsRecorder) AddRequestDuration( + ctx context.Context, + duration time.Duration, + values HTTPLabels, +) { + if h.reqDuration == nil { + return + } + + h.reqDuration.Record( + ctx, duration.Seconds(), + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("host", values.Host), + attribute.String("path", values.Path), + attribute.Int("code", values.Code), + attribute.String("type", "Http"), + ), + ) +} + +// AddInFlightRequest Adds 1 to the number of current in-flight requests. All labels should be specified except for +// `Code`, as it will just be ignored. To remove a request use RemInFlightRequest. +func (h *HTTPMetricsRecorder) AddInFlightRequest( + ctx context.Context, + values HTTPLabels, +) { + if h.reqInFlight == nil { + return + } + + h.reqInFlight.Add( + ctx, + 1, + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + ), + ) +} + +func (h *HTTPMetricsRecorder) AddRequestError( + ctx context.Context, + values HTTPLabels, +) { + if h.errorCounter == nil { + return + } + + h.errorCounter.Add( + ctx, + 1, + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + attribute.Int("code", values.Code), + ), + ) +} + +func (h *HTTPMetricsRecorder) AddRequestSuccess( + ctx context.Context, + values HTTPLabels, +) { + if h.successCounter == nil { + return + } + + h.successCounter.Add( + ctx, + 1, + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + attribute.Int("code", values.Code), + ), + ) +} + +// RemInFlightRequest Remove 1 from the number of current in-flight requests. All labels should be specified except +// for `Code`, as it will just be ignored. Labels should match the ones passed to the equivalent AddInFlightRequest call. +func (h *HTTPMetricsRecorder) RemInFlightRequest( + ctx context.Context, + values HTTPLabels, +) { + if h.reqInFlight == nil { + return + } + + h.reqInFlight.Add( + ctx, + -1, + metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + ), + ) +} + +func (h *HTTPMetricsRecorder) AddRequestSize( + ctx context.Context, + request *http.Request, + values HTTPLabels, +) { + if h.reqSize == nil { + return + } + + size := computeApproximateRequestSize(request) + h.reqSize.Record(ctx, int64(size), metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + attribute.String("host", values.Host), + attribute.Int("code", values.Code), + )) +} + +func (h *HTTPMetricsRecorder) AddResponseSize( + ctx context.Context, + response *echo.Response, + values HTTPLabels, +) { + if h.resSize == nil { + return + } + + size := response.Size + h.resSize.Record(ctx, size, metric.WithAttributes( + attribute.String("method", values.Method), + attribute.String("path", values.Path), + attribute.String("type", "Http"), + attribute.String("host", values.Host), + attribute.Int("code", values.Code), + )) +} + +func computeApproximateRequestSize(r *http.Request) int { + s := 0 + if r.URL != nil { + s = len(r.URL.Path) + } + + s += len(r.Method) + s += len(r.Proto) + for name, values := range r.Header { + s += len(name) + for _, value := range values { + s += len(value) + } + } + s += len(r.Host) + + // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. + + if r.ContentLength != -1 { + s += int(r.ContentLength) + } + + return s +} diff --git a/internal/pkg/http/customecho/middlewares/otel_metrics/metrics_middleware.go b/internal/pkg/http/customecho/middlewares/otel_metrics/metrics_middleware.go new file mode 100644 index 00000000..3312f1f5 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_metrics/metrics_middleware.go @@ -0,0 +1,79 @@ +package otelmetrics + +// ref:https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go +// https://github.com/labstack/echo-contrib/blob/master/prometheus/prometheus.go +// https://github.com/worldline-go/tell/tree/main/metric/metricecho +// https://opentelemetry.io/docs/instrumentation/go/manual/#metrics + +// https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions/http-metrics/ + +import ( + "time" + + "github.com/labstack/echo/v4" +) + +// HTTPMetrics is a middleware for adding otel metrics for a given request +// If recorder config is nil, the middleware will use a recorder with default configuration. +func HTTPMetrics(opts ...Option) echo.MiddlewareFunc { + config := defualtConfig + + for _, opt := range opts { + opt.apply(&config) + } + + httpMetricsRecorder := NewHTTPMetricsRecorder(config) + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) (err error) { + if config.skipper(c) { + return next(c) + } + + request := c.Request() + ctx := request.Context() + + values := HTTPLabels{ + Method: request.Method, + Path: c.Path(), + Host: request.URL.Host, + } + + httpMetricsRecorder.AddInFlightRequest(ctx, values) + + start := time.Now() + + defer func() { + elapsed := time.Since(start) + + values.Code = c.Response().Status + + httpMetricsRecorder.AddRequestToTotal(ctx, values) + + httpMetricsRecorder.AddRequestDuration(ctx, elapsed, values) + + httpMetricsRecorder.RemInFlightRequest(ctx, values) + + httpMetricsRecorder.AddRequestSize(ctx, request, values) + + httpMetricsRecorder.AddResponseSize(ctx, c.Response(), values) + + if err != nil { + httpMetricsRecorder.AddRequestError(ctx, values) + } else { + httpMetricsRecorder.AddRequestSuccess(ctx, values) + } + }() + + err = next(c) + if err != nil { + // handle echo error in this middleware and raise echo errorhandler func and our custom error handler + // when we call c.Error more than once, `c.Response().Committed` becomes true and response doesn't write to client again in our error handler + // Error will update response status with occurred error object status code + c.Error(err) + } + + return err + } + } +} diff --git a/internal/pkg/http/customecho/middlewares/otel_metrics/view.go b/internal/pkg/http/customecho/middlewares/otel_metrics/view.go new file mode 100644 index 00000000..c2dc5532 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_metrics/view.go @@ -0,0 +1,33 @@ +package otelmetrics + +import ( + "go.opentelemetry.io/otel/sdk/metric" +) + +// https://opentelemetry.io/docs/instrumentation/go/manual/#registering-views +func GetViews() []metric.View { + customBucketView := metric.NewView( + metric.Instrument{ + Name: "*request_duration_seconds", + }, + metric.Stream{ + Aggregation: metric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{ + .005, + .01, + .025, + .05, + .1, + .25, + .5, + 1, + 2.5, + 5, + 10, + }, + }, + }, + ) + + return []metric.View{customBucketView} +} diff --git a/internal/pkg/http/customecho/middlewares/otel_tracing/config.go b/internal/pkg/http/customecho/middlewares/otel_tracing/config.go new file mode 100644 index 00000000..e01d1641 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_tracing/config.go @@ -0,0 +1,82 @@ +package oteltracing + +import ( + "github.com/labstack/echo/v4/middleware" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + oteltrace "go.opentelemetry.io/otel/trace" +) + +// Ref: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/github.com/labstack/echo/otelecho/echo.go + +// config is used to configure the mux middleware. +type config struct { + tracerProvider oteltrace.TracerProvider + propagators propagation.TextMapPropagator + skipper middleware.Skipper + instrumentationName string + serviceName string +} + +// Option specifies instrumentation configuration options. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +var defualtConfig = config{ + tracerProvider: otel.GetTracerProvider(), + propagators: otel.GetTextMapPropagator(), + skipper: middleware.DefaultSkipper, + instrumentationName: "echo", + serviceName: "app", +} + +// WithPropagators specifies propagators to use for extracting +// information from the HTTP requests. If none are specified, global +// ones will be used. +func WithPropagators(propagators propagation.TextMapPropagator) Option { + return optionFunc(func(cfg *config) { + if propagators != nil { + cfg.propagators = propagators + } + }) +} + +// WithTracerProvider specifies a tracer provider to use for creating a tracer. +// If none is specified, the global provider is used. +func WithTracerProvider(provider oteltrace.TracerProvider) Option { + return optionFunc(func(cfg *config) { + if provider != nil { + cfg.tracerProvider = provider + } + }) +} + +// WithSkipper specifies a skipper for allowing requests to skip generating spans. +func WithSkipper(skipper middleware.Skipper) Option { + return optionFunc(func(cfg *config) { + cfg.skipper = skipper + }) +} + +func WithInstrumentationName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.instrumentationName != "" { + cfg.instrumentationName = v + } + }) +} + +func WithServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} diff --git a/internal/pkg/http/customecho/middlewares/otel_tracing/tracing_middleware.go b/internal/pkg/http/customecho/middlewares/otel_tracing/tracing_middleware.go new file mode 100644 index 00000000..f5c58591 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/otel_tracing/tracing_middleware.go @@ -0,0 +1,96 @@ +package oteltracing + +// Ref: https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/instrumentation/github.com/labstack/echo/otelecho/echo.go +// Note: for consideration of 4xx status as error in traces, I customized original echo oteltrace middleware for handing my requirements + +// https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/http/ + +import ( + "fmt" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" + + "github.com/labstack/echo/v4" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/semconv/v1.20.0/httpconv" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + oteltrace "go.opentelemetry.io/otel/trace" +) + +// HttpTrace returns echo middleware which will trace incoming requests. +func HttpTrace(opts ...Option) echo.MiddlewareFunc { + cfg := defualtConfig + for _, opt := range opts { + opt.apply(&cfg) + } + + tracer := cfg.tracerProvider.Tracer(cfg.instrumentationName) + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if cfg.skipper(c) { + return next(c) + } + + c.Set(cfg.instrumentationName, tracer) + request := c.Request() + // doesn't contain trace information and after completing trace on new ctx we should go back to our old savedCtx + savedCtx := request.Context() + + defer func() { + // we should go back to previous context in end of our operation because new context contains child spans, and if we don't set it back to previous context, after returning from this method all further parent spans becomes a new child for existing child span! + request = request.WithContext(savedCtx) + c.SetRequest(request) + }() + + // create new ctx from existing savedCtx + ctx := cfg.propagators.Extract( + savedCtx, + propagation.HeaderCarrier(request.Header), + ) + + // //https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md + // httpconv doesn't exist in semconv v1.21.0 we have to use v1.20.0 for that + // https://github.com/open-telemetry/opentelemetry-go/pull/4362 + opts := []oteltrace.SpanStartOption{ + oteltrace.WithAttributes( + httpconv.ServerRequest(cfg.serviceName, request)...), + oteltrace.WithSpanKind(oteltrace.SpanKindServer), + } + + if path := c.Path(); path != "" { + rAttr := semconv.HTTPRoute(path) + opts = append(opts, oteltrace.WithAttributes(rAttr)) + } + + spanName := c.Path() + if spanName == "" { + spanName = fmt.Sprintf( + "HTTP %s route not found", + request.Method, + ) + } + + ctx, span := tracer.Start(ctx, spanName, opts...) + defer span.End() + + // add the new context into the request, because new ctx contains our created span and we want all inner spans in next middlewares become child span of this span + // pass the span through the request context + c.SetRequest(request.WithContext(ctx)) + + // serve the request to the next middleware + err := next(c) + if err != nil { + // handle echo error in this middleware and raise echo errorhandler func and our custom error handler + // when we call c.Error more than once, `c.Response().Committed` becomes true and response doesn't write to client again in our error handler + // Error will update response status with occurred error object status code + c.Error(err) + } + + status := c.Response().Status + err = utils.HttpTraceStatusFromSpanWithCode(span, err, status) + + return err + } + } +} diff --git a/internal/pkg/http/customecho/middlewares/problem_detail/config.go b/internal/pkg/http/customecho/middlewares/problem_detail/config.go new file mode 100644 index 00000000..27fcbc3c --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/problem_detail/config.go @@ -0,0 +1,34 @@ +package problemdetail + +import ( + problemDetails "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/problemdetails" + + "github.com/labstack/echo/v4/middleware" +) + +type config struct { + Skipper middleware.Skipper + ProblemParser problemDetails.ErrorParserFunc +} + +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +func WithSkipper(skipper middleware.Skipper) Option { + return optionFunc(func(cfg *config) { + cfg.Skipper = skipper + }) +} + +func WithErrorParser(errorParser problemDetails.ErrorParserFunc) Option { + return optionFunc(func(cfg *config) { + cfg.ProblemParser = errorParser + }) +} diff --git a/internal/pkg/http/customecho/middlewares/problem_detail/problem_detail_middleware.go b/internal/pkg/http/customecho/middlewares/problem_detail/problem_detail_middleware.go new file mode 100644 index 00000000..9db974a1 --- /dev/null +++ b/internal/pkg/http/customecho/middlewares/problem_detail/problem_detail_middleware.go @@ -0,0 +1,44 @@ +package problemdetail + +import ( + problemDetails "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/problemdetails" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +func ProblemDetail(opts ...Option) echo.MiddlewareFunc { + cfg := config{} + for _, opt := range opts { + opt.apply(&cfg) + } + + if cfg.Skipper == nil { + cfg.Skipper = middleware.DefaultSkipper + } + + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if cfg.Skipper(c) { + return next(c) + } + + err := next(c) + + prbError := problemDetails.ParseError(err) + + if cfg.ProblemParser != nil { + prbError = cfg.ProblemParser(prbError) + } + + if prbError != nil { + // handle echo error in this middleware and raise echo errorhandler func and our custom error handler + // when we call c.Error more than once, `c.Response().Committed` becomes true and response doesn't write to client again in our error handler + // Error will update response status with occurred error object status code + c.Error(prbError) + } + + return prbError + } + } +} diff --git a/internal/pkg/http/http_errors/custom_errors/api_error.go b/internal/pkg/http/http_errors/custom_errors/api_error.go deleted file mode 100644 index 21232e7b..00000000 --- a/internal/pkg/http/http_errors/custom_errors/api_error.go +++ /dev/null @@ -1,46 +0,0 @@ -package customErrors - -import ( - "emperror.dev/errors" -) - -func NewApiError(message string, code int) error { - ae := &apiError{ - CustomError: NewCustomError(nil, code, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -func NewApiErrorWrap(err error, code int, message string) error { - ae := &apiError{ - CustomError: NewCustomError(err, code, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -type apiError struct { - CustomError -} - -type ApiError interface { - CustomError - IsApiError() bool -} - -func (a *apiError) IsApiError() bool { - return true -} - -func IsApiError(err error, code int) bool { - var apiError ApiError - // us, ok := grpc_errors.Cause(err).(ApiError) - if errors.As(err, &apiError) { - return apiError.IsApiError() && apiError.Status() == code - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/application_error.go b/internal/pkg/http/http_errors/custom_errors/application_error.go deleted file mode 100644 index 3d083526..00000000 --- a/internal/pkg/http/http_errors/custom_errors/application_error.go +++ /dev/null @@ -1,66 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewApplicationError(message string) error { - ae := &applicationError{ - CustomError: NewCustomError(nil, http.StatusInternalServerError, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -func NewApplicationErrorWithCode(message string, code int) error { - ae := &applicationError{ - CustomError: NewCustomError(nil, code, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -func NewApplicationErrorWrap(err error, message string) error { - ae := &applicationError{ - CustomError: NewCustomError(err, http.StatusInternalServerError, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -func NewApplicationErrorWrapWithCode(err error, code int, message string) error { - ae := &applicationError{ - CustomError: NewCustomError(err, code, message), - } - stackErr := errors.WithStackIf(ae) - - return stackErr -} - -type applicationError struct { - CustomError -} - -type ApplicationError interface { - CustomError - IsApplicationError() bool -} - -func (a *applicationError) IsApplicationError() bool { - return true -} - -func IsApplicationError(err error, code int) bool { - var applicationError ApplicationError - // us, ok := grpc_errors.Cause(err).(ApplicationError) - if errors.As(err, &applicationError) { - return applicationError.IsApplicationError() && applicationError.Status() == code - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/bad_request_error.go b/internal/pkg/http/http_errors/custom_errors/bad_request_error.go deleted file mode 100644 index 20104d44..00000000 --- a/internal/pkg/http/http_errors/custom_errors/bad_request_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewBadRequestError(message string) error { - br := &badRequestError{ - CustomError: NewCustomError(nil, http.StatusBadRequest, message), - } - stackErr := errors.WithStackIf(br) - - return stackErr -} - -func NewBadRequestErrorWrap(err error, message string) error { - br := &badRequestError{ - CustomError: NewCustomError(err, http.StatusBadRequest, message), - } - stackErr := errors.WithStackIf(br) - - return stackErr -} - -type badRequestError struct { - CustomError -} - -type BadRequestError interface { - CustomError - IsBadRequestError() bool -} - -func (b *badRequestError) IsBadRequestError() bool { - return true -} - -func IsBadRequestError(err error) bool { - var badRequestError BadRequestError - // us, ok := grpc_errors.Cause(err).(BadRequestError) - if errors.As(err, &badRequestError) { - return badRequestError.IsBadRequestError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/conflict_error.go b/internal/pkg/http/http_errors/custom_errors/conflict_error.go deleted file mode 100644 index a7194368..00000000 --- a/internal/pkg/http/http_errors/custom_errors/conflict_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewConflictError(message string) error { - ce := &conflictError{ - CustomError: NewCustomError(nil, http.StatusConflict, message), - } - stackErr := errors.WithStackIf(ce) - - return stackErr -} - -func NewConflictErrorWrap(err error, message string) error { - ce := &conflictError{ - CustomError: NewCustomError(err, http.StatusConflict, message), - } - stackErr := errors.WithStackIf(ce) - - return stackErr -} - -type conflictError struct { - CustomError -} - -type ConflictError interface { - CustomError - IsConflictError() bool -} - -func (c *conflictError) IsConflictError() bool { - return true -} - -func IsConflictError(err error) bool { - var conflictError ConflictError - // us, ok := grpc_errors.Cause(err).(ConflictError) - if errors.As(err, &conflictError) { - return conflictError.IsConflictError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/custom_errors_test.go b/internal/pkg/http/http_errors/custom_errors/custom_errors_test.go deleted file mode 100644 index 1298aded..00000000 --- a/internal/pkg/http/http_errors/custom_errors/custom_errors_test.go +++ /dev/null @@ -1,348 +0,0 @@ -package customErrors - -import ( - "fmt" - "testing" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/contracts" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "emperror.dev/errors" - "github.com/stretchr/testify/assert" -) - -func Test_BadRequest_Err(t *testing.T) { - rootErr2 := NewBadRequestErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling bad request errorUtils") - badErr := NewBadRequestErrorWrap(rootErr, "this is a bad request errorUtils") - err := errors.WithMessage(badErr, "outer errorUtils wrapper") - - assert.True(t, IsBadRequestError(err)) - assert.True(t, IsCustomError(err)) - assert.True(t, IsCustomError(rootErr2)) - assert.True(t, IsCustomError(rootErr2)) - - var customError CustomError - var customError2 CustomError - errors.As(err, &customError) - errors.As(err, &customError2) - - assert.NotNil(t, customError2) - - assert.Equal(t, 400, customError.Status()) - assert.Equal(t, "this is a bad request errorUtils", customError.Message()) - assert.Equal( - t, - "this is a bad request errorUtils: handling bad request errorUtils", - customError.Error(), - ) - assert.NotNil(t, customError.Unwrap()) - assert.NotNil(t, customError.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println( - errorUtils.ErrorsWithStack(err), - ) // write errorUtils messages with stacktrace - } else { - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) - } -} - -func Test_NotFound_Err(t *testing.T) { - rootErr := errors.New("handling not found errorUtils") - notFoundErr := NewNotFoundErrorWrap(rootErr, "this is a not found errorUtils") - err := errors.WithMessage(notFoundErr, "outer errorUtils wrapper") - - assert.True(t, IsNotFoundError(err)) - assert.True(t, IsCustomError(err)) - - var notFound NotFoundError - errors.As(err, ¬Found) - - assert.Equal(t, 404, notFound.Status()) - assert.Equal(t, "this is a not found errorUtils", notFound.Message()) - assert.Equal( - t, - "this is a not found errorUtils: handling not found errorUtils", - notFound.Error(), - ) - assert.NotNil(t, notFound.Unwrap()) - assert.NotNil(t, notFound.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println( - errorUtils.ErrorsWithStack(err), - ) // write errorUtils messages with stacktrace - } else { - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) - } -} - -func Test_Domain_Err(t *testing.T) { - rootErr2 := NewDomainErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling domain_events errorUtils") - domainErr := NewDomainErrorWithCodeWrap(rootErr, 400, "this is a domain_events errorUtils") - err := errors.WithMessage(domainErr, "outer errorUtils wrapper") - - assert.True(t, IsDomainError(err, 400)) - assert.True(t, IsDomainError(rootErr2, 400)) - assert.True(t, IsCustomError(err)) - assert.True(t, IsCustomError(rootErr2)) - - var customError CustomError - errors.As(err, &customError) - - assert.Equal(t, 400, customError.Status()) - assert.Equal(t, "this is a domain_events errorUtils", customError.Message()) - assert.Equal( - t, - "this is a domain_events errorUtils: handling domain_events errorUtils", - customError.Error(), - ) - assert.NotNil(t, customError.Unwrap()) - assert.NotNil(t, customError.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println( - errorUtils.ErrorsWithStack(err), - ) // write errorUtils messages with stacktrace - } else { - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) - } -} - -func Test_Application_Err(t *testing.T) { - rootErr2 := NewApplicationErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling application_exceptions errorUtils") - err := NewApplicationErrorWrapWithCode( - rootErr, - 400, - "this is a application_exceptions errorUtils", - ) - err = errors.WithMessage(err, "outer errorUtils wrapper") - - assert.True(t, IsApplicationError(err, 400)) - assert.True(t, IsApplicationError(rootErr2, 500)) - assert.True(t, IsCustomError(err)) - assert.True(t, IsCustomError(rootErr2)) - - var appErr ApplicationError - errors.As(err, &appErr) - - assert.Equal(t, 400, appErr.Status()) - assert.Equal(t, "this is a application_exceptions errorUtils", appErr.Message()) - assert.Equal( - t, - "this is a application_exceptions errorUtils: handling application_exceptions errorUtils", - appErr.Error(), - ) - assert.NotNil(t, appErr.Unwrap()) - assert.NotNil(t, appErr.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println(errorUtils.ErrorsWithStack(err)) - } else { - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) - } -} - -func Test_Internal_Server_Error(t *testing.T) { - rootErr := errors.New("handling internal server errorUtils") - internalServerErr := NewInternalServerErrorWrap(rootErr, "this is a internal server errorUtils") - err := errors.WithMessage(internalServerErr, "this is a internal server errorUtils") - - assert.True(t, IsInternalServerError(err)) - assert.True(t, IsCustomError(err)) - - var internalErr InternalServerError - errors.As(err, &internalErr) - - assert.Equal(t, 500, internalErr.Status()) - assert.Equal(t, "this is a internal server errorUtils", internalErr.Message()) - assert.Equal( - t, - "this is a internal server errorUtils: handling internal server errorUtils", - internalErr.Error(), - ) - assert.NotNil(t, internalErr.Unwrap()) - assert.NotNil(t, internalErr.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println(errorUtils.ErrorsWithStack(err)) - } else { - fmt.Println(errorUtils.ErrorsWithStack(err)) - } -} - -func Test_Marshaling_Error(t *testing.T) { - rootErr2 := NewMarshalingErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling marshaling errorUtils") - marshalErr := NewMarshalingErrorWrap(rootErr, "this is a marshaling errorUtils") - err := errors.WithMessage(marshalErr, "this is a marshaling errorUtils") - - assert.True(t, IsInternalServerError(err)) - assert.True(t, IsInternalServerError(rootErr2)) - assert.True(t, IsCustomError(err)) - assert.True(t, IsCustomError(rootErr2)) - - assert.True(t, IsMarshalingError(err)) - assert.True(t, IsMarshalingError(rootErr2)) - - var customErr CustomError - var customErr2 CustomError - - errors.As(err, &customErr) - errors.As(rootErr2, &customErr2) - - assert.NotNil(t, customErr) - assert.NotNil(t, customErr2) - - assert.Equal(t, 500, customErr.Status()) - assert.Equal(t, "this is a marshaling errorUtils", customErr.Message()) - assert.Equal( - t, - "this is a marshaling errorUtils: handling marshaling errorUtils", - customErr.Error(), - ) - assert.NotNil(t, customErr.Unwrap()) - assert.NotNil(t, customErr.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println(errorUtils.ErrorsWithStack(err)) - } else { - fmt.Println(errorUtils.ErrorsWithStack(err)) - } -} - -func Test_Validation_Error(t *testing.T) { - rootErr2 := NewValidationErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling validation errorUtils") - validationErr := NewValidationErrorWrap(rootErr, "this is a validation errorUtils") - err := errors.WithMessage(validationErr, "this is a validation errorUtils") - - assert.True(t, IsBadRequestError(err)) - assert.True(t, IsBadRequestError(rootErr2)) - assert.True(t, IsCustomError(err)) - assert.True(t, IsCustomError(rootErr2)) - - assert.True(t, IsValidationError(err)) - assert.True(t, IsValidationError(rootErr2)) - - var customErr CustomError - var customErr2 CustomError - - errors.As(err, &customErr) - errors.As(rootErr2, &customErr2) - - assert.NotNil(t, customErr) - assert.NotNil(t, customErr2) - - assert.Equal(t, 400, customErr.Status()) - assert.Equal(t, "this is a validation errorUtils", customErr.Message()) - assert.Equal( - t, - "this is a validation errorUtils: handling validation errorUtils", - customErr.Error(), - ) - assert.NotNil(t, customErr.Unwrap()) - assert.NotNil(t, customErr.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println(errorUtils.ErrorsWithStack(err)) - } else { - fmt.Println(errorUtils.ErrorsWithStack(err)) - } -} - -func Test_Conflict_Error(t *testing.T) { - rootErr2 := NewConflictErrorWrap( - nil, - fmt.Sprintf("domain_events event already exists in event registry"), - ) - - rootErr := errors.New("handling conflict errorUtils") - conflictErr := NewConflictErrorWrap(rootErr, "this is a conflict errorUtils") - err := errors.WithMessage(conflictErr, "this is a conflict errorUtils") - - assert.True(t, IsCustomError(err)) - assert.True(t, IsConflictError(err)) - assert.True(t, IsCustomError(rootErr2)) - assert.True(t, IsConflictError(rootErr2)) - - var customErr CustomError - var customErr2 CustomError - errors.As(err, &customErr) - errors.As(rootErr2, &customErr2) - - assert.NotNil(t, customErr2) - - assert.Equal(t, 409, customErr.Status()) - assert.Equal(t, "this is a conflict errorUtils", customErr.Message()) - assert.Equal( - t, - "this is a conflict errorUtils: handling conflict errorUtils", - customErr.Error(), - ) - assert.NotNil(t, customErr.Unwrap()) - assert.NotNil(t, customErr.Cause()) - - var stackErr contracts.StackTracer - if ok := errors.As(err, &stackErr); ok { - // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package - fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for - fmt.Println(errorUtils.ErrorsWithStack(err)) - } else { - fmt.Println(errorUtils.ErrorsWithStack(err)) - } -} - -func myfoo(e error) error { - // https://itnext.io/golang-error-handling-best-practice-a36f47b0b94c - // Note: Do not repeat Wrap, it will record redundancy call stacks, we usually care about root stack trace - return errors.WithMessage(e, "foo failed") // or grpc_errors.WrapIf() -} - -func mybar(e error) error { - return errors.WithMessage(myfoo(e), "bar failed") // or grpc_errors.WrapIf() -} diff --git a/internal/pkg/http/http_errors/custom_errors/domain_error.go b/internal/pkg/http/http_errors/custom_errors/domain_error.go deleted file mode 100644 index 38720d6b..00000000 --- a/internal/pkg/http/http_errors/custom_errors/domain_error.go +++ /dev/null @@ -1,66 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewDomainError(message string) error { - de := &domainError{ - CustomError: NewCustomError(nil, http.StatusBadRequest, message), - } - stackErr := errors.WithStackIf(de) - - return stackErr -} - -func NewDomainErrorWithCode(message string, code int) error { - de := &domainError{ - CustomError: NewCustomError(nil, code, message), - } - stackErr := errors.WithStackIf(de) - - return stackErr -} - -func NewDomainErrorWrap(err error, message string) error { - de := &domainError{ - CustomError: NewCustomError(err, http.StatusBadRequest, message), - } - stackErr := errors.WithStackIf(de) - - return stackErr -} - -func NewDomainErrorWithCodeWrap(err error, code int, message string) error { - de := &domainError{ - CustomError: NewCustomError(err, code, message), - } - stackErr := errors.WithStackIf(de) - - return stackErr -} - -type domainError struct { - CustomError -} - -type DomainError interface { - CustomError - IsDomainError() bool -} - -func (d *domainError) IsDomainError() bool { - return true -} - -func IsDomainError(err error, code int) bool { - var domainErr DomainError - // us, ok := grpc_errors.Cause(err).(DomainError) - if errors.As(err, &domainErr) { - return domainErr.IsDomainError() && domainErr.Status() == code - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/forbiden_error.go b/internal/pkg/http/http_errors/custom_errors/forbiden_error.go deleted file mode 100644 index 0a35add3..00000000 --- a/internal/pkg/http/http_errors/custom_errors/forbiden_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewForbiddenError(message string) error { - ne := &forbiddenError{ - CustomError: NewCustomError(nil, http.StatusForbidden, message), - } - stackErr := errors.WithStackIf(ne) - - return stackErr -} - -func NewForbiddenErrorWrap(err error, message string) error { - ne := &forbiddenError{ - CustomError: NewCustomError(err, http.StatusForbidden, message), - } - stackErr := errors.WithStackIf(ne) - - return stackErr -} - -type forbiddenError struct { - CustomError -} - -type ForbiddenError interface { - CustomError - IsForbiddenError() bool -} - -func (f *forbiddenError) IsForbiddenError() bool { - return true -} - -func IsForbiddenError(err error) bool { - var forbiddenError ForbiddenError - // us, ok := grpc_errors.Cause(err).(ForbiddenError) - if errors.As(err, &forbiddenError) { - return forbiddenError.IsForbiddenError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/internal_server_error.go b/internal/pkg/http/http_errors/custom_errors/internal_server_error.go deleted file mode 100644 index f6db30a5..00000000 --- a/internal/pkg/http/http_errors/custom_errors/internal_server_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewInternalServerError(message string) error { - br := &internalServerError{ - CustomError: NewCustomError(nil, http.StatusInternalServerError, message), - } - stackErr := errors.WithStackIf(br) - - return stackErr -} - -func NewInternalServerErrorWrap(err error, message string) error { - br := &internalServerError{ - CustomError: NewCustomError(err, http.StatusInternalServerError, message), - } - stackErr := errors.WithStackIf(br) - - return stackErr -} - -type internalServerError struct { - CustomError -} - -type InternalServerError interface { - CustomError - IsInternalServerError() bool -} - -func (i *internalServerError) IsInternalServerError() bool { - return true -} - -func IsInternalServerError(err error) bool { - var internalErr InternalServerError - // us, ok := grpc_errors.Cause(err).(InternalServerError) - if errors.As(err, &internalErr) { - return internalErr.IsInternalServerError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/marshaling_error.go b/internal/pkg/http/http_errors/custom_errors/marshaling_error.go deleted file mode 100644 index 84ce3853..00000000 --- a/internal/pkg/http/http_errors/custom_errors/marshaling_error.go +++ /dev/null @@ -1,51 +0,0 @@ -package customErrors - -import ( - "emperror.dev/errors" -) - -func NewMarshalingError(message string) error { - internal := NewInternalServerError(message) - customErr := GetCustomError(internal) - ue := &marshalingError{ - InternalServerError: customErr.(InternalServerError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -func NewMarshalingErrorWrap(err error, message string) error { - internal := NewInternalServerErrorWrap(err, message) - customErr := GetCustomError(internal) - ue := &marshalingError{ - InternalServerError: customErr.(InternalServerError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -type marshalingError struct { - InternalServerError -} - -type MarshalingError interface { - InternalServerError - IsMarshalingError() bool -} - -func (m *marshalingError) IsMarshalingError() bool { - return true -} - -func IsMarshalingError(err error) bool { - var me MarshalingError - - // us, ok := grpc_errors.Cause(err).(MarshalingError) - if errors.As(err, &me) { - return me.IsMarshalingError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/not_found_error.go b/internal/pkg/http/http_errors/custom_errors/not_found_error.go deleted file mode 100644 index a334c7c7..00000000 --- a/internal/pkg/http/http_errors/custom_errors/not_found_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewNotFoundError(message string) error { - ne := ¬FoundError{ - CustomError: NewCustomError(nil, http.StatusNotFound, message), - } - stackErr := errors.WithStackIf(ne) - - return stackErr -} - -func NewNotFoundErrorWrap(err error, message string) error { - ne := ¬FoundError{ - CustomError: NewCustomError(err, http.StatusNotFound, message), - } - stackErr := errors.WithStackIf(ne) - - return stackErr -} - -type notFoundError struct { - CustomError -} - -type NotFoundError interface { - CustomError - IsNotFoundError() bool -} - -func (n *notFoundError) IsNotFoundError() bool { - return true -} - -func IsNotFoundError(err error) bool { - var notFoundError NotFoundError - // us, ok := grpc_errors.Cause(err).(NotFoundError) - if errors.As(err, ¬FoundError) { - return notFoundError.IsNotFoundError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/unauthorized_error.go b/internal/pkg/http/http_errors/custom_errors/unauthorized_error.go deleted file mode 100644 index 51638b5a..00000000 --- a/internal/pkg/http/http_errors/custom_errors/unauthorized_error.go +++ /dev/null @@ -1,48 +0,0 @@ -package customErrors - -import ( - "net/http" - - "emperror.dev/errors" -) - -func NewUnAuthorizedError(message string) error { - ue := &unauthorizedError{ - CustomError: NewCustomError(nil, http.StatusUnauthorized, message), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -func NewUnAuthorizedErrorWrap(err error, message string) error { - ue := &unauthorizedError{ - CustomError: NewCustomError(err, http.StatusUnauthorized, message), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -type unauthorizedError struct { - CustomError -} - -type UnauthorizedError interface { - CustomError - IsUnAuthorizedError() bool -} - -func (u *unauthorizedError) IsUnAuthorizedError() bool { - return true -} - -func IsUnAuthorizedError(err error) bool { - var unauthorizedError UnauthorizedError - // us, ok := grpc_errors.Cause(err).(UnauthorizedError) - if errors.As(err, &unauthorizedError) { - return unauthorizedError.IsUnAuthorizedError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/unmarshaling_error.go b/internal/pkg/http/http_errors/custom_errors/unmarshaling_error.go deleted file mode 100644 index b65dec8d..00000000 --- a/internal/pkg/http/http_errors/custom_errors/unmarshaling_error.go +++ /dev/null @@ -1,50 +0,0 @@ -package customErrors - -import ( - "emperror.dev/errors" -) - -func NewUnMarshalingError(message string) error { - internal := NewInternalServerError(message) - customErr := GetCustomError(internal) - ue := &unMarshalingError{ - InternalServerError: customErr.(InternalServerError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -func NewUnMarshalingErrorWrap(err error, message string) error { - internal := NewInternalServerErrorWrap(err, message) - customErr := GetCustomError(internal) - ue := &unMarshalingError{ - InternalServerError: customErr.(InternalServerError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -type unMarshalingError struct { - InternalServerError -} - -type UnMarshalingError interface { - InternalServerError - IsUnMarshalingError() bool -} - -func (u *unMarshalingError) IsUnMarshalingError() bool { - return true -} - -func IsUnMarshalingError(err error) bool { - var unMarshalingError UnMarshalingError - // us, ok := grpc_errors.Cause(err).(UnMarshalingError) - if errors.As(err, &unMarshalingError) { - return unMarshalingError.IsUnMarshalingError() - } - - return false -} diff --git a/internal/pkg/http/http_errors/custom_errors/validation_error.go b/internal/pkg/http/http_errors/custom_errors/validation_error.go deleted file mode 100644 index b2d2dda8..00000000 --- a/internal/pkg/http/http_errors/custom_errors/validation_error.go +++ /dev/null @@ -1,50 +0,0 @@ -package customErrors - -import ( - "emperror.dev/errors" -) - -func NewValidationError(message string) error { - bad := NewBadRequestError(message) - customErr := GetCustomError(bad) - ue := &validationError{ - BadRequestError: customErr.(BadRequestError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -func NewValidationErrorWrap(err error, message string) error { - bad := NewBadRequestErrorWrap(err, message) - customErr := GetCustomError(bad) - ue := &validationError{ - BadRequestError: customErr.(BadRequestError), - } - stackErr := errors.WithStackIf(ue) - - return stackErr -} - -type validationError struct { - BadRequestError -} - -type ValidationError interface { - BadRequestError - IsValidationError() bool -} - -func (v *validationError) IsValidationError() bool { - return true -} - -func IsValidationError(err error) bool { - var validationError ValidationError - // us, ok := grpc_errors.Cause(err).(ValidationError) - if errors.As(err, &validationError) { - return validationError.IsValidationError() - } - - return false -} diff --git a/internal/pkg/http/http_fx.go b/internal/pkg/http/http_fx.go index c2f9334a..20ba60cb 100644 --- a/internal/pkg/http/http_fx.go +++ b/internal/pkg/http/http_fx.go @@ -2,7 +2,7 @@ package http import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/client" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "go.uber.org/fx" ) diff --git a/internal/pkg/http/http_errors/contracts/contracts.go b/internal/pkg/http/httperrors/contracts/contracts.go similarity index 100% rename from internal/pkg/http/http_errors/contracts/contracts.go rename to internal/pkg/http/httperrors/contracts/contracts.go diff --git a/internal/pkg/http/httperrors/customerrors/api_error.go b/internal/pkg/http/httperrors/customerrors/api_error.go new file mode 100644 index 00000000..6c6e72dd --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/api_error.go @@ -0,0 +1,64 @@ +package customErrors + +import ( + "emperror.dev/errors" +) + +func NewApiError(message string, code int) ApiError { + // `NewPlain` doesn't add stack-trace at all + apiErrMessage := errors.NewPlain("api error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(apiErrMessage, message) + + apiError := &apiError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return apiError +} + +func NewApiErrorWrap(err error, code int, message string) ApiError { + if err == nil { + return NewApiError(message, code) + } + + // `WithMessage` doesn't add stack-trace at all + apiErrMessage := errors.WithMessage(err, "api error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(apiErrMessage, message) + + apiError := &apiError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return apiError +} + +type apiError struct { + CustomError +} + +type ApiError interface { + CustomError + isAPIError() +} + +func (a *apiError) isAPIError() { +} + +func IsApiError(err error, code int) bool { + var apiError ApiError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested api error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(ApiError); ok { + return true + } + + // us, ok := errors.Cause(err).(ApiError) + if errors.As(err, &apiError) { + return apiError.Status() == code + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/application_error.go b/internal/pkg/http/httperrors/customerrors/application_error.go new file mode 100644 index 00000000..dd716506 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/application_error.go @@ -0,0 +1,78 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewApplicationError(message string) ApplicationError { + return NewApplicationErrorWithCode(message, http.StatusInternalServerError) +} + +func NewApplicationErrorWithCode(message string, code int) ApplicationError { + // `NewPlain` doesn't add stack-trace at all + applicationErrMessage := errors.NewPlain("application error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(applicationErrMessage, message) + + applicationError := &applicationError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return applicationError +} + +func NewApplicationErrorWrap(err error, message string) ApplicationError { + return NewApplicationErrorWrapWithCode(err, http.StatusInternalServerError, message) +} + +func NewApplicationErrorWrapWithCode( + err error, + code int, + message string, +) ApplicationError { + if err == nil { + return NewApplicationErrorWithCode(message, code) + } + + // `WithMessage` doesn't add stack-trace at all + applicationErrMessage := errors.WithMessage(err, "application error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(applicationErrMessage, message) + + applicationError := &applicationError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return applicationError +} + +type applicationError struct { + CustomError +} + +type ApplicationError interface { + CustomError + isApplicationError() +} + +func (a *applicationError) isApplicationError() { +} + +func IsApplicationError(err error, code int) bool { + var applicationError ApplicationError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested application error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(ApplicationError); ok { + return true + } + + // us, ok := errors.Cause(err).(ApplicationError) + if errors.As(err, &applicationError) { + return applicationError.Status() == code + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/bad_request_error.go b/internal/pkg/http/httperrors/customerrors/bad_request_error.go new file mode 100644 index 00000000..84cf6662 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/bad_request_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewBadRequestError(message string) BadRequestError { + // `NewPlain` doesn't add stack-trace at all + badRequestErrMessage := errors.NewPlain("bad request error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(badRequestErrMessage, message) + + badRequestError := &badRequestError{ + CustomError: NewCustomError(stackErr, http.StatusBadRequest, message), + } + + return badRequestError +} + +func NewBadRequestErrorWrap(err error, message string) BadRequestError { + if err == nil { + return NewBadRequestError(message) + } + + // `WithMessage` doesn't add stack-trace at all + badRequestErrMessage := errors.WithMessage(err, "bad request error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(badRequestErrMessage, message) + + badRequestError := &badRequestError{ + CustomError: NewCustomError(stackErr, http.StatusBadRequest, message), + } + + return badRequestError +} + +type badRequestError struct { + CustomError +} + +type BadRequestError interface { + CustomError + isBadRequestError() +} + +func (b *badRequestError) isBadRequestError() { +} + +func IsBadRequestError(err error) bool { + var badRequestError BadRequestError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested bad-request error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(BadRequestError); ok { + return true + } + + // us, ok := errors.Cause(err).(BadRequestError) + if errors.As(err, &badRequestError) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/conflict_error.go b/internal/pkg/http/httperrors/customerrors/conflict_error.go new file mode 100644 index 00000000..887236d0 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/conflict_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewConflictError(message string) ConflictError { + // `NewPlain` doesn't add stack-trace at all + conflictErrMessage := errors.NewPlain("conflict error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(conflictErrMessage, message) + + conflictError := &conflictError{ + CustomError: NewCustomError(stackErr, http.StatusConflict, message), + } + + return conflictError +} + +func NewConflictErrorWrap(err error, message string) ConflictError { + if err == nil { + return NewConflictError(message) + } + + // `WithMessage` doesn't add stack-trace at all + conflictErrMessage := errors.WithMessage(err, "conflict error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(conflictErrMessage, message) + + conflictError := &conflictError{ + CustomError: NewCustomError(stackErr, http.StatusConflict, message), + } + + return conflictError +} + +type conflictError struct { + CustomError +} + +type ConflictError interface { + CustomError + isConflictError() +} + +func (c *conflictError) isConflictError() { +} + +func IsConflictError(err error) bool { + var conflictError ConflictError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested notfound error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(ConflictError); ok { + return true + } + + // us, ok := errors.Cause(err).(ConflictError) + if errors.As(err, &conflictError) { + return true + } + + return false +} diff --git a/internal/pkg/http/http_errors/custom_errors/custom_errors.go b/internal/pkg/http/httperrors/customerrors/custom_errors.go similarity index 72% rename from internal/pkg/http/http_errors/custom_errors/custom_errors.go rename to internal/pkg/http/httperrors/customerrors/custom_errors.go index a7a0d6c7..361bd43d 100644 --- a/internal/pkg/http/http_errors/custom_errors/custom_errors.go +++ b/internal/pkg/http/httperrors/customerrors/custom_errors.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/contracts" "emperror.dev/errors" ) @@ -20,7 +20,7 @@ import ( type customError struct { statusCode int message string - err error + error } type CustomError interface { @@ -28,7 +28,7 @@ type CustomError interface { contracts.Wrapper contracts.Causer contracts.Formatter - IsCustomError() bool + isCustomError() Status() int Message() string } @@ -36,20 +36,19 @@ type CustomError interface { func NewCustomError(err error, code int, message string) CustomError { m := &customError{ statusCode: code, - err: err, + error: err, message: message, } return m } -func (e *customError) IsCustomError() bool { - return true +func (e *customError) isCustomError() { } func (e *customError) Error() string { - if e.err != nil { - return e.message + ": " + e.err.Error() + if e.error != nil { + return e.error.Error() } return e.message @@ -64,19 +63,25 @@ func (e *customError) Status() int { } func (e *customError) Cause() error { - return e.err + return e.error } func (e *customError) Unwrap() error { - return e.err + return e.error } func (e *customError) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { - fmt.Fprintf(s, "%+v\n", e.Cause()) - io.WriteString(s, e.message) + //%s error messages separated by a colon and a space (": ") + //%q double-quoted error messages separated by a colon and a space (": ") + //%v one error message per line + //%+v one error message per line and stack trace (if any) + + // if we have a call-stacked error, +v shows callstack for this error + fmt.Fprintf(s, "%+v", e.Cause()) + // io.WriteString(s, e.message) return } fallthrough @@ -104,8 +109,9 @@ func IsCustomError(err error) bool { return true } + // us, ok := errors.Cause(err).(ConflictError) if errors.As(err, &customErr) { - return customErr.IsCustomError() + return true } return false diff --git a/internal/pkg/http/httperrors/customerrors/custom_errors_test.go b/internal/pkg/http/httperrors/customerrors/custom_errors_test.go new file mode 100644 index 00000000..53f40f77 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/custom_errors_test.go @@ -0,0 +1,503 @@ +package customErrors + +import ( + "fmt" + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/contracts" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" + + "emperror.dev/errors" + "github.com/stretchr/testify/assert" +) + +func Test_Domain_Err(t *testing.T) { + rootErr2 := NewDomainErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling domain_events errorUtils") + domainErr := NewDomainErrorWithCodeWrap(rootErr, 400, "this is a domain_events errorUtils") + err := errors.WithMessage(domainErr, "outer errorUtils wrapper") + + assert.True(t, IsDomainError(err, 400)) + assert.True(t, IsDomainError(rootErr2, 400)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + + var domainError DomainError + errors.As(err, &domainError) + + _, isConflict := domainErr.(ConflictError) + _, isConflict2 := domainError.(ConflictError) + assert.False(t, isConflict) + assert.False(t, isConflict2) + + _, isDomainError := domainErr.(DomainError) + _, isDomainError2 := domainError.(DomainError) + assert.True(t, isDomainError) + assert.True(t, isDomainError2) + + assert.True(t, IsDomainError(domainErr, 400)) + assert.True(t, IsDomainError(domainError, 400)) + assert.False(t, IsDomainError(NewConflictError("conflict error"), 400)) + + assert.Equal(t, 400, domainError.Status()) + assert.Equal(t, "this is a domain_events errorUtils", domainError.Message()) + assert.Equal( + t, + "this is a domain_events errorUtils: domain error: handling domain_events errorUtils", + domainError.Error(), + ) + assert.NotNil(t, domainError.Unwrap()) + assert.NotNil(t, domainError.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println( + errorUtils.ErrorsWithStack(err), + ) // write errorUtils messages with stacktrace + } else { + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) + } +} + +func Test_Application_Err(t *testing.T) { + rootErr2 := NewApplicationErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling application_exceptions errorUtils") + appErr := NewApplicationErrorWrapWithCode( + rootErr, + 400, + "this is a application_exceptions errorUtils", + ) + err := errors.WithMessage(appErr, "outer errorUtils wrapper") + + assert.True(t, IsApplicationError(err, 400)) + assert.True(t, IsApplicationError(rootErr2, 500)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + + var applicationError ApplicationError + errors.As(err, &applicationError) + + _, isConflict := appErr.(ConflictError) + _, isConflict2 := applicationError.(ConflictError) + assert.False(t, isConflict) + assert.False(t, isConflict2) + + _, isApplicationError := appErr.(ApplicationError) + _, isApplicationError2 := applicationError.(ApplicationError) + assert.True(t, isApplicationError) + assert.True(t, isApplicationError2) + + assert.True(t, IsApplicationError(appErr, 400)) + assert.True(t, IsApplicationError(applicationError, 400)) + assert.False(t, IsApplicationError(NewConflictError("conflict error"), 400)) + + assert.Equal(t, 400, applicationError.Status()) + assert.Equal(t, "this is a application_exceptions errorUtils", applicationError.Message()) + assert.Equal( + t, + "this is a application_exceptions errorUtils: application error: handling application_exceptions errorUtils", + applicationError.Error(), + ) + assert.NotNil(t, applicationError.Unwrap()) + assert.NotNil(t, applicationError.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) + } +} + +func Test_Api_Err(t *testing.T) { + rootErr2 := NewApiErrorWrap( + nil, + http.StatusBadRequest, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling api_exceptions errorUtils") + appErr := NewApiErrorWrap( + rootErr, + 400, + "this is a api_exceptions errorUtils", + ) + err := errors.WithMessage(appErr, "outer errorUtils wrapper") + + assert.True(t, IsApiError(err, 400)) + assert.True(t, IsApiError(rootErr2, 500)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + + var apiError ApiError + errors.As(err, &apiError) + + _, isConflict := appErr.(ConflictError) + _, isConflict2 := apiError.(ConflictError) + assert.False(t, isConflict) + assert.False(t, isConflict2) + + _, isApiError := appErr.(ApiError) + _, isApiError2 := apiError.(ApiError) + assert.True(t, isApiError) + assert.True(t, isApiError2) + + assert.True(t, IsApiError(appErr, 400)) + assert.True(t, IsApiError(apiError, 400)) + assert.False(t, IsApiError(NewConflictError("conflict error"), 400)) + + assert.Equal(t, 400, apiError.Status()) + assert.Equal(t, "this is a api_exceptions errorUtils", apiError.Message()) + assert.Equal( + t, + "this is a api_exceptions errorUtils: api error: handling api_exceptions errorUtils", + apiError.Error(), + ) + assert.NotNil(t, apiError.Unwrap()) + assert.NotNil(t, apiError.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) + } +} + +func Test_BadRequest_Err(t *testing.T) { + rootErr2 := NewBadRequestErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling bad request errorUtils") + badErr := NewBadRequestErrorWrap(rootErr, "this is a bad request errorUtils") + err := errors.WithMessage(badErr, "outer errorUtils wrapper") + + assert.True(t, IsBadRequestError(err)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + assert.True(t, IsCustomError(rootErr2)) + + var customError CustomError + var customError2 CustomError + errors.As(err, &customError) + errors.As(err, &customError2) + + assert.NotNil(t, customError2) + + var badRequestError BadRequestError + errors.As(err, &badRequestError) + + _, isConflict := badErr.(ConflictError) + _, isConflict2 := badRequestError.(ConflictError) + assert.False(t, isConflict) + assert.False(t, isConflict2) + + _, isBadRequest := badErr.(BadRequestError) + _, isBadRequest2 := badRequestError.(BadRequestError) + assert.True(t, isBadRequest) + assert.True(t, isBadRequest2) + + assert.True(t, IsBadRequestError(badErr)) + assert.True(t, IsBadRequestError(badRequestError)) + assert.False(t, IsBadRequestError(NewConflictError("conflict error"))) + + assert.Equal(t, 400, badRequestError.Status()) + assert.Equal(t, "this is a bad request errorUtils", badRequestError.Message()) + assert.Equal( + t, + "this is a bad request errorUtils: bad request error: handling bad request errorUtils", + badRequestError.Error(), + ) + assert.NotNil(t, badRequestError.Unwrap()) + assert.NotNil(t, badRequestError.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println( + errorUtils.ErrorsWithStack(err), + ) // write errorUtils messages with stacktrace + } else { + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) + } +} + +func Test_NotFound_Err(t *testing.T) { + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling not found errorUtils") + notFoundErr := NewNotFoundErrorWrap(rootErr, "this is a not found errorUtils") + err := errors.WithMessage(notFoundErr, "outer errorUtils wrapper") + + assert.True(t, IsNotFoundError(err)) + assert.True(t, IsCustomError(err)) + + var notFound NotFoundError + errors.As(err, ¬Found) + + _, isConflict := notFoundErr.(ConflictError) + _, isConflict2 := notFound.(ConflictError) + assert.False(t, isConflict) + assert.False(t, isConflict2) + + _, isNotFound := notFoundErr.(NotFoundError) + _, isNotFound2 := notFound.(NotFoundError) + assert.True(t, isNotFound) + assert.True(t, isNotFound2) + + assert.True(t, IsNotFoundError(notFoundErr)) + assert.True(t, IsNotFoundError(notFound)) + assert.False(t, IsNotFoundError(NewConflictError("conflict error"))) + + assert.Equal(t, http.StatusNotFound, notFound.Status()) + assert.Equal(t, "this is a not found errorUtils", notFound.Message()) + assert.Equal( + t, + "this is a not found errorUtils: not found error: handling not found errorUtils", + notFound.Error(), + ) + assert.NotNil(t, notFound.Unwrap()) + assert.NotNil(t, notFound.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println( + errorUtils.ErrorsWithStack(err), + ) // write errorUtils messages with stacktrace + } else { + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) + } +} + +func Test_Internal_Server_Error(t *testing.T) { + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling internal server errorUtils") + internalServerErr := NewInternalServerErrorWrap(rootErr, "this is a internal server errorUtils") + err := errors.WithMessage(internalServerErr, "outer errorUtils wrapper") + + assert.True(t, IsInternalServerError(err)) + assert.True(t, IsCustomError(err)) + + var internalErr InternalServerError + errors.As(err, &internalErr) + + assert.True(t, IsInternalServerError(internalErr)) + assert.False(t, IsInternalServerError(NewConflictError("conflict error"))) + + assert.Equal(t, http.StatusInternalServerError, internalErr.Status()) + assert.Equal(t, "this is a internal server errorUtils", internalErr.Message()) + assert.Equal( + t, + "this is a internal server errorUtils: internal server error: handling internal server errorUtils", + internalErr.Error(), + ) + assert.NotNil(t, internalErr.Unwrap()) + assert.NotNil(t, internalErr.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithStack(err)) + } +} + +func Test_Forbidden_Error(t *testing.T) { + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling forbidden errorUtils") + forbiddenError := NewForbiddenErrorWrap(rootErr, "this is a forbidden errorUtils") + err := errors.WithMessage(forbiddenError, "outer errorUtils wrapper") + + assert.True(t, IsForbiddenError(err)) + assert.True(t, IsCustomError(err)) + + var forbiddenErr ForbiddenError + errors.As(err, &forbiddenErr) + + assert.True(t, IsForbiddenError(forbiddenErr)) + assert.False(t, IsForbiddenError(NewConflictError("conflict error"))) + + assert.Equal(t, http.StatusForbidden, forbiddenErr.Status()) + assert.Equal(t, "this is a forbidden errorUtils", forbiddenErr.Message()) + assert.Equal( + t, + "this is a forbidden errorUtils: forbidden error: handling forbidden errorUtils", + forbiddenErr.Error(), + ) + assert.NotNil(t, forbiddenErr.Unwrap()) + assert.NotNil(t, forbiddenErr.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithStack(err)) + } +} + +func Test_Marshaling_Error(t *testing.T) { + rootErr2 := NewMarshalingErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + rootErr := errors.NewPlain("handling marshaling errorUtils") + marshalErr := NewMarshalingErrorWrap(rootErr, "this is a marshaling errorUtils") + err := errors.WithMessage(marshalErr, "outer errorUtils wrapper") + + assert.True(t, IsInternalServerError(err)) + assert.True(t, IsInternalServerError(rootErr2)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + + assert.True(t, IsMarshalingError(err)) + assert.True(t, IsMarshalingError(rootErr2)) + + var marshallingErr MarshalingError + errors.As(err, &marshallingErr) + + assert.True(t, IsMarshalingError(marshallingErr)) + assert.False(t, IsMarshalingError(NewConflictError("conflict error"))) + + assert.Equal(t, 500, marshallingErr.Status()) + assert.Equal(t, "this is a marshaling errorUtils", marshallingErr.Message()) + assert.Equal( + t, + "this is a marshaling errorUtils: marshaling error: handling marshaling errorUtils", + marshallingErr.Error(), + ) + assert.NotNil(t, marshallingErr.Unwrap()) + assert.NotNil(t, marshallingErr.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithStack(err)) + } +} + +func Test_Validation_Error(t *testing.T) { + rootErr2 := NewValidationErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + rootErr := errors.New("handling validation errorUtils") + validationErr := NewValidationErrorWrap(rootErr, "this is a validation errorUtils") + err := errors.WithMessage(validationErr, "this is a top error message") + + assert.True(t, IsBadRequestError(err)) + assert.True(t, IsBadRequestError(rootErr2)) + assert.True(t, IsCustomError(err)) + assert.True(t, IsCustomError(rootErr2)) + + assert.True(t, IsValidationError(err)) + assert.True(t, IsValidationError(rootErr2)) + + var customErr CustomError + var customErr2 CustomError + + errors.As(err, &customErr) + errors.As(rootErr2, &customErr2) + + assert.NotNil(t, customErr) + assert.NotNil(t, customErr2) + + assert.Equal(t, http.StatusBadRequest, customErr.Status()) + assert.Equal(t, "this is a validation errorUtils", customErr.Message()) + assert.Equal( + t, + "this is a validation errorUtils: validation error: handling validation errorUtils", + customErr.Error(), + ) + assert.NotNil(t, customErr.Unwrap()) + assert.NotNil(t, customErr.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithStack(err)) + } +} + +func Test_Conflict_Error(t *testing.T) { + rootErr2 := NewConflictErrorWrap( + nil, + fmt.Sprintf("domain_events event already exists in event registry"), + ) + + // `NewPlain` doesn't add stack-trace but `New` will add stack-trace + rootErr := errors.NewPlain("handling conflict errorUtils") + conflictErr := NewConflictErrorWrap(rootErr, "this is a conflict errorUtils") + err := errors.WithMessage(conflictErr, "this is a top error message") + + assert.True(t, IsCustomError(err)) + assert.True(t, IsConflictError(err)) + assert.True(t, IsCustomError(rootErr2)) + assert.True(t, IsConflictError(rootErr2)) + + var conflictError ConflictError + errors.As(err, &conflictError) + + assert.Equal(t, 409, conflictError.Status()) + assert.Equal(t, "this is a conflict errorUtils", conflictError.Message()) + assert.Equal( + t, + "this is a conflict errorUtils: conflict error: handling conflict errorUtils", + conflictError.Error(), + ) + assert.NotNil(t, conflictError.Unwrap()) + assert.NotNil(t, conflictError.Cause()) + + var stackErr contracts.StackTracer + if ok := errors.As(err, &stackErr); ok { + // https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package + fmt.Println(errorUtils.ErrorsWithoutStack(err, false)) // Just write errorUtils messages for + fmt.Println(errorUtils.ErrorsWithStack(err)) + } else { + fmt.Println(errorUtils.ErrorsWithStack(err)) + } +} + +func myfoo(e error) error { + // https://itnext.io/golang-error-handling-best-practice-a36f47b0b94c + // Note: Do not repeat Wrap, it will record redundancy call stacks, we usually care about root stack trace + return errors.WithMessage(e, "foo failed") // or grpc_errors.WrapIf() +} + +func mybar(e error) error { + return errors.WithMessage(myfoo(e), "bar failed") // or grpc_errors.WrapIf() +} diff --git a/internal/pkg/http/httperrors/customerrors/domain_error.go b/internal/pkg/http/httperrors/customerrors/domain_error.go new file mode 100644 index 00000000..f10a779e --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/domain_error.go @@ -0,0 +1,74 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewDomainError(message string) DomainError { + return NewDomainErrorWithCode(message, http.StatusBadRequest) +} + +func NewDomainErrorWithCode(message string, code int) DomainError { + // `NewPlain` doesn't add stack-trace at all + domainErrMessage := errors.NewPlain("domain error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(domainErrMessage, message) + + domainError := &domainError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return domainError +} + +func NewDomainErrorWrap(err error, message string) DomainError { + return NewDomainErrorWithCodeWrap(err, http.StatusBadRequest, message) +} + +func NewDomainErrorWithCodeWrap(err error, code int, message string) DomainError { + if err == nil { + return NewDomainErrorWithCode(message, code) + } + + // `WithMessage` doesn't add stack-trace at all + domainErrMessage := errors.WithMessage(err, "domain error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(domainErrMessage, message) + + domainError := &domainError{ + CustomError: NewCustomError(stackErr, code, message), + } + + return domainError +} + +type domainError struct { + CustomError +} + +type DomainError interface { + CustomError + isDomainError() +} + +func (d *domainError) isDomainError() { +} + +func IsDomainError(err error, code int) bool { + var domainErr DomainError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested notfound error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(DomainError); ok { + return true + } + + // us, ok := errors.Cause(err).(DomainError) + if errors.As(err, &domainErr) { + return domainErr.Status() == code + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/forbiden_error.go b/internal/pkg/http/httperrors/customerrors/forbiden_error.go new file mode 100644 index 00000000..0f91b1e4 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/forbiden_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewForbiddenError(message string) ForbiddenError { + // `NewPlain` doesn't add stack-trace at all + forbiddenErrMessage := errors.NewPlain("forbidden error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(forbiddenErrMessage, message) + + forbiddenError := &forbiddenError{ + CustomError: NewCustomError(stackErr, http.StatusForbidden, message), + } + + return forbiddenError +} + +func NewForbiddenErrorWrap(err error, message string) ForbiddenError { + if err == nil { + return NewForbiddenError(message) + } + + // `WithMessage` doesn't add stack-trace at all + forbiddenErrMessage := errors.WithMessage(err, "forbidden error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(forbiddenErrMessage, message) + + forbiddenError := &forbiddenError{ + CustomError: NewCustomError(stackErr, http.StatusForbidden, message), + } + + return forbiddenError +} + +type forbiddenError struct { + CustomError +} + +type ForbiddenError interface { + CustomError + isForbiddenError() +} + +func (f *forbiddenError) isForbiddenError() { +} + +func IsForbiddenError(err error) bool { + var forbiddenError ForbiddenError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested forbidden error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(ForbiddenError); ok { + return true + } + + // us, ok := errors.Cause(err).(ForbiddenError) + if errors.As(err, &forbiddenError) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/internal_server_error.go b/internal/pkg/http/httperrors/customerrors/internal_server_error.go new file mode 100644 index 00000000..b2818574 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/internal_server_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewInternalServerError(message string) InternalServerError { + // `NewPlain` doesn't add stack-trace at all + internalErrMessage := errors.NewPlain("internal server error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(internalErrMessage, message) + + internalServerError := &internalServerError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return internalServerError +} + +func NewInternalServerErrorWrap(err error, message string) InternalServerError { + if err == nil { + return NewInternalServerError(message) + } + + // `WithMessage` doesn't add stack-trace at all + internalErrMessage := errors.WithMessage(err, "internal server error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(internalErrMessage, message) + + internalServerError := &internalServerError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return internalServerError +} + +type internalServerError struct { + CustomError +} + +type InternalServerError interface { + CustomError + isInternalServerError() +} + +func (i *internalServerError) isInternalServerError() { +} + +func IsInternalServerError(err error) bool { + var internalServerErr InternalServerError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested internal server error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(InternalServerError); ok { + return true + } + + // us, ok := errors.Cause(err).(InternalServerError) + if errors.As(err, &internalServerErr) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/marshaling_error.go b/internal/pkg/http/httperrors/customerrors/marshaling_error.go new file mode 100644 index 00000000..04c09637 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/marshaling_error.go @@ -0,0 +1,69 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewMarshalingError(message string) MarshalingError { + // `NewPlain` doesn't add stack-trace at all + marshalingErrMessage := errors.NewPlain("marshaling error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(marshalingErrMessage, message) + + marshalingError := &marshalingError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return marshalingError +} + +func NewMarshalingErrorWrap(err error, message string) MarshalingError { + if err == nil { + return NewMarshalingError(message) + } + + // `WithMessage` doesn't add stack-trace at all + marshalingErrMessage := errors.WithMessage(err, "marshaling error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(marshalingErrMessage, message) + + marshalingError := &marshalingError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return marshalingError +} + +type marshalingError struct { + CustomError +} + +type MarshalingError interface { + InternalServerError + isMarshalingError() +} + +func (m *marshalingError) isMarshalingError() { +} + +func (m *marshalingError) isInternalServerError() { +} + +func IsMarshalingError(err error) bool { + var marshalingErr MarshalingError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested marshaling error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(MarshalingError); ok { + return true + } + + // us, ok := errors.Cause(err).(MarshalingError) + if errors.As(err, &marshalingErr) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/not_found_error.go b/internal/pkg/http/httperrors/customerrors/not_found_error.go new file mode 100644 index 00000000..ed70336d --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/not_found_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewNotFoundError(message string) NotFoundError { + // `NewPlain` doesn't add stack-trace at all + notFoundErrMessage := errors.NewPlain("not found error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(notFoundErrMessage, message) + + notFoundError := ¬FoundError{ + CustomError: NewCustomError(stackErr, http.StatusBadRequest, message), + } + + return notFoundError +} + +func NewNotFoundErrorWrap(err error, message string) NotFoundError { + if err == nil { + return NewNotFoundError(message) + } + + // `WithMessage` doesn't add stack-trace at all + notFoundErrMessage := errors.WithMessage(err, "not found error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(notFoundErrMessage, message) + + notFoundError := ¬FoundError{ + CustomError: NewCustomError(stackErr, http.StatusNotFound, message), + } + + return notFoundError +} + +type notFoundError struct { + CustomError +} + +type NotFoundError interface { + CustomError + isNotFoundError() +} + +func (n *notFoundError) isNotFoundError() { +} + +func IsNotFoundError(err error) bool { + var notFoundError NotFoundError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested notfound error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(NotFoundError); ok { + return true + } + + // us, ok := errors.Cause(err).(NotFoundError) + if errors.As(err, ¬FoundError) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/unauthorized_error.go b/internal/pkg/http/httperrors/customerrors/unauthorized_error.go new file mode 100644 index 00000000..2a6cc25e --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/unauthorized_error.go @@ -0,0 +1,66 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewUnAuthorizedError(message string) UnauthorizedError { + // `NewPlain` doesn't add stack-trace at all + unAuthorizedErrMessage := errors.NewPlain("unauthorized error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(unAuthorizedErrMessage, message) + + unAuthorizedError := &unauthorizedError{ + CustomError: NewCustomError(stackErr, http.StatusUnauthorized, message), + } + + return unAuthorizedError +} + +func NewUnAuthorizedErrorWrap(err error, message string) UnauthorizedError { + if err == nil { + return NewUnAuthorizedError(message) + } + + // `WithMessage` doesn't add stack-trace at all + unAuthorizedErrMessage := errors.WithMessage(err, "unauthorized error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(unAuthorizedErrMessage, message) + + unAuthorizedError := &unauthorizedError{ + CustomError: NewCustomError(stackErr, http.StatusUnauthorized, message), + } + + return unAuthorizedError +} + +type unauthorizedError struct { + CustomError +} + +type UnauthorizedError interface { + CustomError + isUnAuthorizedError() +} + +func (u *unauthorizedError) isUnAuthorizedError() { +} + +func IsUnAuthorizedError(err error) bool { + var unauthorizedError UnauthorizedError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested unauthorized error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(UnauthorizedError); ok { + return true + } + + // us, ok := errors.Cause(err).(UnauthorizedError) + if errors.As(err, &unauthorizedError) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/unmarshaling_error.go b/internal/pkg/http/httperrors/customerrors/unmarshaling_error.go new file mode 100644 index 00000000..e6bbea35 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/unmarshaling_error.go @@ -0,0 +1,69 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewUnMarshalingError(message string) UnMarshalingError { + // `NewPlain` doesn't add stack-trace at all + unMarshalingErrMessage := errors.NewPlain("unMarshaling error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(unMarshalingErrMessage, message) + + unMarshalingError := &unMarshalingError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return unMarshalingError +} + +func NewUnMarshalingErrorWrap(err error, message string) UnMarshalingError { + if err == nil { + return NewUnMarshalingError(message) + } + + // `WithMessage` doesn't add stack-trace at all + unMarshalingErrMessage := errors.WithMessage(err, "unMarshaling error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(unMarshalingErrMessage, message) + + unMarshalingError := &unMarshalingError{ + CustomError: NewCustomError(stackErr, http.StatusInternalServerError, message), + } + + return unMarshalingError +} + +type unMarshalingError struct { + CustomError +} + +type UnMarshalingError interface { + InternalServerError + isUnMarshalingError() +} + +func (u *unMarshalingError) isUnMarshalingError() { +} + +func (u *unMarshalingError) isInternalServerError() { +} + +func IsUnMarshalingError(err error) bool { + var unMarshalingError UnMarshalingError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested unMarshaling error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(UnMarshalingError); ok { + return true + } + + // us, ok := errors.Cause(err).(UnMarshalingError) + if errors.As(err, &unMarshalingError) { + return true + } + + return false +} diff --git a/internal/pkg/http/httperrors/customerrors/validation_error.go b/internal/pkg/http/httperrors/customerrors/validation_error.go new file mode 100644 index 00000000..74164940 --- /dev/null +++ b/internal/pkg/http/httperrors/customerrors/validation_error.go @@ -0,0 +1,69 @@ +package customErrors + +import ( + "net/http" + + "emperror.dev/errors" +) + +func NewValidationError(message string) ValidationError { + // `NewPlain` doesn't add stack-trace at all + validationErrMessage := errors.NewPlain("validation error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(validationErrMessage, message) + + validationError := &validationError{ + CustomError: NewCustomError(stackErr, http.StatusBadRequest, message), + } + + return validationError +} + +func NewValidationErrorWrap(err error, message string) ValidationError { + if err == nil { + return NewValidationError(message) + } + + // `WithMessage` doesn't add stack-trace at all + validationErrMessage := errors.WithMessage(err, "validation error") + // `WrapIf` add stack-trace if not added before + stackErr := errors.WrapIf(validationErrMessage, message) + + validationError := &validationError{ + CustomError: NewCustomError(stackErr, http.StatusBadRequest, message), + } + + return validationError +} + +type validationError struct { + CustomError +} + +type ValidationError interface { + BadRequestError + isValidationError() +} + +func (v *validationError) isValidationError() { +} + +func (v *validationError) isBadRequestError() { +} + +func IsValidationError(err error) bool { + var validationError ValidationError + + // https://github.com/golang/go/blob/master/src/net/error_windows.go#L10C2-L12C3 + // this doesn't work for a nested validation error, and we should use errors.As for traversing errors in all levels + if _, ok := err.(ValidationError); ok { + return true + } + + // us, ok := errors.Cause(err).(ValidationError) + if errors.As(err, &validationError) { + return true + } + + return false +} diff --git a/internal/pkg/http/http_errors/problemDetails/custom_problem_details_errors.go b/internal/pkg/http/httperrors/problemdetails/custom_problem_details_errors.go similarity index 100% rename from internal/pkg/http/http_errors/problemDetails/custom_problem_details_errors.go rename to internal/pkg/http/httperrors/problemdetails/custom_problem_details_errors.go diff --git a/internal/pkg/http/http_errors/problemDetails/option_builder.go b/internal/pkg/http/httperrors/problemdetails/option_builder.go similarity index 100% rename from internal/pkg/http/http_errors/problemDetails/option_builder.go rename to internal/pkg/http/httperrors/problemdetails/option_builder.go diff --git a/internal/pkg/http/http_errors/problemDetails/problem_detail_parser.go b/internal/pkg/http/httperrors/problemdetails/problem_detail_parser.go similarity index 75% rename from internal/pkg/http/http_errors/problemDetails/problem_detail_parser.go rename to internal/pkg/http/httperrors/problemdetails/problem_detail_parser.go index 85bfd2ae..f4b86b61 100644 --- a/internal/pkg/http/http_errors/problemDetails/problem_detail_parser.go +++ b/internal/pkg/http/httperrors/problemdetails/problem_detail_parser.go @@ -7,9 +7,9 @@ import ( "reflect" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" "emperror.dev/errors" "github.com/go-playground/validator" @@ -19,7 +19,11 @@ type ProblemDetailParser struct { internalErrors map[reflect.Type]func(err error) ProblemDetailErr } -func NewProblemDetailParser(builder func(builder *OptionBuilder)) *ProblemDetailParser { +type ErrorParserFunc func(err error) ProblemDetailErr + +func NewProblemDetailParser( + builder func(builder *OptionBuilder), +) *ProblemDetailParser { optionBuilder := NewOptionBuilder() builder(optionBuilder) items := optionBuilder.Build() @@ -40,14 +44,26 @@ func ParseError(err error) ProblemDetailErr { customErr := customErrors.GetCustomError(err) var validatorErr validator.ValidationErrors - if err != nil { + if err != nil && customErr != nil { switch { case customErrors.IsDomainError(err, customErr.Status()): - return NewDomainProblemDetail(customErr.Status(), customErr.Error(), stackTrace) + return NewDomainProblemDetail( + customErr.Status(), + customErr.Error(), + stackTrace, + ) case customErrors.IsApplicationError(err, customErr.Status()): - return NewApplicationProblemDetail(customErr.Status(), customErr.Error(), stackTrace) + return NewApplicationProblemDetail( + customErr.Status(), + customErr.Error(), + stackTrace, + ) case customErrors.IsApiError(err, customErr.Status()): - return NewApiProblemDetail(customErr.Status(), customErr.Error(), stackTrace) + return NewApiProblemDetail( + customErr.Status(), + customErr.Error(), + stackTrace, + ) case customErrors.IsBadRequestError(err): return NewBadRequestProblemDetail(customErr.Error(), stackTrace) case customErrors.IsNotFoundError(err): @@ -55,7 +71,10 @@ func ParseError(err error) ProblemDetailErr { case customErrors.IsValidationError(err): return NewValidationProblemDetail(customErr.Error(), stackTrace) case customErrors.IsUnAuthorizedError(err): - return NewUnAuthorizedErrorProblemDetail(customErr.Error(), stackTrace) + return NewUnAuthorizedErrorProblemDetail( + customErr.Error(), + stackTrace, + ) case customErrors.IsForbiddenError(err): return NewForbiddenProblemDetail(customErr.Error(), stackTrace) case customErrors.IsConflictError(err): @@ -63,11 +82,21 @@ func ParseError(err error) ProblemDetailErr { case customErrors.IsInternalServerError(err): return NewInternalServerProblemDetail(customErr.Error(), stackTrace) case customErrors.IsCustomError(err): - return NewProblemDetailFromCodeAndDetail(customErr.Status(), customErr.Error(), stackTrace) + return NewProblemDetailFromCodeAndDetail( + customErr.Status(), + customErr.Error(), + stackTrace, + ) case customErrors.IsUnMarshalingError(err): return NewInternalServerProblemDetail(err.Error(), stackTrace) case customErrors.IsMarshalingError(err): return NewInternalServerProblemDetail(err.Error(), stackTrace) + + default: + return NewInternalServerProblemDetail(err.Error(), stackTrace) + } + } else if err != nil && customErr == nil { + switch { case errors.Is(err, sql.ErrNoRows): return NewNotFoundErrorProblemDetail(err.Error(), stackTrace) case errors.Is(err, context.DeadlineExceeded): diff --git a/internal/pkg/http/http_errors/problemDetails/problem_details.go b/internal/pkg/http/httperrors/problemdetails/problem_details.go similarity index 94% rename from internal/pkg/http/http_errors/problemDetails/problem_details.go rename to internal/pkg/http/httperrors/problemdetails/problem_details.go index f7a8d61f..4a6f76c0 100644 --- a/internal/pkg/http/http_errors/problemDetails/problem_details.go +++ b/internal/pkg/http/httperrors/problemdetails/problem_details.go @@ -7,9 +7,9 @@ import ( "reflect" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/contracts" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/contracts" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" ) @@ -114,7 +114,12 @@ func (p *problemDetail) SetStackTrace(stackTrace string) ProblemDetailErr { } // NewProblemDetail New ProblemDetail Error -func NewProblemDetail(status int, title string, detail string, stackTrace string) ProblemDetailErr { +func NewProblemDetail( + status int, + title string, + detail string, + stackTrace string, +) ProblemDetailErr { problemDetail := &problemDetail{ Status: status, Title: title, @@ -155,7 +160,7 @@ func NewProblemDetailFromCodeAndDetail( } func Map[E error](problem ProblemDetailFunc[E]) { - errorType := typeMapper.GetTypeFromGeneric[E]() + errorType := typeMapper.GetGenericTypeByT[E]() if errorType.Kind() == reflect.Interface { types := typeMapper.TypesImplementedInterface[E]() for _, t := range types { @@ -191,7 +196,7 @@ func ResolveProblemDetail(err error) ProblemDetailErr { // WriteTo writes the JSON Problem to an HTTP Response Writer func WriteTo(p ProblemDetailErr, w http.ResponseWriter) (int, error) { - defaultLogger.Logger.Error(p.Error()) + defaultLogger.GetLogger().Error(p.Error()) stackTrace := p.GetStackTrace() fmt.Println(stackTrace) diff --git a/internal/pkg/http/http_errors/problemDetails/problem_details_test.go b/internal/pkg/http/httperrors/problemdetails/problem_details_test.go similarity index 98% rename from internal/pkg/http/http_errors/problemDetails/problem_details_test.go rename to internal/pkg/http/httperrors/problemdetails/problem_details_test.go index 0f618e7a..25a05d11 100644 --- a/internal/pkg/http/http_errors/problemDetails/problem_details_test.go +++ b/internal/pkg/http/httperrors/problemdetails/problem_details_test.go @@ -4,7 +4,7 @@ import ( "net/http" "testing" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" "github.com/stretchr/testify/assert" diff --git a/internal/pkg/logger/config/log_options.go b/internal/pkg/logger/config/log_options.go index 6163960b..827f0591 100644 --- a/internal/pkg/logger/config/log_options.go +++ b/internal/pkg/logger/config/log_options.go @@ -2,21 +2,22 @@ package config import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/models" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[LogOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[LogOptions]()) type LogOptions struct { LogLevel string `mapstructure:"level"` LogType models.LogType `mapstructure:"logType"` CallerEnabled bool `mapstructure:"callerEnabled"` + EnableTracing bool `mapstructure:"enableTracing" default:"true"` } -func ProvideLogConfig(env environemnt.Environment) (*LogOptions, error) { +func ProvideLogConfig(env environment.Environment) (*LogOptions, error) { return config.BindConfigKey[*LogOptions](optionName, env) } diff --git a/internal/pkg/logger/default_logger/default_logger.go b/internal/pkg/logger/defaultlogger/default_logger.go similarity index 82% rename from internal/pkg/logger/default_logger/default_logger.go rename to internal/pkg/logger/defaultlogger/default_logger.go index 0623b17c..e7634d0e 100644 --- a/internal/pkg/logger/default_logger/default_logger.go +++ b/internal/pkg/logger/defaultlogger/default_logger.go @@ -11,20 +11,20 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" ) -var Logger logger.Logger +var l logger.Logger -func SetupDefaultLogger() { +func initLogger() { logType := os.Getenv("LogConfig_LogType") switch logType { case "Zap", "": - Logger = zap.NewZapLogger( + l = zap.NewZapLogger( &config.LogOptions{LogType: models.Zap, CallerEnabled: false}, constants.Dev, ) break case "Logrus": - Logger = logrous.NewLogrusLogger( + l = logrous.NewLogrusLogger( &config.LogOptions{LogType: models.Logrus, CallerEnabled: false}, constants.Dev, ) @@ -32,3 +32,11 @@ func SetupDefaultLogger() { default: } } + +func GetLogger() logger.Logger { + if l == nil { + initLogger() + } + + return l +} diff --git a/internal/pkg/logger/external/fxlog/fx.go b/internal/pkg/logger/external/fxlog/fx.go index 934049ff..bab76ffc 100644 --- a/internal/pkg/logger/external/fxlog/fx.go +++ b/internal/pkg/logger/external/fxlog/fx.go @@ -139,7 +139,7 @@ func (l *FxCustomLogger) LogEvent(event fxevent.Event) { logger.Fields{"error": e.Err}, ) } else { - l.Debugw("initialized custom fxevent.Logger", logger.Fields{"function": e.ConstructorName}) + l.Debugw("initialized custom fxevent.logger", logger.Fields{"function": e.ConstructorName}) } } } diff --git a/internal/pkg/logger/external/gromlog/gorm.go b/internal/pkg/logger/external/gromlog/gorm.go index 65a2577f..4e488e81 100644 --- a/internal/pkg/logger/external/gromlog/gorm.go +++ b/internal/pkg/logger/external/gromlog/gorm.go @@ -20,12 +20,12 @@ type GormCustomLogger struct { func NewGormCustomLogger(logger logger.Logger) *GormCustomLogger { //cfg, err := config.ProvideLogConfig() // - //var logger logger.Logger - //if cfg.LogType == models.Logrus && err != nil { + //var logger logger.logger + //if cfg.LogType == datamodels.Logrus && err != nil { // logger = logrous.NewLogrusLogger(cfg, constants.Dev) //} else { // if err != nil { - // cfg = &config.LogOptions{LogLevel: "info", LogType: models.Zap} + // cfg = &config.LogOptions{LogLevel: "info", LogType: datamodels.Zap} // } // logger = zap.NewZapLogger(cfg, constants.Dev) //} diff --git a/internal/pkg/logger/logrous/logrous_logger.go b/internal/pkg/logger/logrous/logrous_logger.go index 277bd89a..c3539de4 100644 --- a/internal/pkg/logger/logrous/logrous_logger.go +++ b/internal/pkg/logger/logrous/logrous_logger.go @@ -4,7 +4,7 @@ import ( "os" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/config" @@ -12,6 +12,7 @@ import ( "github.com/nolleh/caption_json_formatter" "github.com/sirupsen/logrus" + "github.com/uptrace/opentelemetry-go-extra/otellogrus" ) type logrusLogger struct { @@ -32,7 +33,10 @@ var loggerLevelMap = map[string]logrus.Level{ } // NewLogrusLogger creates a new logrus logger -func NewLogrusLogger(cfg *config2.LogOptions, env environemnt.Environment) logger.Logger { +func NewLogrusLogger( + cfg *config2.LogOptions, + env environment.Environment, +) logger.Logger { logrusLogger := &logrusLogger{level: cfg.LogLevel, logOptions: cfg} logrusLogger.initLogger(env) @@ -40,7 +44,7 @@ func NewLogrusLogger(cfg *config2.LogOptions, env environemnt.Environment) logge } // InitLogger Init logger -func (l *logrusLogger) initLogger(env environemnt.Environment) { +func (l *logrusLogger) initLogger(env environment.Environment) { logLevel := l.GetLoggerLevel() // Create a new instance of the logger. You can have any number of instances. @@ -65,6 +69,16 @@ func (l *logrusLogger) initLogger(env environemnt.Environment) { logrusLogger.SetFormatter(&caption_json_formatter.Formatter{PrettyPrint: true}) } + if l.logOptions.EnableTracing { + // Instrument logrus. + logrus.AddHook(otellogrus.NewHook(otellogrus.WithLevels( + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + ))) + } + l.logger = logrusLogger } @@ -190,6 +204,8 @@ func (l *logrusLogger) GrpcClientInterceptorLogger( ) } -func (l *logrusLogger) mapToFields(fields map[string]interface{}) *logrus.Entry { +func (l *logrusLogger) mapToFields( + fields map[string]interface{}, +) *logrus.Entry { return l.logger.WithFields(logrus.Fields{"ss": 1}) } diff --git a/internal/pkg/logger/pipelines/logging_pipeline.go b/internal/pkg/logger/pipelines/logging_pipeline.go new file mode 100644 index 00000000..069bf62f --- /dev/null +++ b/internal/pkg/logger/pipelines/logging_pipeline.go @@ -0,0 +1,58 @@ +package loggingpipelines + +import ( + "context" + "fmt" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/mehdihadeli/go-mediatr" +) + +type requestLoggerPipeline struct { + logger logger.Logger +} + +func NewMediatorLoggingPipeline(l logger.Logger) mediatr.PipelineBehavior { + return &requestLoggerPipeline{logger: l} +} + +func (r *requestLoggerPipeline) Handle( + ctx context.Context, + request interface{}, + next mediatr.RequestHandlerFunc, +) (interface{}, error) { + startTime := time.Now() + defer func() { + elapsed := time.Since(startTime) + r.logger.Infof("Request took %s", elapsed) + }() + + requestName := typeMapper.GetNonePointerTypeName(request) + + r.logger.Infow( + fmt.Sprintf("Handling request: '%s'", requestName), + logger.Fields{"Request": request}, + ) + + response, err := next(ctx) + if err != nil { + r.logger.Infof("Request failed with error: %v", err) + + return nil, err + } + + responseName := typeMapper.GetNonePointerTypeName(response) + + r.logger.Infow( + fmt.Sprintf( + "Request handled successfully with response: '%s'", + responseName, + ), + logger.Fields{"Response": response}, + ) + + return response, nil +} diff --git a/internal/pkg/logger/zap/zap_logger.go b/internal/pkg/logger/zap/zap_logger.go index bb142a9d..7f73402e 100644 --- a/internal/pkg/logger/zap/zap_logger.go +++ b/internal/pkg/logger/zap/zap_logger.go @@ -4,12 +4,13 @@ import ( "os" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/models" + "github.com/uptrace/opentelemetry-go-extra/otelzap" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -40,9 +41,13 @@ var loggerLevelMap = map[string]zapcore.Level{ } // NewZapLogger create new zap logger -func NewZapLogger(cfg *config2.LogOptions, env environemnt.Environment) ZapLogger { +func NewZapLogger( + cfg *config2.LogOptions, + env environment.Environment, +) ZapLogger { zapLogger := &zapLogger{level: cfg.LogLevel, logOptions: cfg} zapLogger.initLogger(env) + return zapLogger } @@ -55,11 +60,12 @@ func (l *zapLogger) getLoggerLevel() zapcore.Level { if !exist { return zapcore.DebugLevel } + return level } // InitLogger Init logger -func (l *zapLogger) initLogger(env environemnt.Environment) { +func (l *zapLogger) initLogger(env environment.Environment) { logLevel := l.getLoggerLevel() logWriter := zapcore.AddSync(os.Stdout) @@ -109,6 +115,11 @@ func (l *zapLogger) initLogger(env environemnt.Environment) { logger := zap.New(core, options...) + if l.logOptions.EnableTracing { + // add logs as events to tracing + logger = otelzap.New(logger).Logger + } + l.logger = logger l.sugarLogger = logger.Sugar() } @@ -301,6 +312,7 @@ func getFieldType(value interface{}) zapcore.FieldType { case error: return zapcore.ErrorType default: - return zapcore.StringerType + // uses reflection to serialize arbitrary objects, so it can be slow and allocation-heavy. + return zapcore.ReflectType } } diff --git a/internal/pkg/mapper/mapper.go b/internal/pkg/mapper/mapper.go index e5693696..2d1826ed 100644 --- a/internal/pkg/mapper/mapper.go +++ b/internal/pkg/mapper/mapper.go @@ -7,8 +7,8 @@ import ( "fmt" "reflect" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflection_helper" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflectionhelper" "emperror.dev/errors" "github.com/ahmetb/go-linq/v3" @@ -80,9 +80,16 @@ func CreateMap[TSrc any, TDst any]() error { return ErrUnsupportedMap } - if srcType.Kind() == reflect.Ptr && srcType.Elem().Kind() == reflect.Struct { - pointerStructTypeKey := mappingsEntry{SourceType: srcType, DestinationType: desType} - nonePointerStructTypeKey := mappingsEntry{SourceType: srcType.Elem(), DestinationType: desType.Elem()} + if srcType.Kind() == reflect.Ptr && + srcType.Elem().Kind() == reflect.Struct { + pointerStructTypeKey := mappingsEntry{ + SourceType: srcType, + DestinationType: desType, + } + nonePointerStructTypeKey := mappingsEntry{ + SourceType: srcType.Elem(), + DestinationType: desType.Elem(), + } if _, ok := maps[nonePointerStructTypeKey]; ok { return ErrMapAlreadyExists } @@ -108,11 +115,13 @@ func CreateMap[TSrc any, TDst any]() error { maps[pointerStructTypeKey] = nil } - if srcType.Kind() == reflect.Ptr && srcType.Elem().Kind() == reflect.Struct { + if srcType.Kind() == reflect.Ptr && + srcType.Elem().Kind() == reflect.Struct { srcType = srcType.Elem() } - if desType.Kind() == reflect.Ptr && desType.Elem().Kind() == reflect.Struct { + if desType.Kind() == reflect.Ptr && + desType.Elem().Kind() == reflect.Struct { desType = desType.Elem() } @@ -163,14 +172,16 @@ func Map[TDes any, TSrc any](src TSrc) (TDes, error) { desIsArray := false srcIsArray := false - if srcType.Kind() == reflect.Array || (srcType.Kind() == reflect.Ptr && srcType.Elem().Kind() == reflect.Array) || + if srcType.Kind() == reflect.Array || + (srcType.Kind() == reflect.Ptr && srcType.Elem().Kind() == reflect.Array) || srcType.Kind() == reflect.Slice || (srcType.Kind() == reflect.Ptr && srcType.Elem().Kind() == reflect.Slice) { srcType = srcType.Elem() srcIsArray = true } - if desType.Kind() == reflect.Array || (desType.Kind() == reflect.Ptr && desType.Elem().Kind() == reflect.Array) || + if desType.Kind() == reflect.Array || + (desType.Kind() == reflect.Ptr && desType.Elem().Kind() == reflect.Array) || desType.Kind() == reflect.Slice || (desType.Kind() == reflect.Ptr && desType.Elem().Kind() == reflect.Slice) { desType = desType.Elem() @@ -209,7 +220,7 @@ func configProfile(srcType reflect.Type, destType reflect.Type) { // check for provided types kind. // if not struct - skip. if srcType.Kind() != reflect.Struct { - defaultLogger.Logger.Errorf( + defaultLogger.GetLogger().Errorf( "expected reflect.Struct kind for type %s, but got %s", srcType.String(), srcType.Kind().String(), @@ -217,7 +228,7 @@ func configProfile(srcType reflect.Type, destType reflect.Type) { } if destType.Kind() != reflect.Struct { - defaultLogger.Logger.Errorf( + defaultLogger.GetLogger().Errorf( "expected reflect.Struct kind for type %s, but got %s", destType.String(), destType.Kind().String(), @@ -234,7 +245,10 @@ func configProfile(srcType reflect.Type, destType reflect.Type) { for srcKey, srcTag := range srcMeta.keysToTags { if _, ok := destMeta.keysToTags[strcase.ToCamel(srcKey)]; ok { - profile = append(profile, [2]string{srcKey, strcase.ToCamel(srcKey)}) + profile = append( + profile, + [2]string{srcKey, strcase.ToCamel(srcKey)}, + ) } // case src key equals dest key @@ -319,7 +333,7 @@ func mapStructs[TDes any, TSrc any](src reflect.Value, dest reflect.Value) { // if types or their slices were not registered - abort profile, ok := profiles[getProfileKey(src.Type(), dest.Type())] if !ok { - defaultLogger.Logger.Errorf( + defaultLogger.GetLogger().Errorf( "no conversion specified for types %s and %s", src.Type().String(), dest.Type().String(), @@ -337,7 +351,9 @@ func mapStructs[TDes any, TSrc any](src reflect.Value, dest reflect.Value) { // var destinationFieldValue reflect.Value if !sourceField.CanInterface() { if mapperConfig.MapUnexportedFields { - sourceFiledValue = reflectionHelper.GetFieldValue(sourceField) + sourceFiledValue = reflectionHelper.GetFieldValue( + sourceField, + ) } else { // for getting pointer for non-pointer struct we can use reflect.Addr() for calling pointer receivers properties sourceFiledValue = reflectionHelper.GetFieldValueFromMethodAndReflectValue(src.Addr(), strcase.ToCamel(keys[SrcKeyIndex])) @@ -404,7 +420,10 @@ func mapMaps[TDes any, TSrc any](src reflect.Value, dest reflect.Value) { } } -func processValues[TDes any, TSrc any](src reflect.Value, dest reflect.Value) error { +func processValues[TDes any, TSrc any]( + src reflect.Value, + dest reflect.Value, +) error { // if src of dest is an interface - get underlying type if src.Kind() == reflect.Interface { src = src.Elem() diff --git a/internal/pkg/messaging/bus/bus.go b/internal/pkg/messaging/bus/bus.go deleted file mode 100644 index 9719400f..00000000 --- a/internal/pkg/messaging/bus/bus.go +++ /dev/null @@ -1,12 +0,0 @@ -package bus - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" -) - -type Bus interface { - producer.Producer - consumer.BusControl - consumer.ConsumerConnector -} diff --git a/internal/pkg/messaging/otel/tracing/utils.go b/internal/pkg/messaging/otel/tracing/utils.go deleted file mode 100644 index 91c17097..00000000 --- a/internal/pkg/messaging/otel/tracing/utils.go +++ /dev/null @@ -1,38 +0,0 @@ -package tracing - -import ( - "context" - - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/trace" -) - -// TraceMessagingErrFromSpan setting span with status error with error message -func TraceMessagingErrFromSpan(span trace.Span, err error) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(MessagingErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} - -func TraceMessagingErrFromContext(ctx context.Context, err error) error { - // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors - span := trace.SpanFromContext(ctx) - defer span.End() - - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(MessagingErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} diff --git a/internal/pkg/migration/goose/postgres.go b/internal/pkg/migration/goose/postgres.go index 91e3d0c3..11298cc1 100644 --- a/internal/pkg/migration/goose/postgres.go +++ b/internal/pkg/migration/goose/postgres.go @@ -43,21 +43,34 @@ func (m *goosePostgresMigrator) Down(_ context.Context, version uint) error { return err } -func (m *goosePostgresMigrator) executeCommand(command migration.CommandType, version uint) error { +func (m *goosePostgresMigrator) executeCommand( + command migration.CommandType, + version uint, +) error { switch command { case migration.Up: if version == 0 { - // In test environment, ewe need a fix for applying application working directory correctly. we will apply this in our environment setup process in `config/environment` file + // In test environment, we need a fix for applying application working directory correctly. we will apply this in our environment setup process in `config/environment` file return goose.Run("up", m.db, m.config.MigrationsDir) } - return goose.Run("up-to VERSION ", m.db, m.config.MigrationsDir, strconv.FormatUint(uint64(version), 10)) + return goose.Run( + "up-to VERSION ", + m.db, + m.config.MigrationsDir, + strconv.FormatUint(uint64(version), 10), + ) case migration.Down: if version == 0 { return goose.Run("down", m.db, m.config.MigrationsDir) } - return goose.Run("down-to VERSION ", m.db, m.config.MigrationsDir, strconv.FormatUint(uint64(version), 10)) + return goose.Run( + "down-to VERSION ", + m.db, + m.config.MigrationsDir, + strconv.FormatUint(uint64(version), 10), + ) default: return errors.New("invalid migration direction") } diff --git a/internal/pkg/migration/migration_options.go b/internal/pkg/migration/migration_options.go index 99997a0e..b6c8701d 100644 --- a/internal/pkg/migration/migration_options.go +++ b/internal/pkg/migration/migration_options.go @@ -2,8 +2,8 @@ package migration import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) @@ -27,8 +27,8 @@ type MigrationOptions struct { SkipMigration bool `mapstructure:"skipMigration"` } -func ProvideConfig(environment environemnt.Environment) (*MigrationOptions, error) { - optionName := strcase.ToLowerCamel(typeMapper.GetTypeNameByT[MigrationOptions]()) +func ProvideConfig(environment environment.Environment) (*MigrationOptions, error) { + optionName := strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[MigrationOptions]()) return config.BindConfigKey[*MigrationOptions](optionName, environment) } diff --git a/internal/pkg/mongodb/health.go b/internal/pkg/mongodb/health.go index e48af264..7a132610 100644 --- a/internal/pkg/mongodb/health.go +++ b/internal/pkg/mongodb/health.go @@ -3,7 +3,7 @@ package mongodb import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "go.mongodb.org/mongo-driver/mongo" ) @@ -12,7 +12,7 @@ type mongoHealthChecker struct { client *mongo.Client } -func NewMongoHealthChecker(client *mongo.Client) health.Health { +func NewMongoHealthChecker(client *mongo.Client) contracts.Health { return &mongoHealthChecker{client} } diff --git a/internal/pkg/mongodb/helpers.go b/internal/pkg/mongodb/helpers.go index 3f10068f..6f180059 100644 --- a/internal/pkg/mongodb/helpers.go +++ b/internal/pkg/mongodb/helpers.go @@ -31,28 +31,31 @@ func Paginate[T any]( limit := int64(listQuery.GetLimit()) skip := int64(listQuery.GetOffset()) - cursor, err := collection.Find(ctx, filter, &options.FindOptions{ - Limit: &limit, - Skip: &skip, - }) + cursor, err := collection.Find( + ctx, + filter, + &options.FindOptions{ + Limit: &limit, + Skip: &skip, + }) if err != nil { - return nil, errors.WrapIf(err, "Find") + return nil, err } - defer cursor.Close(ctx) // nolint: errcheck - products := make([]T, 0, listQuery.GetSize()) + defer cursor.Close(ctx) - for cursor.Next(ctx) { - var prod T - if err := cursor.Decode(&prod); err != nil { - return nil, errors.WrapIf(err, "Find") - } - products = append(products, prod) - } + var items []T - if err := cursor.Err(); err != nil { - return nil, errors.WrapIf(err, "cursor.Err") + // https://www.mongodb.com/docs/drivers/go/current/fundamentals/crud/read-operations/cursor/#retrieve-all-documents + err = cursor.All(ctx, &items) + if err != nil { + return nil, err } - return utils.NewListResult[T](products, listQuery.GetSize(), listQuery.GetPage(), count), nil + return utils.NewListResult[T]( + items, + listQuery.GetSize(), + listQuery.GetPage(), + count, + ), nil } diff --git a/internal/pkg/mongodb/mongo_fx.go b/internal/pkg/mongodb/mongo_fx.go index f4ec39a4..6d1890f7 100644 --- a/internal/pkg/mongodb/mongo_fx.go +++ b/internal/pkg/mongodb/mongo_fx.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "go.mongodb.org/mongo-driver/mongo" @@ -26,7 +26,7 @@ var ( NewMongoDB, fx.Annotate( NewMongoHealthChecker, - fx.As(new(health.Health)), + fx.As(new(contracts.Health)), fx.ResultTags(fmt.Sprintf(`group:"%s"`, "healths")), ), ) @@ -34,7 +34,11 @@ var ( mongoInvokes = fx.Invoke(registerHooks) //nolint:gochecknoglobals ) -func registerHooks(lc fx.Lifecycle, client *mongo.Client, logger logger.Logger) { +func registerHooks( + lc fx.Lifecycle, + client *mongo.Client, + logger logger.Logger, +) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { err := client.Ping(ctx, nil) diff --git a/internal/pkg/mongodb/mongo_options.go b/internal/pkg/mongodb/mongo_options.go index 46ba2f6b..75810244 100644 --- a/internal/pkg/mongodb/mongo_options.go +++ b/internal/pkg/mongodb/mongo_options.go @@ -2,22 +2,27 @@ package mongodb import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) type MongoDbOptions struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - User string `mapstructure:"user"` - Password string `mapstructure:"password"` - Database string `mapstructure:"database"` - UseAuth bool `mapstructure:"useAuth"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + User string `mapstructure:"user"` + Password string `mapstructure:"password"` + Database string `mapstructure:"database"` + UseAuth bool `mapstructure:"useAuth"` + EnableTracing bool `mapstructure:"enableTracing" default:"true"` } -func provideConfig(environment environemnt.Environment) (*MongoDbOptions, error) { - optionName := strcase.ToLowerCamel(typeMapper.GetTypeNameByT[MongoDbOptions]()) +func provideConfig( + environment environment.Environment, +) (*MongoDbOptions, error) { + optionName := strcase.ToLowerCamel( + typeMapper.GetGenericTypeNameByT[MongoDbOptions](), + ) return config.BindConfigKey[*MongoDbOptions](optionName, environment) } diff --git a/internal/pkg/mongodb/mongodb.go b/internal/pkg/mongodb/mongodb.go index e448c09f..338f079e 100644 --- a/internal/pkg/mongodb/mongodb.go +++ b/internal/pkg/mongodb/mongodb.go @@ -8,6 +8,7 @@ import ( "github.com/kamva/mgm/v3" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" _ "github.com/golang-migrate/migrate/v4/source/file" ) @@ -21,7 +22,13 @@ const ( // NewMongoDB Create new MongoDB client func NewMongoDB(cfg *MongoDbOptions) (*mongo.Client, error) { - uriAddress := fmt.Sprintf("mongodb://%s:%s@%s:%d", cfg.User, cfg.Password, cfg.Host, cfg.Port) + uriAddress := fmt.Sprintf( + "mongodb://%s:%s@%s:%d", + cfg.User, + cfg.Password, + cfg.Host, + cfg.Port, + ) opt := options.Client().ApplyURI(uriAddress). SetConnectTimeout(connectTimeout). SetMaxConnIdleTime(maxConnIdleTime). @@ -29,7 +36,9 @@ func NewMongoDB(cfg *MongoDbOptions) (*mongo.Client, error) { SetMaxPoolSize(maxPoolSize) if cfg.UseAuth { - opt = opt.SetAuth(options.Credential{Username: cfg.User, Password: cfg.Password}) + opt = opt.SetAuth( + options.Credential{Username: cfg.User, Password: cfg.Password}, + ) } ctx := context.Background() @@ -38,6 +47,11 @@ func NewMongoDB(cfg *MongoDbOptions) (*mongo.Client, error) { return nil, err } + if cfg.EnableTracing { + // add tracing + opt.Monitor = otelmongo.NewMonitor() + } + // setup https://github.com/Kamva/mgm err = mgm.SetDefaultConfig(nil, cfg.Database, opt) if err != nil { diff --git a/internal/pkg/mongodb/repository/mongo_generic_repository.go b/internal/pkg/mongodb/repository/mongo_generic_repository.go index 32b74215..e1c284ba 100644 --- a/internal/pkg/mongodb/repository/mongo_generic_repository.go +++ b/internal/pkg/mongodb/repository/mongo_generic_repository.go @@ -6,11 +6,11 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data/specification" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" - reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflection_helper" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflectionhelper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "emperror.dev/errors" @@ -61,9 +61,12 @@ func NewGenericMongoRepository[TEntity interface{}]( } } -func (m *mongoGenericRepository[TDataModel, TEntity]) Add(ctx context.Context, entity TEntity) error { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (m *mongoGenericRepository[TDataModel, TEntity]) Add( + ctx context.Context, + entity TEntity, +) error { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) @@ -91,7 +94,10 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) Add(ctx context.Context, e return nil } -func (m *mongoGenericRepository[TDataModel, TEntity]) AddAll(ctx context.Context, entities []TEntity) error { +func (m *mongoGenericRepository[TDataModel, TEntity]) AddAll( + ctx context.Context, + entities []TEntity, +) error { for _, entity := range entities { err := m.Add(ctx, entity) if err != nil { @@ -102,9 +108,12 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) AddAll(ctx context.Context return nil } -func (m *mongoGenericRepository[TDataModel, TEntity]) GetById(ctx context.Context, id uuid.UUID) (TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (m *mongoGenericRepository[TDataModel, TEntity]) GetById( + ctx context.Context, + id uuid.UUID, +) (TEntity, error) { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) if modelType == dataModelType { @@ -118,12 +127,18 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) GetById(ctx context.Contex if err == mongo.ErrNoDocuments { return *new(TEntity), customErrors.NewNotFoundErrorWrap( err, - fmt.Sprintf("can't find the entity with id %s into the database.", id.String()), + fmt.Sprintf( + "can't find the entity with id %s into the database.", + id.String(), + ), ) } return *new(TEntity), errors.WrapIf( err, - fmt.Sprintf("can't find the entity with id %s into the database.", id.String()), + fmt.Sprintf( + "can't find the entity with id %s into the database.", + id.String(), + ), ) } return model, nil @@ -148,12 +163,17 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) GetAll( ctx context.Context, listQuery *utils.ListQuery, ) (*utils.ListResult[TEntity], error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) if modelType == dataModelType { - result, err := mongodb.Paginate[TEntity](ctx, listQuery, collection, nil) + result, err := mongodb.Paginate[TEntity]( + ctx, + listQuery, + collection, + nil, + ) if err != nil { return nil, err } @@ -176,37 +196,49 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) Search( searchTerm string, listQuery *utils.ListQuery, ) (*utils.ListResult[TEntity], error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) if modelType == dataModelType { - fields := reflectionHelper.GetAllFields(typeMapper.GetTypeFromGeneric[TEntity]()) + fields := reflectionHelper.GetAllFields( + typeMapper.GetGenericTypeByT[TEntity](), + ) var a bson.A for _, field := range fields { if field.Type.Kind() != reflect.String { continue } name := strcase.ToLowerCamel(field.Name) - a = append(a, bson.D{{Key: name, Value: primitive.Regex{Pattern: searchTerm, Options: "gi"}}}) + a = append( + a, + bson.D{ + {Key: name, Value: primitive.Regex{Pattern: searchTerm}}, + }, + ) } filter := bson.D{ {Key: "$or", Value: a}, } - result, err := mongodb.Paginate[TEntity](ctx, listQuery, collection, filter) + result, err := mongodb.Paginate[TEntity]( + ctx, + listQuery, + collection, + filter, + ) if err != nil { return nil, err } return result, nil } else { - fields := reflectionHelper.GetAllFields(typeMapper.GetTypeFromGeneric[TDataModel]()) + fields := reflectionHelper.GetAllFields(typeMapper.GetGenericTypeByT[TDataModel]()) var a bson.A for _, field := range fields { if field.Type.Kind() != reflect.String { continue } name := strcase.ToLowerCamel(field.Name) - a = append(a, bson.D{{Key: name, Value: primitive.Regex{Pattern: searchTerm, Options: "gi"}}}) + a = append(a, bson.D{{Key: name, Value: primitive.Regex{Pattern: searchTerm}}}) } filter := bson.D{ {Key: "$or", Value: a}, @@ -227,8 +259,8 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) GetByFilter( ctx context.Context, filters map[string]interface{}, ) ([]TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) // we could use also bson.D{} for filtering, it is also a map @@ -281,8 +313,8 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) FirstOrDefault( ctx context.Context, filters map[string]interface{}, ) (TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) if modelType == dataModelType { @@ -315,9 +347,12 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) FirstOrDefault( } } -func (m *mongoGenericRepository[TDataModel, TEntity]) Update(ctx context.Context, entity TEntity) error { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (m *mongoGenericRepository[TDataModel, TEntity]) Update( + ctx context.Context, + entity TEntity, +) error { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) ops := options.FindOneAndUpdate() ops.SetReturnDocument(options.After) @@ -367,7 +402,10 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) Update(ctx context.Context return nil } -func (m *mongoGenericRepository[TDataModel, TEntity]) UpdateAll(ctx context.Context, entities []TEntity) error { +func (m *mongoGenericRepository[TDataModel, TEntity]) UpdateAll( + ctx context.Context, + entities []TEntity, +) error { for _, e := range entities { err := m.Update(ctx, e) if err != nil { @@ -378,7 +416,10 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) UpdateAll(ctx context.Cont return nil } -func (m *mongoGenericRepository[TDataModel, TEntity]) Delete(ctx context.Context, id uuid.UUID) error { +func (m *mongoGenericRepository[TDataModel, TEntity]) Delete( + ctx context.Context, + id uuid.UUID, +) error { collection := m.db.Database(m.databaseName).Collection(m.collectionName) if err := collection.FindOneAndDelete(ctx, bson.M{"_id": id.String()}).Err(); err != nil { @@ -393,8 +434,8 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) SkipTake( skip int, take int, ) ([]TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() collection := m.db.Database(m.databaseName).Collection(m.collectionName) l := int64(take) s := int64(skip) @@ -436,7 +477,9 @@ func (m *mongoGenericRepository[TDataModel, TEntity]) SkipTake( } } -func (m *mongoGenericRepository[TDataModel, TEntity]) Count(ctx context.Context) int64 { +func (m *mongoGenericRepository[TDataModel, TEntity]) Count( + ctx context.Context, +) int64 { collection := m.db.Database(m.databaseName).Collection(m.collectionName) count, err := collection.CountDocuments(ctx, bson.M{}) if err != nil { diff --git a/internal/pkg/mongodb/repository/mongo_generic_repository_test.go b/internal/pkg/mongodb/repository/mongo_generic_repository_test.go index 7837d708..96b75adf 100644 --- a/internal/pkg/mongodb/repository/mongo_generic_repository_test.go +++ b/internal/pkg/mongodb/repository/mongo_generic_repository_test.go @@ -2,28 +2,24 @@ package repository import ( "context" - "log" "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data/specification" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - mongo2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/mongo" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" + mongocontainer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/mongo" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) -const ( - DatabaseName = "catalogs_write" - CollectionName = "products" -) - // Product is a domain_events entity type Product struct { ID string @@ -39,98 +35,121 @@ type ProductMongo struct { IsAvailable bool `json:"isAvailable" bson:"isAvailable"` } -func init() { - err := mapper.CreateMap[*ProductMongo, *Product]() - if err != nil { - log.Fatal(err) - } +type mongoGenericRepositoryTest struct { + suite.Suite + databaseName string + collectionName string + mongoClient *mongo.Client + productRepository data.GenericRepository[*ProductMongo] + productRepositoryWithDataModel data.GenericRepositoryWithDataModel[*ProductMongo, *Product] + products []*ProductMongo +} + +func TestMongoGenericRepository(t *testing.T) { + suite.Run( + t, + &mongoGenericRepositoryTest{ + databaseName: "catalogs_write", + collectionName: "products", + }, + ) +} + +func (c *mongoGenericRepositoryTest) SetupSuite() { + opts, err := mongocontainer.NewMongoTestContainers(defaultLogger.GetLogger()). + PopulateContainerOptions(context.Background(), c.T()) + c.Require().NoError(err) + + mongoClient, err := mongodb.NewMongoDB(opts) + c.Require().NoError(err) + c.mongoClient = mongoClient + + c.productRepository = NewGenericMongoRepository[*ProductMongo]( + mongoClient, + c.databaseName, + c.collectionName, + ) + c.productRepositoryWithDataModel = NewGenericMongoRepositoryWithDataModel[*ProductMongo, *Product]( + mongoClient, + c.databaseName, + c.collectionName, + ) + + err = mapper.CreateMap[*ProductMongo, *Product]() + c.Require().NoError(err) err = mapper.CreateMap[*Product, *ProductMongo]() - if err != nil { - log.Fatal(err) - } + c.Require().NoError(err) } -func Test_Add(t *testing.T) { +func (c *mongoGenericRepositoryTest) SetupTest() { + p, err := c.seedData(context.Background()) + c.Require().NoError(err) + c.products = p +} + +func (c *mongoGenericRepositoryTest) TearDownTest() { + err := c.cleanupMongoData() + c.Require().NoError(err) +} + +func (c *mongoGenericRepositoryTest) Test_Add() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) product := &ProductMongo{ - ID: uuid.NewV4(). - String(), // we generate id ourselves because auto generate mongo string id column with type _id is not an uuid - Name: "added_product", - Weight: 100, + ID: uuid.NewV4().String(), + Name: gofakeit.Name(), + Weight: gofakeit.Number(100, 1000), IsAvailable: true, } - err = repository.Add(ctx, product) - if err != nil { - t.Fatal(err) - } + err := c.productRepository.Add(ctx, product) + c.Require().NoError(err) id, err := uuid.FromString(product.ID) - if err != nil { - return - } + c.Require().NoError(err) - p, err := repository.GetById(ctx, id) - if err != nil { - return - } + p, err := c.productRepository.GetById(ctx, id) + c.Require().NoError(err) - assert.NotNil(t, p) - assert.Equal(t, product.ID, p.ID) + c.Assert().NotNil(p) + c.Assert().Equal(product.ID, p.ID) } -func Test_Add_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Add_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - product := &Product{ - ID: uuid.NewV4(). - String(), + product := &ProductMongo{ // we generate id ourselves because auto generate mongo string id column with type _id is not an uuid - Name: "added_product", - Weight: 100, + ID: uuid.NewV4().String(), + Name: gofakeit.Name(), + Weight: gofakeit.Number(100, 1000), IsAvailable: true, } - err = repository.Add(ctx, product) - if err != nil { - t.Fatal(err) - } + err := c.productRepository.Add(ctx, product) + c.Require().NoError(err) id, err := uuid.FromString(product.ID) - if err != nil { - return - } + c.Require().NoError(err) - p, err := repository.GetById(ctx, id) - if err != nil { - return - } + p, err := c.productRepository.GetById(ctx, id) + c.Require().NoError(err) - assert.NotNil(t, p) - assert.Equal(t, product.ID, p.ID) + c.Assert().NotNil(p) + c.Assert().Equal(product.ID, p.ID) } -func Test_Get_By_Id(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Get_By_Id() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } + all, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + p := all.Items[0] id, err := uuid.FromString(p.ID) + name := p.Name testCases := []struct { Name string @@ -138,7 +157,7 @@ func Test_Get_By_Id(t *testing.T) { ExpectResult *ProductMongo }{ { - Name: "ExistingProduct", + Name: name, ProductId: id, ExpectResult: p, }, @@ -149,12 +168,11 @@ func Test_Get_By_Id(t *testing.T) { }, } - for _, c := range testCases { - c := c - t.Run(c.Name, func(t *testing.T) { + for _, s := range testCases { + c.T().Run(s.Name, func(t *testing.T) { t.Parallel() - res, err := repository.GetById(ctx, c.ProductId) - if c.ExpectResult == nil { + res, err := c.productRepository.GetById(ctx, s.ProductId) + if s.ExpectResult == nil { assert.Error(t, err) assert.True(t, customErrors.IsNotFoundError(err)) assert.Nil(t, res) @@ -167,20 +185,18 @@ func Test_Get_By_Id(t *testing.T) { } } -func Test_Get_By_Id_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Get_By_Id_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } + all, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) p := all.Items[0] id, err := uuid.FromString(p.ID) + name := p.Name testCases := []struct { Name string @@ -188,7 +204,7 @@ func Test_Get_By_Id_With_Data_Model(t *testing.T) { ExpectResult *Product }{ { - Name: "ExistingProduct", + Name: name, ProductId: id, ExpectResult: p, }, @@ -199,12 +215,14 @@ func Test_Get_By_Id_With_Data_Model(t *testing.T) { }, } - for _, c := range testCases { - c := c - t.Run(c.Name, func(t *testing.T) { + for _, s := range testCases { + c.T().Run(s.Name, func(t *testing.T) { t.Parallel() - res, err := repository.GetById(ctx, c.ProductId) - if c.ExpectResult == nil { + res, err := c.productRepositoryWithDataModel.GetById( + ctx, + s.ProductId, + ) + if s.ExpectResult == nil { assert.Error(t, err) assert.True(t, customErrors.IsNotFoundError(err)) assert.Nil(t, res) @@ -217,359 +235,355 @@ func Test_Get_By_Id_With_Data_Model(t *testing.T) { } } -func Test_First_Or_Default(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_First_Or_Default() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } + all, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + p := all.Items[0] - single, err := repository.FirstOrDefault(ctx, map[string]interface{}{"_id": p.ID}) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) + single, err := c.productRepository.FirstOrDefault( + ctx, + map[string]interface{}{"_id": p.ID}, + ) + c.Require().NoError(err) + c.Assert().NotNil(single) } -func Test_First_Or_Default_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_First_Or_Default_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - all, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - return - } + all, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + p := all.Items[0] - single, err := repository.FirstOrDefault(ctx, map[string]interface{}{"_id": p.ID}) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) + single, err := c.productRepositoryWithDataModel.FirstOrDefault( + ctx, + map[string]interface{}{"_id": p.ID}, + ) + + c.Require().NoError(err) + c.Assert().NotNil(single) } -func Test_Get_All(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Get_All() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - models, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) - assert.NotEmpty(t, models.Items) + c.Assert().NotEmpty(models.Items) } -func Test_Get_All_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Get_All_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - models, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) - assert.NotEmpty(t, models.Items) + c.Assert().NotEmpty(models.Items) } -func Test_Search(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Search() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - models, err := repository.Search(ctx, "seed_product1", utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepository.Search( + ctx, + c.products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) - assert.NotEmpty(t, models.Items) - assert.Equal(t, len(models.Items), 1) + c.Assert().NotEmpty(models.Items) + c.Assert().Equal(len(models.Items), 1) } -func Test_Search_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Search_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - models, err := repository.Search(ctx, "seed_product1", utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepositoryWithDataModel.Search( + ctx, + c.products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) - assert.NotEmpty(t, models.Items) - assert.Equal(t, len(models.Items), 1) + c.Assert().NotEmpty(models.Items) + c.Assert().Equal(len(models.Items), 1) } -func Test_GetByFilter(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_GetByFilter() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - models, err := repository.GetByFilter(ctx, map[string]interface{}{"name": "seed_product1"}) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepository.GetByFilter( + ctx, + map[string]interface{}{"name": c.products[0].Name}, + ) + c.Require().NoError(err) - assert.NotEmpty(t, models) - assert.Equal(t, len(models), 1) + c.Assert().NotEmpty(models) + c.Assert().Equal(len(models), 1) } -func Test_GetByFilter_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_GetByFilter_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - models, err := repository.GetByFilter(ctx, map[string]interface{}{"name": "seed_product1"}) - if err != nil { - t.Fatal(err) - } + models, err := c.productRepositoryWithDataModel.GetByFilter( + ctx, + map[string]interface{}{"name": c.products[0].Name}, + ) + c.Require().NoError(err) - assert.NotEmpty(t, models) - assert.Equal(t, len(models), 1) + c.Assert().NotEmpty(models) + c.Assert().Equal(len(models), 1) } -func Test_Update(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Update() { ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + products, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + product := products.Items[0] product.Name = "product2_updated" - err = repository.Update(ctx, product) - if err != nil { - t.Fatal(err) - } + err = c.productRepository.Update(ctx, product) + c.Require().NoError(err) id, err := uuid.FromString(product.ID) - if err != nil { - t.Fatal(err) - } + c.Require().NoError(err) - single, err := repository.GetById(ctx, id) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) - assert.Equal(t, "product2_updated", single.Name) + single, err := c.productRepository.GetById(ctx, id) + c.Require().NoError(err) + + c.Assert().NotNil(single) + c.Assert().Equal("product2_updated", single.Name) } -func Test_Update_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Update_With_Data_Model() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + products, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + product := products.Items[0] product.Name = "product2_updated" - err = repository.Update(ctx, product) - if err != nil { - t.Fatal(err) - } + err = c.productRepositoryWithDataModel.Update(ctx, product) + c.Require().NoError(err) id, err := uuid.FromString(product.ID) - if err != nil { - t.Fatal(err) - } + c.Require().NoError(err) - single, err := repository.GetById(ctx, id) - if err != nil { - t.Fatal(err) - } - assert.NotNil(t, single) - assert.Equal(t, "product2_updated", single.Name) -} - -func Test_Delete(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } - product := products.Items[0] - - id, err := uuid.FromString(product.ID) - if err != nil { - t.Fatal(err) - } + single, err := c.productRepositoryWithDataModel.GetById(ctx, id) + c.Require().NoError(err) - err = repository.Delete(ctx, id) - if err != nil { - return - } - - single, err := repository.GetById(ctx, id) - assert.Nil(t, single) + c.Assert().NotNil(single) + c.Assert().Equal("product2_updated", single.Name) } -func Test_Delete_With_Data_Model(t *testing.T) { +func (c *mongoGenericRepositoryTest) Test_Delete() { ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) - if err != nil { - t.Fatal(err) - } + products, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + product := products.Items[0] id, err := uuid.FromString(product.ID) - if err != nil { - t.Fatal(err) - } - - err = repository.Delete(ctx, id) - if err != nil { - return - } - - single, err := repository.GetById(ctx, id) - assert.Nil(t, single) -} - -func Test_Count(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - - count := repository.Count(ctx) - assert.Equal(t, count, int64(2)) -} - -func Test_Count_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - count := repository.Count(ctx) - assert.Equal(t, count, int64(2)) -} + c.Require().NoError(err) -func Test_Skip_Take(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } + err = c.productRepository.Delete(ctx, id) + c.Require().NoError(err) - entities, err := repository.SkipTake(ctx, 1, 1) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, len(entities), 1) + single, err := c.productRepository.GetById(ctx, id) + c.Assert().Nil(single) } -func Test_Skip_Take_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) - } - - entities, err := repository.SkipTake(ctx, 1, 1) - if err != nil { - t.Fatal(err) - } +//func Test_Delete_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) +// +// products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) +// if err != nil { +// t.Fatal(err) +// } +// product := products.Items[0] +// +// id, err := uuid.FromString(product.ID) +// if err != nil { +// t.Fatal(err) +// } +// +// err = repository.Delete(ctx, id) +// if err != nil { +// return +// } +// +// single, err := repository.GetById(ctx, id) +// assert.Nil(t, single) +//} +// +//func Test_Count(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepository(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// count := repository.Count(ctx) +// assert.Equal(t, count, int64(2)) +//} +// +//func Test_Count_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// count := repository.Count(ctx) +// assert.Equal(t, count, int64(2)) +//} +// +//func Test_Skip_Take(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepository(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.SkipTake(ctx, 1, 1) +// if err != nil { +// t.Fatal(err) +// } +// +// assert.Equal(t, len(entities), 1) +//} +// +//func Test_Skip_Take_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.SkipTake(ctx, 1, 1) +// if err != nil { +// t.Fatal(err) +// } +// +// assert.Equal(t, len(entities), 1) +//} +// +//func Test_Find(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepository(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.Find( +// ctx, +// specification.And( +// specification.Equal("is_available", true), +// specification.Equal("name", "seed_product1"), +// ), +// ) +// if err != nil { +// return +// } +// assert.Equal(t, len(entities), 1) +//} +// +//func Test_Find_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.Find( +// ctx, +// specification.And( +// specification.Equal("is_available", true), +// specification.Equal("name", "seed_product1"), +// ), +// ) +// if err != nil { +// return +// } +// assert.Equal(t, len(entities), 1) +//} +// +//func setupGenericMongoRepositoryWithDataModel( +// ctx context.Context, +// t *testing.T, +//) (data.GenericRepositoryWithDataModel[*ProductMongo, *Product], error) { +// db, err := mongocontainer.NewMongoTestContainers(defaultLogger.GetLogger()). +// Start(ctx, t) +// if err != nil { +// return nil, err +// } +// +// err = seedData(ctx, db) +// if err != nil { +// return nil, err +// } +// +// return NewGenericMongoRepositoryWithDataModel[*ProductMongo, *Product]( +// db, +// DatabaseName, +// CollectionName, +// ), nil +//} + +func (c *mongoGenericRepositoryTest) cleanupMongoData() error { + collections := []string{c.collectionName} + err := cleanupCollections( + c.mongoClient, + collections, + c.databaseName, + ) - assert.Equal(t, len(entities), 1) + return err } -func Test_Find(t *testing.T) { +func cleanupCollections( + db *mongo.Client, + collections []string, + databaseName string, +) error { + database := db.Database(databaseName) ctx := context.Background() - repository, err := setupGenericMongoRepository(ctx, t) - if err != nil { - t.Fatal(err) - } - entities, err := repository.Find( - ctx, - specification.And(specification.Equal("is_available", true), specification.Equal("name", "seed_product1")), - ) - if err != nil { - return - } - assert.Equal(t, len(entities), 1) -} + // Iterate over the collections and delete all collections + for _, collection := range collections { + collection := database.Collection(collection) -func Test_Find_With_Data_Model(t *testing.T) { - ctx := context.Background() - repository, err := setupGenericMongoRepositoryWithDataModel(ctx, t) - if err != nil { - t.Fatal(err) + err := collection.Drop(ctx) + if err != nil { + return err + } } - entities, err := repository.Find( - ctx, - specification.And(specification.Equal("is_available", true), specification.Equal("name", "seed_product1")), - ) - if err != nil { - return - } - assert.Equal(t, len(entities), 1) + return nil } -func setupGenericMongoRepositoryWithDataModel( +func (c *mongoGenericRepositoryTest) seedData( ctx context.Context, - t *testing.T, -) (data.GenericRepositoryWithDataModel[*ProductMongo, *Product], error) { - defaultLogger.SetupDefaultLogger() - - db, err := mongo2.NewMongoTestContainers(defaultLogger.Logger).Start(ctx, t) - if err != nil { - return nil, err - } - - err = seedAndMigration(ctx, db) - if err != nil { - return nil, err - } - - return NewGenericMongoRepositoryWithDataModel[*ProductMongo, *Product](db, DatabaseName, CollectionName), nil -} - -func setupGenericMongoRepository(ctx context.Context, t *testing.T) (data.GenericRepository[*ProductMongo], error) { - defaultLogger.SetupDefaultLogger() - - db, err := mongo2.NewMongoTestContainers(defaultLogger.Logger).Start(ctx, t) - if err != nil { - return nil, err - } - - err = seedAndMigration(ctx, db) - if err != nil { - return nil, err - } - - return NewGenericMongoRepository[*ProductMongo](db, DatabaseName, CollectionName), nil -} - -func seedAndMigration(ctx context.Context, db *mongo.Client) error { +) ([]*ProductMongo, error) { seedProducts := []*ProductMongo{ { ID: uuid.NewV4(). @@ -595,11 +609,12 @@ func seedAndMigration(ctx context.Context, db *mongo.Client) error { data[i] = v } - collection := db.Database(DatabaseName).Collection(CollectionName) + collection := c.mongoClient.Database(c.databaseName). + Collection(c.collectionName) _, err := collection.InsertMany(ctx, data, &options.InsertManyOptions{}) if err != nil { - return err + return nil, err } - return nil + return seedProducts, nil } diff --git a/internal/pkg/otel/config/open_telemtry_options.go b/internal/pkg/otel/config/open_telemtry_options.go deleted file mode 100644 index c1e457f7..00000000 --- a/internal/pkg/otel/config/open_telemtry_options.go +++ /dev/null @@ -1,43 +0,0 @@ -package config - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" - - "github.com/iancoleman/strcase" -) - -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[OpenTelemetryOptions]()) - -type OpenTelemetryOptions struct { - Enabled bool `mapstructure:"enabled"` - ServiceName string `mapstructure:"serviceName"` - InstrumentationName string `mapstructure:"instrumentationName"` - Id int64 `mapstructure:"id"` - AlwaysOnSampler bool `mapstructure:"alwaysOnSampler"` - JaegerExporterOptions *JaegerExporterOptions `mapstructure:"jaegerExporterOptions"` - ZipkinExporterOptions *ZipkinExporterOptions `mapstructure:"zipkinExporterOptions"` - OTelMetricsOptions *OTelMetricsOptions `mapstructure:"otelMetricsOptions"` - UseStdout bool `mapstructure:"useStdout"` -} - -type JaegerExporterOptions struct { - AgentHost string `mapstructure:"agentHost"` - AgentPort string `mapstructure:"agentPort"` -} - -type ZipkinExporterOptions struct { - Url string `mapstructure:"url"` -} - -type OTelMetricsOptions struct { - Host string `mapstructure:"host"` - Port string `mapstructure:"port"` - Name string `mapstructure:"name"` - MetricsRoutePath string `mapstructure:"metricsRoutePath"` -} - -func ProvideOtelConfig(environment environemnt.Environment) (*OpenTelemetryOptions, error) { - return config.BindConfigKey[*OpenTelemetryOptions](optionName, environment) -} diff --git a/internal/pkg/otel/tracing/consts.go b/internal/pkg/otel/constants/consts.go similarity index 77% rename from internal/pkg/otel/tracing/consts.go rename to internal/pkg/otel/constants/consts.go index 251219d2..ab77589d 100644 --- a/internal/pkg/otel/tracing/consts.go +++ b/internal/pkg/otel/constants/consts.go @@ -1,4 +1,4 @@ -package tracing +package constants const ( TraceId = "general.trace-id" @@ -6,5 +6,4 @@ const ( ParentSpanId = "general.parent-span-id" Traceparent = "general.traceparent" Timestamp = "general.timestamp" - ErrorMessage = "general.error_message" ) diff --git a/internal/pkg/otel/constants/telemetrytags/telemetrytags.go b/internal/pkg/otel/constants/telemetrytags/telemetrytags.go new file mode 100644 index 00000000..ec1982e8 --- /dev/null +++ b/internal/pkg/otel/constants/telemetrytags/telemetrytags.go @@ -0,0 +1,119 @@ +package telemetrytags + +type app struct { + DefaultSourceName string + Consumer string + EventHandler string + Subscription string + Stream string + Partition string + Request string + RequestName string + RequestResultName string + RequestResult string + Command string + CommandName string + CommandResult string + CommandResultName string + Query string + QueryName string + QueryResult string + QueryResultName string + Event string + EventName string + EventResult string + EventResultName string +} + +// https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/messaging/ +type messaging struct { + System string + Destination string + DestinationKind string + Url string + MessageId string + ConversationId string + CorrelationId string + CausationId string + Operation string +} + +type exceptions struct { + EventName string + Type string + Message string + Stacktrace string +} + +type general struct{} + +// https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/rpc/ +type grpc struct{} + +type netHttp struct { + Transport string + PeerIp string + PeerPort string + PeerName string + HostIp string + HostPort string + HostName string +} + +var App = app{ + DefaultSourceName: "app", + Consumer: "app.consumer", + EventHandler: "app.event-handler", + Subscription: "app.subscription", + Stream: "app.stream", + Partition: "app.partition", + Request: "app.request", + RequestName: "app.request_name", + RequestResultName: "app.request_result_name", + RequestResult: "app.request_result", + Command: "app.command", + CommandName: "app.command_name", + CommandResult: "app.command_result", + CommandResultName: "app.command_result_name", + Query: "app.query", + QueryName: "app.query_name", + QueryResult: "app.query_result", + QueryResultName: "app.query_result_name", + Event: "app.event", + EventName: "app.event_name", + EventResult: "app.event_result", + EventResultName: "app.event_result_name", +} + +var Exceptions = exceptions{ + EventName: "exception", + Type: "exception.type", + Message: "exception.message", + Stacktrace: "exception.stacktrace", +} + +var General = general{} + +var Grpc = grpc{} + +var Messaging = messaging{ + System: "messaging.system", + Destination: "messaging.destination", + DestinationKind: "messaging.destination_kind", + Url: "messaging.url", + MessageId: "messaging.message_id", + ConversationId: "messaging.conversation_id", + CorrelationId: "messaging.correlation_id", + CausationId: "messaging.causation_id", + Operation: "messaging.operation", +} + +var NetHttp = netHttp{ + Transport: "net.transport", + PeerIp: "net.peer.ip", + PeerPort: "net.peer.port", + PeerName: "net.peer.name", + HostIp: "net.host.ip", + HostPort: "net.host.port", + HostName: "net.host.name", +} diff --git a/internal/pkg/otel/constants/tracing/components/components.go b/internal/pkg/otel/constants/tracing/components/components.go new file mode 100644 index 00000000..3b1cd9d2 --- /dev/null +++ b/internal/pkg/otel/constants/tracing/components/components.go @@ -0,0 +1,8 @@ +package components + +const ( + CommandHandler = "command_handler" + RequestHandler = "request_handler" + QueryHandler = "query_handler" + EventHandler = "event_handler" +) diff --git a/internal/pkg/otel/constants/tracing/operations/operations.go b/internal/pkg/otel/constants/tracing/operations/operations.go new file mode 100644 index 00000000..4e5d524c --- /dev/null +++ b/internal/pkg/otel/constants/tracing/operations/operations.go @@ -0,0 +1,7 @@ +package operations + +const ( + Delete = "delete" + Read = "read" + Add = "add" +) diff --git a/internal/pkg/otel/metrics/custom_meter.go b/internal/pkg/otel/metrics/custom_meter.go new file mode 100644 index 00000000..47d2cbf5 --- /dev/null +++ b/internal/pkg/otel/metrics/custom_meter.go @@ -0,0 +1,22 @@ +package metrics + +import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +type AppMetrics interface { + metric.Meter +} + +type appMetrics struct { + metric.Meter +} + +func NewAppMeter(name string, opts ...metric.MeterOption) AppMetrics { + // Meter can be a global/package variable. + // https://github.com/open-telemetry/opentelemetry-go/blob/46f2ce5ca6adaa264c37cdbba251c9184a06ed7f/metric.go#LL35C6-L35C11 + meter := otel.Meter(name, opts...) + + return &appMetrics{Meter: meter} +} diff --git a/internal/pkg/otel/metrics/mediatr/pipelines/config.go b/internal/pkg/otel/metrics/mediatr/pipelines/config.go new file mode 100644 index 00000000..03812b0b --- /dev/null +++ b/internal/pkg/otel/metrics/mediatr/pipelines/config.go @@ -0,0 +1,43 @@ +package pipelines + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" +) + +type config struct { + logger logger.Logger + serviceName string +} + +var defaultConfig = &config{ + serviceName: "app", + logger: defaultLogger.GetLogger(), +} + +// Option specifies instrumentation configuration options. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +func WithServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} + +func WithLogger(l logger.Logger) Option { + return optionFunc(func(cfg *config) { + if cfg.logger != nil { + cfg.logger = l + } + }) +} diff --git a/internal/pkg/otel/metrics/mediatr/pipelines/mediator_metrics_pipeline.go b/internal/pkg/otel/metrics/mediatr/pipelines/mediator_metrics_pipeline.go new file mode 100644 index 00000000..f30828e1 --- /dev/null +++ b/internal/pkg/otel/metrics/mediatr/pipelines/mediator_metrics_pipeline.go @@ -0,0 +1,163 @@ +package pipelines + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants/telemetrytags" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + customAttribute "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/mehdihadeli/go-mediatr" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +type mediatorMetricsPipeline struct { + config *config + meter metrics.AppMetrics +} + +func NewMediatorMetricsPipeline( + appMetrics metrics.AppMetrics, + opts ...Option, +) mediatr.PipelineBehavior { + cfg := defaultConfig + for _, opt := range opts { + opt.apply(cfg) + } + + return &mediatorMetricsPipeline{ + config: cfg, + meter: appMetrics, + } +} + +func (r *mediatorMetricsPipeline) Handle( + ctx context.Context, + request interface{}, + next mediatr.RequestHandlerFunc, +) (interface{}, error) { + requestName := typemapper.GetSnakeTypeName(request) + + requestNameTag := telemetrytags.App.RequestName + requestTag := telemetrytags.App.Request + requestResultNameTag := telemetrytags.App.RequestResultName + requestResultTag := telemetrytags.App.RequestResult + requestType := "request" + + switch { + case strings.Contains(typemapper.GetPackageName(request), "command") || strings.Contains(typemapper.GetPackageName(request), "commands"): + requestNameTag = telemetrytags.App.CommandName + requestTag = telemetrytags.App.Command + requestResultNameTag = telemetrytags.App.CommandResultName + requestResultTag = telemetrytags.App.CommandResult + requestType = "command" + case strings.Contains(typemapper.GetPackageName(request), "query") || strings.Contains(typemapper.GetPackageName(request), "queries"): + requestNameTag = telemetrytags.App.QueryName + requestTag = telemetrytags.App.Query + requestResultNameTag = telemetrytags.App.QueryResultName + requestResultTag = telemetrytags.App.QueryResult + requestType = "query" + case strings.Contains(typemapper.GetPackageName(request), "event") || strings.Contains(typemapper.GetPackageName(request), "events"): + requestNameTag = telemetrytags.App.EventName + requestTag = telemetrytags.App.Event + requestResultNameTag = telemetrytags.App.EventResultName + requestResultTag = telemetrytags.App.EventResult + requestType = "event" + } + + successRequestsCounter, err := r.meter.Int64Counter( + fmt.Sprintf("%s.success_total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the number of success '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return nil, err + } + + failedRequestsCounter, err := r.meter.Int64Counter( + fmt.Sprintf("%s.failed_total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the number of failed '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return nil, err + } + + totalRequestsCounter, err := r.meter.Int64Counter( + fmt.Sprintf("%s.total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the total number of '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return nil, err + } + + durationValueRecorder, err := r.meter.Int64Histogram( + fmt.Sprintf("%s.duration", requestName), + metric.WithUnit("ms"), + metric.WithDescription( + fmt.Sprintf( + "Measures the duration of '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return nil, err + } + + // Start recording the duration + startTime := time.Now() + + response, err := next(ctx) + + // Calculate the duration + duration := time.Since(startTime).Milliseconds() + + // response will be nil if we have an error + responseName := typemapper.GetSnakeTypeName(response) + + opt := metric.WithAttributes( + attribute.String(requestNameTag, requestName), + customAttribute.Object(requestTag, request), + attribute.String(requestResultNameTag, responseName), + customAttribute.Object(requestResultTag, response), + ) + + // Record metrics + totalRequestsCounter.Add(ctx, 1, opt) + + if err == nil { + successRequestsCounter.Add(ctx, 1, opt) + } else { + failedRequestsCounter.Add(ctx, 1, opt) + } + + durationValueRecorder.Record(ctx, duration, opt) + + return response, err +} diff --git a/internal/pkg/otel/metrics/messaging/pipeline/config.go b/internal/pkg/otel/metrics/messaging/pipeline/config.go new file mode 100644 index 00000000..03812b0b --- /dev/null +++ b/internal/pkg/otel/metrics/messaging/pipeline/config.go @@ -0,0 +1,43 @@ +package pipelines + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" +) + +type config struct { + logger logger.Logger + serviceName string +} + +var defaultConfig = &config{ + serviceName: "app", + logger: defaultLogger.GetLogger(), +} + +// Option specifies instrumentation configuration options. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +func WithServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} + +func WithLogger(l logger.Logger) Option { + return optionFunc(func(cfg *config) { + if cfg.logger != nil { + cfg.logger = l + } + }) +} diff --git a/internal/pkg/otel/metrics/messaging/pipeline/messaging_metrics_pipeline.go b/internal/pkg/otel/metrics/messaging/pipeline/messaging_metrics_pipeline.go new file mode 100644 index 00000000..c70c6881 --- /dev/null +++ b/internal/pkg/otel/metrics/messaging/pipeline/messaging_metrics_pipeline.go @@ -0,0 +1,258 @@ +package pipelines + +import ( + "context" + "fmt" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + attribute2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +type messagingMetricsPipeline struct { + config *config + meter metrics.AppMetrics +} + +func NewMessagingMetricsPipeline( + appMetrics metrics.AppMetrics, + opts ...Option, +) pipeline.ConsumerPipeline { + cfg := defaultConfig + for _, opt := range opts { + opt.apply(cfg) + } + + return &messagingMetricsPipeline{ + config: cfg, + meter: appMetrics, + } +} + +func (m *messagingMetricsPipeline) Handle( + ctx context.Context, + consumerContext types2.MessageConsumeContext, + next pipeline.ConsumerHandlerFunc, +) error { + message := consumerContext.Message() + + successRequestsCounter, err := m.meter.Int64Counter( + fmt.Sprintf("%s.success_total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the number of success '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return err + } + + failedRequestsCounter, err := r.meter.Int64Counter( + fmt.Sprintf("%s.failed_total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the number of failed '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return err + } + + totalRequestsCounter, err := r.meter.Int64Counter( + fmt.Sprintf("%s.total", requestName), + metric.WithUnit("count"), + metric.WithDescription( + fmt.Sprintf( + "Measures the total number of '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return err + } + + durationValueRecorder, err := r.meter.Int64Histogram( + fmt.Sprintf("%s.duration", requestName), + metric.WithUnit("ms"), + metric.WithDescription( + fmt.Sprintf( + "Measures the duration of '%s' %s", + requestName, + requestType, + ), + ), + ) + if err != nil { + return err + } + + // Start recording the duration + startTime := time.Now() + + response, err := next(ctx) + + // Calculate the duration + duration := time.Since(startTime).Milliseconds() + + responseName := typeMapper.GetSnakeTypeName(response) + opt := metric.WithAttributes( + attribute.String(requestNameAttribute, requestName), + attribute2.Object(requestAttribute, request), + attribute.String(requestResultName, responseName), + attribute2.Object(requestResult, response), + ) + + // Record metrics + totalRequestsCounter.Add(ctx, 1, opt) + + if err == nil { + successRequestsCounter.Add(ctx, 1, opt) + } else { + failedRequestsCounter.Add(ctx, 1, opt) + } + + durationValueRecorder.Record(ctx, duration, opt) + + return nil +} + +// +//func (r *messagingMetricsPipeline) Handle( +// ctx context.Context, +// request interface{}, +// next mediatr.RequestHandlerFunc, +//) (interface{}, error) { +// requestName := typeMapper.GetSnakeTypeName(request) +// +// requestNameAttribute := app.RequestName +// requestAttribute := app.Request +// requestResultName := app.RequestResultName +// requestResult := app.RequestResult +// requestType := "request" +// +// switch { +// case strings.Contains(typeMapper.GetPackageName(request), "command") || strings.Contains(typeMapper.GetPackageName(request), "commands"): +// requestNameAttribute = app.ShortTypeName +// requestAttribute = app.Command +// requestResultName = app.CommandResultName +// requestResult = app.CommandResult +// requestType = "command" +// case strings.Contains(typeMapper.GetPackageName(request), "query") || strings.Contains(typeMapper.GetPackageName(request), "queries"): +// requestNameAttribute = app.QueryName +// requestAttribute = app.Query +// requestResultName = app.QueryResultName +// requestResult = app.QueryResult +// requestType = "query" +// case strings.Contains(typeMapper.GetPackageName(request), "event") || strings.Contains(typeMapper.GetPackageName(request), "events"): +// requestNameAttribute = app.EventName +// requestAttribute = app.Event +// requestResultName = app.EventResultName +// requestResult = app.EventResult +// requestType = "event" +// } +// +// successRequestsCounter, err := r.meter.Int64Counter( +// fmt.Sprintf("%s.success_total", requestName), +// metric.WithUnit("count"), +// metric.WithDescription( +// fmt.Sprintf( +// "Measures the number of success '%s' %s", +// requestName, +// requestType, +// ), +// ), +// ) +// if err != nil { +// return nil, err +// } +// +// failedRequestsCounter, err := r.meter.Int64Counter( +// fmt.Sprintf("%s.failed_total", requestName), +// metric.WithUnit("count"), +// metric.WithDescription( +// fmt.Sprintf( +// "Measures the number of failed '%s' %s", +// requestName, +// requestType, +// ), +// ), +// ) +// if err != nil { +// return nil, err +// } +// +// totalRequestsCounter, err := r.meter.Int64Counter( +// fmt.Sprintf("%s.total", requestName), +// metric.WithUnit("count"), +// metric.WithDescription( +// fmt.Sprintf( +// "Measures the total number of '%s' %s", +// requestName, +// requestType, +// ), +// ), +// ) +// if err != nil { +// return nil, err +// } +// +// durationValueRecorder, err := r.meter.Int64Histogram( +// fmt.Sprintf("%s.duration", requestName), +// metric.WithUnit("ms"), +// metric.WithDescription( +// fmt.Sprintf( +// "Measures the duration of '%s' %s", +// requestName, +// requestType, +// ), +// ), +// ) +// if err != nil { +// return nil, err +// } +// +// // Start recording the duration +// startTime := time.Now() +// +// response, err := next(ctx) +// +// // Calculate the duration +// duration := time.Since(startTime).Milliseconds() +// +// responseName := typeMapper.GetSnakeTypeName(response) +// opt := metric.WithAttributes( +// attribute.String(requestNameAttribute, requestName), +// attribute2.Object(requestAttribute, request), +// attribute.String(requestResultName, responseName), +// attribute2.Object(requestResult, response), +// ) +// +// // Record metrics +// totalRequestsCounter.Add(ctx, 1, opt) +// +// if err == nil { +// successRequestsCounter.Add(ctx, 1, opt) +// } else { +// failedRequestsCounter.Add(ctx, 1, opt) +// } +// +// durationValueRecorder.Record(ctx, duration, opt) +// +// return response, err +//} diff --git a/internal/pkg/otel/metrics/metrics.go b/internal/pkg/otel/metrics/metrics.go index 9534b1ec..06b9dded 100644 --- a/internal/pkg/otel/metrics/metrics.go +++ b/internal/pkg/otel/metrics/metrics.go @@ -1,81 +1,310 @@ package metrics +// https://github.com/riferrei/otel-with-golang/blob/main/main.go +// https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go +// https://opentelemetry.io/docs/instrumentation/go/manual/#metrics + import ( "context" + "time" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/config" "emperror.dev/errors" "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/samber/lo" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/prometheus" - api "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" ) type OtelMetrics struct { - Config *config.OpenTelemetryOptions - Logger logger.Logger - Meter api.Meter - Echo *echo.Echo + config *MetricsOptions + logger logger.Logger + appMetrics AppMetrics + environment environment.Environment + provider *metric.MeterProvider } // NewOtelMetrics adds otel metrics -// https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go func NewOtelMetrics( - config *config.OpenTelemetryOptions, + config *MetricsOptions, logger logger.Logger, + environment environment.Environment, ) (*OtelMetrics, error) { if config == nil { return nil, errors.New("metrics config can't be nil") } - e := echo.New() - e.HideBanner = false + otelMetrics := &OtelMetrics{ + config: config, + logger: logger, + environment: environment, + } + + resource, err := otelMetrics.newResource() + if err != nil { + return nil, errors.WrapIf(err, "failed to create resource") + } + + appMetrics, err := otelMetrics.initMetrics(resource) + if err != nil { + return nil, err + } + + otelMetrics.appMetrics = appMetrics + + return otelMetrics, nil +} + +func (o *OtelMetrics) Shutdown(ctx context.Context) error { + return o.provider.Shutdown(ctx) +} + +func (o *OtelMetrics) newResource() (*resource.Resource, error) { + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-traces/main.go#L49C1-L56C5 + resource, err := resource.New( + context.Background(), + resource.WithFromEnv(), + resource.WithTelemetrySDK(), + resource.WithHost(), + resource.WithOS(), + resource.WithSchemaURL(semconv.SchemaURL), + resource.WithAttributes( + semconv.ServiceName(o.config.ServiceName), + semconv.ServiceVersion(o.config.Version), + attribute.String("environment", o.environment.GetEnvironmentName()), + semconv.TelemetrySDKVersionKey.String("v1.21.0"), // semconv version + semconv.TelemetrySDKLanguageGo, + )) - // The exporter embeds a default OpenTelemetry Reader and - // implements prometheus.Collector, allowing it to be used as - // both a Reader and Collector. - exporter, err := prometheus.New() + return resource, err +} + +func (o *OtelMetrics) initMetrics( + resource *resource.Resource, +) (AppMetrics, error) { + metricsExporter, err := o.configExporters() if err != nil { - logger.Fatal(err) + return nil, err } - provider := metric.NewMeterProvider(metric.WithReader(exporter)) - meter := provider.Meter( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics", + + batchExporters := lo.Map( + metricsExporter, + func(item metric.Reader, index int) metric.Option { + return metric.WithReader(item) + }, ) - return &OtelMetrics{Config: config, Meter: meter, Logger: logger, Echo: e}, nil + // https://opentelemetry.io/docs/instrumentation/go/exporting_data/#resources + // Resources are a special type of attribute that apply to all spans generated by a process + opts := append( + batchExporters, + metric.WithResource(resource), + ) + + // otel library collects metrics and send this metrics to some exporter like console or prometheus + provider := metric.NewMeterProvider(opts...) + + // Register our MeterProvider as the global so any imported + // instrumentation in the future will default to using it. + otel.SetMeterProvider(provider) + o.provider = provider + + appMeter := NewAppMeter(o.config.InstrumentationName) + + return appMeter, nil } -func (o *OtelMetrics) Run() error { - o.Echo.Use(middleware.RecoverWithConfig(middleware.RecoverConfig{ - StackSize: 1 << 10, // 1 KB - DisablePrintStack: true, - DisableStackAll: true, - })) +func (o *OtelMetrics) configExporters() ([]metric.Reader, error) { + ctx := context.Background() - var metricsPath string - if o.Config.OTelMetricsOptions.MetricsRoutePath == "" { - metricsPath = "/metrics" - } else { - metricsPath = o.Config.OTelMetricsOptions.MetricsRoutePath + var exporters []metric.Reader + + // use some otel collector endpoints + metricOpts := []otlpmetricgrpc.Option{ + otlpmetricgrpc.WithTimeout(5 * time.Second), + otlpmetricgrpc.WithInsecure(), } - o.Echo.GET(metricsPath, echo.WrapHandler(promhttp.Handler())) - o.Logger.Infof("serving metrics at localhost:%s/metrics", o.Config.OTelMetricsOptions.Port) - err := o.Echo.Start(o.Config.OTelMetricsOptions.Port) + if !o.config.UseOTLP { //nolint:nestif + + if o.config.UseStdout { + console, err := stdoutmetric.New() + if err != nil { + return nil, errors.WrapIf( + err, + "error creating console exporter", + ) + } + + consoleMetricExporter := metric.NewPeriodicReader( + console, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second)) + + exporters = append(exporters, consoleMetricExporter) + } + + if o.config.ElasticApmExporterOptions != nil { + // https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html + // https://www.elastic.co/guide/en/apm/guide/current/open-telemetry-direct.html#instrument-apps-otel + // https://github.com/anilsenay/go-opentelemetry-examples/blob/elastic/cmd/main.go#L35 + metricOpts = append( + metricOpts, + otlpmetricgrpc.WithEndpoint( + o.config.ElasticApmExporterOptions.OTLPEndpoint, + ), + otlpmetricgrpc.WithHeaders( + o.config.ElasticApmExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + exporter, err := otlpmetricgrpc.New(ctx, metricOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create otlpmetric exporter for elastic-apm", + ) + } + + elasticApmExporter := metric.NewPeriodicReader( + exporter, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second)) + + exporters = append(exporters, elasticApmExporter) + } + + if o.config.UptraceExporterOptions != nil { + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-traces/main.go#L49C1-L56C5 + // https://uptrace.dev/get/opentelemetry-go.html#exporting-traces + // https://uptrace.dev/get/opentelemetry-go.html#exporting-metrics + metricOpts = append( + metricOpts, + otlpmetricgrpc.WithEndpoint( + o.config.UptraceExporterOptions.OTLPEndpoint, + ), + otlpmetricgrpc.WithHeaders( + o.config.UptraceExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + exporter, err := otlpmetricgrpc.New(ctx, metricOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create otlpmetric exporter for uptrace", + ) + } + + uptraceExporter := metric.NewPeriodicReader( + exporter, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second)) + + exporters = append(exporters, uptraceExporter) + } + if o.config.SignozExporterOptions != nil { + // https://signoz.io/docs/instrumentation/golang/#instrumentation-of-a-sample-golang-application + // https://signoz.io/blog/distributed-tracing-golang/ + metricOpts = append( + metricOpts, + otlpmetricgrpc.WithEndpoint( + o.config.SignozExporterOptions.OTLPEndpoint, + ), + otlpmetricgrpc.WithHeaders( + o.config.SignozExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + exporter, err := otlpmetricgrpc.New(ctx, metricOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create otlpmetric exporter for signoz", + ) + } - return err + signozExporter := metric.NewPeriodicReader( + exporter, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second)) + + exporters = append(exporters, signozExporter) + } else { + // https://prometheus.io/docs/prometheus/latest/getting_started/ + // https://prometheus.io/docs/guides/go-application/ + // prometheus exporter will collect otel metrics in prometheus registry + // all prometheus exporters will add to a singleton `prometheus.DefaultRegisterer` registry in newConfig method and this registry will use via `promhttp.Handler` through http endpoint on `/metrics` and calls `Collect` on prometheus Reader interface inner signature prometheus.DefaultRegisterer + prometheusExporter, err := prometheus.New() + if err != nil { + return nil, errors.WrapIf( + err, + "error creating prometheus exporter", + ) + } + exporters = append(exporters, prometheusExporter) + } + } else { + for _, oltpProvider := range o.config.OTLPProviders { + if !oltpProvider.Enabled { + continue + } + + metricOpts = append(metricOpts, otlpmetricgrpc.WithEndpoint(oltpProvider.OTLPEndpoint), otlpmetricgrpc.WithHeaders(oltpProvider.OTLPHeaders)) + + // send otel metrics to an otel collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-metrics/main.go#L28 + // https://github.com/open-telemetry/opentelemetry-go/blob/main/exporters/otlp/otlpmetric/otlpmetricgrpc/example_test.go + exporter, err := otlpmetricgrpc.New(ctx, metricOpts...) + if err != nil { + return nil, errors.WrapIf(err, "failed to create otlptracegrpc exporter") + } + metricExporter := metric.NewPeriodicReader( + exporter, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second)) + + exporters = append(exporters, metricExporter) + } + } + + return exporters, nil } -func (o *OtelMetrics) GracefulShutdown(ctx context.Context) error { - err := o.Echo.Shutdown(ctx) - if err != nil { - return err +// we could also use our existing server app port and a new /metrics endpoint instead of a new server with different port for our app metrics + +func (o *OtelMetrics) RegisterMetricsEndpoint( + server contracts.EchoHttpServer, +) { + if o.config.UseOTLP { + return + } + + var metricsPath string + if o.config.MetricsRoutePath == "" { + metricsPath = "metrics" + } else { + metricsPath = o.config.MetricsRoutePath } - return nil + // when we send request to /metrics endpoint, this handler gets singleton `prometheus.DefaultRegisterer` registry with calling `Collect` method on registered prometheus reader and get all get metrics and write them in /metrics endpoint output + server.GetEchoInstance(). + GET(metricsPath, echo.WrapHandler(promhttp.Handler())) } diff --git a/internal/pkg/otel/metrics/metrics_fx.go b/internal/pkg/otel/metrics/metrics_fx.go new file mode 100644 index 00000000..0a07fc69 --- /dev/null +++ b/internal/pkg/otel/metrics/metrics_fx.go @@ -0,0 +1,92 @@ +package metrics + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + "go.opentelemetry.io/contrib/instrumentation/host" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" + "go.uber.org/fx" +) + +var ( + // Module provided to fxlog + // https://uber-go.github.io/fx/modules.html + Module = fx.Module( //nolint:gochecknoglobals + "otelmetrixfx", + metricsProviders, + metricsInvokes, + ) + + metricsProviders = fx.Options(fx.Provide( //nolint:gochecknoglobals + ProvideMetricsConfig, + NewOtelMetrics, + fx.Annotate( + provideMeter, + fx.ParamTags(`optional:"true"`), + fx.As(new(AppMetrics)), + fx.As(new(metric.Meter))), + )) + + metricsInvokes = fx.Options( //nolint:gochecknoglobals + fx.Invoke(registerHooks), + fx.Invoke(func(m *OtelMetrics, server contracts.EchoHttpServer) { + m.RegisterMetricsEndpoint(server) + }), + ) +) + +func provideMeter(otelMetrics *OtelMetrics) AppMetrics { + return otelMetrics.appMetrics +} + +// we don't want to register any dependencies here, its func body should execute always even we don't request for that, so we should use `invoke` +func registerHooks( + lc fx.Lifecycle, + metrics *OtelMetrics, + logger logger.Logger, +) { + lc.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + if metrics.appMetrics == nil { + return nil + } + + if metrics.config.EnableHostMetrics { + // https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/host + // we changed default meter provider in metrics setup + logger.Info("Starting host instrumentation:") + err := host.Start( + host.WithMeterProvider(otel.GetMeterProvider()), + ) + if err != nil { + logger.Errorf( + "error starting host instrumentation: %s", + err, + ) + } + } + + return nil + }, + OnStop: func(ctx context.Context) error { + if metrics.appMetrics == nil { + return nil + } + + if err := metrics.Shutdown(ctx); err != nil { + logger.Errorf( + "error in shutting down metrics provider: %v", + err, + ) + } else { + logger.Info("metrics provider shutdown gracefully") + } + + return nil + }, + }) +} diff --git a/internal/pkg/otel/metrics/metrics_options.go b/internal/pkg/otel/metrics/metrics_options.go new file mode 100644 index 00000000..5ead8960 --- /dev/null +++ b/internal/pkg/otel/metrics/metrics_options.go @@ -0,0 +1,42 @@ +package metrics + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/iancoleman/strcase" +) + +type OTLPProvider struct { + Name string `mapstructure:"name"` + Enabled bool `mapstructure:"enabled"` + OTLPEndpoint string `mapstructure:"otlpEndpoint"` + OTLPHeaders map[string]string `mapstructure:"otlpHeaders"` +} + +type MetricsOptions struct { + Host string `mapstructure:"host"` + Port string `mapstructure:"port"` + ServiceName string `mapstructure:"serviceName"` + Version string `mapstructure:"version"` + MetricsRoutePath string `mapstructure:"metricsRoutePath"` + EnableHostMetrics bool `mapstructure:"enableHostMetrics"` + UseStdout bool `mapstructure:"useStdout"` + InstrumentationName string `mapstructure:"instrumentationName"` + UseOTLP bool `mapstructure:"useOTLP"` + OTLPProviders []OTLPProvider `mapstructure:"otlpProviders"` + ElasticApmExporterOptions *OTLPProvider `mapstructure:"elasticApmExporterOptions"` + UptraceExporterOptions *OTLPProvider `mapstructure:"uptraceExporterOptions"` + SignozExporterOptions *OTLPProvider `mapstructure:"signozExporterOptions"` +} + +func ProvideMetricsConfig( + environment environment.Environment, +) (*MetricsOptions, error) { + optionName := strcase.ToLowerCamel( + typeMapper.GetGenericTypeNameByT[MetricsOptions](), + ) + + return config.BindConfigKey[*MetricsOptions](optionName, environment) +} diff --git a/internal/pkg/otel/metrics/metrics_test.go b/internal/pkg/otel/metrics/metrics_test.go new file mode 100644 index 00000000..89aea69f --- /dev/null +++ b/internal/pkg/otel/metrics/metrics_test.go @@ -0,0 +1,69 @@ +package metrics_test + +import ( + "fmt" + "io" + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestHealth(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "/health suite") +} + +var _ = Describe("/", Ordered, func() { + var ( + url string + err error + res *http.Response + ) + + BeforeAll(func() { + var cfg *metrics.MetricsOptions + + fxtest.New( + GinkgoT(), + zap.Module, + fxlog.FxLogger, + config.Module, + customEcho.Module, + + metrics.Module, + + fx.Populate(&cfg), + ).RequireStart() + + url = fmt.Sprintf("http://%s:%s/metrics", cfg.Host, cfg.Port) + }) + + BeforeEach(func() { + res, err = http.Get(url) + }) + It("returns status OK", func() { + Expect(err).To(BeNil()) + Expect(res.StatusCode).To(Equal(http.StatusOK)) + }) + + It("returns how many requests were made", func() { + b, err := io.ReadAll(res.Body) + Expect(err).To(BeNil()) + + Expect( + b, + ).To(ContainSubstring(`promhttp_metric_handler_requests_total{code="200"} 1`)) + }) +}) diff --git a/internal/pkg/otel/otel_fx.go b/internal/pkg/otel/otel_fx.go deleted file mode 100644 index 52b2295c..00000000 --- a/internal/pkg/otel/otel_fx.go +++ /dev/null @@ -1,107 +0,0 @@ -package otel - -import ( - "context" - "net/http" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - - "emperror.dev/errors" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/trace" - "go.uber.org/fx" -) - -var ( - // Module provided to fxlog - // https://uber-go.github.io/fx/modules.html - Module = fx.Module( //nolint:gochecknoglobals - "otelfx", - otelProviders, - otelInvokes, - ) - - otelProviders = fx.Options(fx.Provide( //nolint:gochecknoglobals - config.ProvideOtelConfig, - metrics.NewOtelMetrics, - tracing.NewOtelTracing, - fx.Annotate( - provideMeter, - fx.As(new(metric.Meter))), - fx.Annotate( - provideTracer, - fx.As(new(tracing.AppTracer)), - fx.As(new(trace.Tracer)), - ), - )) - - otelInvokes = fx.Options(fx.Invoke(registerHooks)) //nolint:gochecknoglobals -) - -func provideMeter(otelMetrics *metrics.OtelMetrics) metric.Meter { - return otelMetrics.Meter -} - -func provideTracer(tracingOtel *tracing.TracingOpenTelemetry) tracing.AppTracer { - return tracingOtel.AppTracer -} - -// we don't want to register any dependencies here, its func body should execute always even we don't request for that, so we should use `invoke` -func registerHooks( - lc fx.Lifecycle, - metrics *metrics.OtelMetrics, - logger logger.Logger, - tracingOtel *tracing.TracingOpenTelemetry, -) { - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - if metrics.Meter == nil { - return nil - } - - go func() { - // https://medium.com/@mokiat/proper-http-shutdown-in-go-bd3bfaade0f2 - // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn’t exit and waits instead for Shutdown to return. - if err := metrics.Run(); !errors.Is(err, http.ErrServerClosed) { - // do a fatal for running OnStop hook - logger.Fatalf( - "(metrics.RunHttpServer) error in running metrics server: {%v}", - err, - ) - } - }() - logger.Infof( - "Metrics server %s is listening on Host:{%s} Http PORT: {%s}", - metrics.Config.OTelMetricsOptions.Name, - metrics.Config.OTelMetricsOptions.Host, - metrics.Config.OTelMetricsOptions.Port, - ) - - return nil - }, - OnStop: func(ctx context.Context) error { - if err := tracingOtel.TracerProvider.Shutdown(ctx); err != nil { - logger.Errorf("error in shutting down trace provider: %v", err) - } else { - logger.Info("trace provider shutdown gracefully") - } - - if metrics.Meter == nil { - return nil - } - // https://github.com/uber-go/fx/blob/v1.20.0/app.go#L573 - // this ctx is just for stopping callbacks or OnStop callbacks, and it has short timeout 15s, and it is not alive in whole lifetime app - // https://medium.com/@mokiat/proper-http-shutdown-in-go-bd3bfaade0f2 - // When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn’t exit and waits instead for Shutdown to return. - if err := metrics.GracefulShutdown(ctx); err != nil { - logger.Errorf("error shutting down metrics server: %v", err) - } else { - logger.Info("metrics server shutdown gracefully") - } - return nil - }, - }) -} diff --git a/internal/pkg/otel/tracing/custom_tracer.go b/internal/pkg/otel/tracing/custom_tracer.go index 3c86d914..d4363e86 100644 --- a/internal/pkg/otel/tracing/custom_tracer.go +++ b/internal/pkg/otel/tracing/custom_tracer.go @@ -3,6 +3,8 @@ package tracing import ( "context" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" ) @@ -22,7 +24,7 @@ func (c *appTracer) Start( ) (context.Context, trace.Span) { parentSpan := trace.SpanFromContext(ctx) if parentSpan != nil { - ContextWithParentSpan(ctx, parentSpan) + utils.ContextWithParentSpan(ctx, parentSpan) } return c.Tracer.Start(ctx, spanName, opts...) diff --git a/internal/pkg/otel/tracing/mediatr/pipelines/config.go b/internal/pkg/otel/tracing/mediatr/pipelines/config.go new file mode 100644 index 00000000..03812b0b --- /dev/null +++ b/internal/pkg/otel/tracing/mediatr/pipelines/config.go @@ -0,0 +1,43 @@ +package pipelines + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" +) + +type config struct { + logger logger.Logger + serviceName string +} + +var defaultConfig = &config{ + serviceName: "app", + logger: defaultLogger.GetLogger(), +} + +// Option specifies instrumentation configuration options. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +func WithServiceName(v string) Option { + return optionFunc(func(cfg *config) { + if cfg.serviceName != "" { + cfg.serviceName = v + } + }) +} + +func WithLogger(l logger.Logger) Option { + return optionFunc(func(cfg *config) { + if cfg.logger != nil { + cfg.logger = l + } + }) +} diff --git a/internal/pkg/otel/tracing/mediatr/pipelines/mediator_tracing_pipeline.go b/internal/pkg/otel/tracing/mediatr/pipelines/mediator_tracing_pipeline.go new file mode 100644 index 00000000..f5ac39a7 --- /dev/null +++ b/internal/pkg/otel/tracing/mediatr/pipelines/mediator_tracing_pipeline.go @@ -0,0 +1,105 @@ +package pipelines + +import ( + "context" + "fmt" + "strings" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants/telemetrytags" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants/tracing/components" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + customAttribute "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/mehdihadeli/go-mediatr" + "go.opentelemetry.io/otel/attribute" +) + +type mediatorTracingPipeline struct { + config *config + tracer tracing.AppTracer +} + +func NewMediatorTracingPipeline( + appTracer tracing.AppTracer, + opts ...Option, +) mediatr.PipelineBehavior { + cfg := defaultConfig + for _, opt := range opts { + opt.apply(cfg) + } + + return &mediatorTracingPipeline{ + config: cfg, + tracer: appTracer, + } +} + +func (r *mediatorTracingPipeline) Handle( + ctx context.Context, + request interface{}, + next mediatr.RequestHandlerFunc, +) (interface{}, error) { + requestName := typeMapper.GetSnakeTypeName(request) + + componentName := components.RequestHandler + requestNameTag := telemetrytags.App.RequestName + requestTag := telemetrytags.App.Request + requestResultNameTag := telemetrytags.App.RequestResultName + requestResultTag := telemetrytags.App.RequestResult + + switch { + case strings.Contains(typeMapper.GetPackageName(request), "command") || strings.Contains(typeMapper.GetPackageName(request), "commands"): + componentName = components.CommandHandler + requestNameTag = telemetrytags.App.CommandName + requestTag = telemetrytags.App.Command + requestResultNameTag = telemetrytags.App.CommandResultName + requestResultTag = telemetrytags.App.CommandResult + case strings.Contains(typeMapper.GetPackageName(request), "query") || strings.Contains(typeMapper.GetPackageName(request), "queries"): + componentName = components.QueryHandler + requestNameTag = telemetrytags.App.QueryName + requestTag = telemetrytags.App.Query + requestResultNameTag = telemetrytags.App.QueryResultName + requestResultTag = telemetrytags.App.QueryResult + case strings.Contains(typeMapper.GetPackageName(request), "event") || strings.Contains(typeMapper.GetPackageName(request), "events"): + componentName = components.EventHandler + requestNameTag = telemetrytags.App.EventName + requestTag = telemetrytags.App.Event + requestResultNameTag = telemetrytags.App.EventResultName + requestResultTag = telemetrytags.App.EventResult + } + + operationName := fmt.Sprintf("%s_handler", requestName) + spanName := fmt.Sprintf( + "%s.%s/%s", + componentName, + operationName, + requestName, + ) // by convention + + // https://golang.org/pkg/context/#Context + newCtx, span := r.tracer.Start(ctx, spanName) + + defer span.End() + + span.SetAttributes( + attribute.String(requestNameTag, requestName), + customAttribute.Object(requestTag, request), + ) + + response, err := next(newCtx) + + responseName := typeMapper.GetSnakeTypeName(response) + span.SetAttributes( + attribute.String(requestResultNameTag, responseName), + customAttribute.Object(requestResultTag, response), + ) + + err = utils.TraceStatusFromSpan( + span, + err, + ) + + return response, err +} diff --git a/internal/pkg/otel/tracing/tracing.go b/internal/pkg/otel/tracing/tracing.go index 3bd46d39..d93521e3 100644 --- a/internal/pkg/otel/tracing/tracing.go +++ b/internal/pkg/otel/tracing/tracing.go @@ -1,91 +1,103 @@ package tracing +// https://opentelemetry.io/docs/reference/specification/ +// https://opentelemetry.io/docs/instrumentation/go/getting-started/ +// https://opentelemetry.io/docs/instrumentation/go/manual/ +// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/ +// https://uptrace.dev/opentelemetry/go-tracing.html +// https://lightstep.com/blog/opentelemetry-go-all-you-need-to-know +// https://trstringer.com/otel-part2-instrumentation/ +// https://trstringer.com/otel-part5-propagation/ +// https://github.com/tedsuo/otel-go-basics/blob/main/server.go +// https://github.com/riferrei/otel-with-golang/blob/main/main.go + import ( - "fmt" - "log" + "context" "os" + "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "emperror.dev/errors" + "github.com/samber/lo" "go.opentelemetry.io/contrib/propagators/ot" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/exporters/zipkin" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.21.0" - - _ "go.opentelemetry.io/otel" ) -// https://opentelemetry.io/docs/reference/specification/ -// https://opentelemetry.io/docs/instrumentation/go/getting-started/ -// https://opentelemetry.io/docs/instrumentation/go/manual/ -// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/ -// https://uptrace.dev/opentelemetry/go-tracing.html -// https://lightstep.com/blog/opentelemetry-go-all-you-need-to-know -// https://trstringer.com/otel-part2-instrumentation/ -// https://trstringer.com/otel-part5-propagation/ -// https://github.com/tedsuo/otel-go-basics/blob/main/server.go - type TracingOpenTelemetry struct { - config *config2.OpenTelemetryOptions - jaegerExporter tracesdk.SpanExporter - zipkinExporter tracesdk.SpanExporter - stdExporter tracesdk.SpanExporter - environment environemnt.Environment - TracerProvider *tracesdk.TracerProvider - AppTracer AppTracer + config *TracingOptions + environment environment.Environment + appTracer AppTracer + provider *tracesdk.TracerProvider } // Create one tracer per package // NOTE: You only need a tracer if you are creating your own spans func NewOtelTracing( - config *config2.OpenTelemetryOptions, - environment environemnt.Environment, + config *TracingOptions, + environment environment.Environment, ) (*TracingOpenTelemetry, error) { - openTel := &TracingOpenTelemetry{config: config, environment: environment} + otelTracing := &TracingOpenTelemetry{ + config: config, + environment: environment, + } - err := openTel.configExporters() + resource, err := otelTracing.newResource() if err != nil { - return nil, errors.WrapIf(err, "error in config exporter") + return nil, errors.WrapIf(err, "failed to create resource") } - // https://opentelemetry.io/docs/instrumentation/go/manual/#initializing-a-new-tracer - err = openTel.configTracerProvider() + appTracer, err := otelTracing.initTracer(resource) if err != nil { return nil, err } - // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/propagators/ot/ot_propagator.go - // https://github.com/open-telemetry/opentelemetry-go/blob/main/propagation/trace_context.go - // https://github.com/open-telemetry/opentelemetry-go/blob/main/propagation/baggage.go/ - // https://trstringer.com/otel-part5-propagation/ - propagators := []propagation.TextMapPropagator{ - ot.OT{}, // should be placed before `TraceContext` for preventing conflict - propagation.TraceContext{}, - propagation.Baggage{}, - } + otelTracing.appTracer = appTracer - // Register our TracerProvider as the global so any imported - // instrumentation in the future will default to using it. - otel.SetTracerProvider(openTel.TracerProvider) - otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagators...)) + return otelTracing, nil +} - // https://trstringer.com/otel-part2-instrumentation/ - // Finally, set the tracer that can be used for this package. global app tracer - openTel.AppTracer = NewAppTracer(config.InstrumentationName) +func (o *TracingOpenTelemetry) Shutdown(ctx context.Context) error { + return o.provider.Shutdown(ctx) +} + +func (o *TracingOpenTelemetry) newResource() (*resource.Resource, error) { + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-traces/main.go#L49C1-L56C5 + resource, err := resource.New(context.Background(), + resource.WithFromEnv(), + resource.WithTelemetrySDK(), + resource.WithHost(), + resource.WithOS(), + resource.WithSchemaURL(semconv.SchemaURL), + resource.WithAttributes( + semconv.ServiceName(o.config.ServiceName), + semconv.ServiceVersion(o.config.Version), + attribute.Int64("ID", o.config.Id), + attribute.String("environment", o.environment.GetEnvironmentName()), + semconv.TelemetrySDKVersionKey.String("v1.21.0"), // semconv version + semconv.TelemetrySDKLanguageGo, + )) - return openTel, nil + return resource, err } -func (o *TracingOpenTelemetry) configTracerProvider() error { +func (o *TracingOpenTelemetry) initTracer( + resource *resource.Resource, +) (AppTracer, error) { + exporters, err := o.configExporters() + if err != nil { + return nil, err + } + var sampler tracesdk.Sampler if o.config.AlwaysOnSampler { sampler = tracesdk.AlwaysSample() @@ -93,68 +105,246 @@ func (o *TracingOpenTelemetry) configTracerProvider() error { sampler = tracesdk.NeverSample() } - // https://github.com/open-telemetry/opentelemetry-go/blob/main/example/fib/main.go#L44 - // Ensure default SDK resources and the required service name are set. - r, err := resource.Merge( - resource.Default(), - resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceNameKey.String(o.config.ServiceName), - attribute.Int64("ID", o.config.Id), - attribute.String("environment", o.environment.GetEnvironmentName()), - ), + batchExporters := lo.Map( + exporters, + func(item tracesdk.SpanExporter, index int) tracesdk.TracerProviderOption { + return tracesdk.WithBatcher(item) + }, ) - if err != nil { - return err - } - tp := tracesdk.NewTracerProvider( - // Always be sure to batch in production. - tracesdk.WithBatcher(o.jaegerExporter), - tracesdk.WithBatcher(o.zipkinExporter), - tracesdk.WithBatcher(o.stdExporter), + // https://opentelemetry.io/docs/instrumentation/go/exporting_data/#resources + // Resources are a special type of attribute that apply to all spans generated by a process + opts := append( + batchExporters, + tracesdk.WithResource(resource), tracesdk.WithSampler(sampler), + ) + + provider := tracesdk.NewTracerProvider(opts...) + + // Register our tracerProvider as the global so any imported + // instrumentation in the future will default to using it. + otel.SetTracerProvider(provider) + o.provider = provider - // https://opentelemetry.io/docs/instrumentation/go/exporting_data/#resources - // Resources are a special type of attribute that apply to all spans generated by a process - tracesdk.WithResource(r), + // https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/propagators/ot/ot_propagator.go + // https://github.com/open-telemetry/opentelemetry-go/blob/main/propagation/trace_context.go + // https://github.com/open-telemetry/opentelemetry-go/blob/main/propagation/baggage.go/ + // https://trstringer.com/otel-part5-propagation/ + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + ot.OT{}, // should be placed before `TraceContext` for preventing conflict + propagation.Baggage{}, + propagation.TraceContext{}, + ), ) - o.TracerProvider = tp - return nil + + // https://trstringer.com/otel-part2-instrumentation/ + // Finally, set the tracer that can be used for this package. global app tracer + appTracer := NewAppTracer(o.config.InstrumentationName) + + return appTracer, nil } -func (o *TracingOpenTelemetry) configExporters() error { - logger := log.New(os.Stderr, "otel_log", log.Ldate|log.Ltime|log.Llongfile) +func (o *TracingOpenTelemetry) configExporters() ([]tracesdk.SpanExporter, error) { + ctx := context.Background() + traceOpts := []otlptracegrpc.Option{ + otlptracegrpc.WithTimeout(5 * time.Second), + otlptracegrpc.WithInsecure(), + } - if o.config.JaegerExporterOptions != nil { - // Create the Jaeger exporter - jaegerExporter, err := jaeger.New(jaeger.WithAgentEndpoint( - jaeger.WithAgentHost(o.config.JaegerExporterOptions.AgentHost), - jaeger.WithAgentPort(o.config.JaegerExporterOptions.AgentPort), - jaeger.WithLogger(logger), - )) - if err != nil { - return err + var exporters []tracesdk.SpanExporter + + if !o.config.UseOTLP { //nolint:nestif + + // jaeger exporter removed from otel spec (it used jaeger agent and jaeger agent port), now we should use OTLP which supports by jaeger now by its built-in `collector` + // https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c + // https://www.jaegertracing.io/docs/1.38/apis/#opentelemetry-protocol-stable + // https://deploy-preview-1892--opentelemetry.netlify.app/blog/2022/jaeger-native-otlp/ + // https://www.jaegertracing.io/docs/1.49/getting-started/ + // https://opentelemetry.io/docs/instrumentation/go/exporters/ + // https://opentelemetry.io/docs/specs/otlp/ + // https://github.com/open-telemetry/opentelemetry-go/pull/4467 + if o.config.JaegerExporterOptions != nil { + traceOpts = append( + traceOpts, + otlptracegrpc.WithEndpoint( + o.config.JaegerExporterOptions.OTLPEndpoint, + ), + otlptracegrpc.WithHeaders( + o.config.JaegerExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + jaegerTraceExporter, err := otlptracegrpc.New(ctx, traceOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create oltptrace exporter for jaeger", + ) + } + + exporters = append(exporters, jaegerTraceExporter) } - o.jaegerExporter = jaegerExporter - } - if o.config.ZipkinExporterOptions != nil { - zipkinExporter, err := zipkin.New( - o.config.ZipkinExporterOptions.Url, - ) - if err != nil { - return err + + // https://medium.com/adevinta-tech-blog/distributed-tracing-with-opentelemetry-in-your-go-python-microservices-1782cd0a1e77 + // https://grafana.com/docs/tempo/latest/getting-started/ + if o.config.TempoExporterOptions != nil { + traceOpts = append( + traceOpts, + otlptracegrpc.WithEndpoint( + o.config.TempoExporterOptions.OTLPEndpoint, + ), + otlptracegrpc.WithHeaders( + o.config.TempoExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + grafanaTempoTraceExporter, err := otlptracegrpc.New( + ctx, + traceOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create oltptrace exporter for grafana-tempo", + ) + } + + exporters = append(exporters, grafanaTempoTraceExporter) } - o.zipkinExporter = zipkinExporter - } - if o.config.UseStdout { - stdExporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) - if err != nil { - return fmt.Errorf("creating stdout exporter: %w", err) + if o.config.ZipkinExporterOptions != nil { + zipkinExporter, err := zipkin.New( + o.config.ZipkinExporterOptions.Url, + ) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create exporter for zipkin", + ) + } + + exporters = append(exporters, zipkinExporter) + } + + if o.config.ElasticApmExporterOptions != nil { + // https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html + // https://www.elastic.co/guide/en/apm/guide/current/open-telemetry-direct.html#instrument-apps-otel + // https://github.com/anilsenay/go-opentelemetry-examples/blob/elastic/cmd/main.go#L35 + traceOpts = append( + traceOpts, + otlptracegrpc.WithEndpoint( + o.config.ElasticApmExporterOptions.OTLPEndpoint, + ), + otlptracegrpc.WithHeaders( + o.config.ElasticApmExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + elasticApmExporter, err := otlptracegrpc.New(ctx, traceOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create oltptrace exporter for elastic-apm", + ) + } + + exporters = append(exporters, elasticApmExporter) + } + + if o.config.UptraceExporterOptions != nil { + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-traces/main.go#L49C1-L56C5 + // https://uptrace.dev/get/opentelemetry-go.html#exporting-traces + // https://uptrace.dev/get/opentelemetry-go.html#exporting-metrics + traceOpts = append( + traceOpts, + otlptracegrpc.WithEndpoint( + o.config.UptraceExporterOptions.OTLPEndpoint, + ), + otlptracegrpc.WithHeaders( + o.config.UptraceExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + uptraceExporter, err := otlptracegrpc.New(ctx, traceOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create oltptrace exporter for uptrace", + ) + } + + exporters = append(exporters, uptraceExporter) + } + + if o.config.SignozExporterOptions != nil { + // https://signoz.io/docs/instrumentation/golang/#instrumentation-of-a-sample-golang-application + // https://signoz.io/blog/distributed-tracing-golang/ + traceOpts = append( + traceOpts, + otlptracegrpc.WithEndpoint( + o.config.SignozExporterOptions.OTLPEndpoint, + ), + otlptracegrpc.WithHeaders( + o.config.SignozExporterOptions.OTLPHeaders, + ), + ) + + // send otel traces to jaeger builtin collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + signozExporter, err := otlptracegrpc.New(ctx, traceOpts...) + if err != nil { + return nil, errors.WrapIf( + err, + "failed to create oltptrace exporter for signoz", + ) + } + + exporters = append(exporters, signozExporter) + } + + if o.config.UseStdout { + stdExporter, err := stdouttrace.New( + stdouttrace.WithWriter( + os.Stdout, + ), // stdExporter default is `stdouttrace.WithWriter(os.Stdout)`, we can remove this also + stdouttrace.WithPrettyPrint(), // make output json with pretty printing + ) + if err != nil { + return nil, errors.WrapIf(err, "creating stdout exporter") + } + + exporters = append(exporters, stdExporter) + } + } else { + // use some otel collector endpoints + for _, oltpProvider := range o.config.OTLPProviders { + if !oltpProvider.Enabled { + continue + } + + traceOpts = append(traceOpts, otlptracegrpc.WithEndpoint(oltpProvider.OTLPEndpoint), otlptracegrpc.WithHeaders(oltpProvider.OTLPHeaders)) + + // send otel metrics to an otel collector endpoint (default grpc port: 4317) + // https://opentelemetry.io/docs/collector/ + // https://github.com/uptrace/uptrace-go/blob/master/example/otlp-traces/main.go#L29 + // https://github.com/open-telemetry/opentelemetry-go/blob/main/exporters/otlp/otlptrace/otlptracehttp/example_test.go#L70 + traceExporter, err := otlptracegrpc.New(ctx, traceOpts...) + if err != nil { + return nil, errors.WrapIf(err, "failed to create otlptracegrpc exporter") + } + + exporters = append(exporters, traceExporter) } - o.stdExporter = stdExporter } - return nil + return exporters, nil } diff --git a/internal/pkg/otel/tracing/tracing_fx.go b/internal/pkg/otel/tracing/tracing_fx.go new file mode 100644 index 00000000..fdaf9bb6 --- /dev/null +++ b/internal/pkg/otel/tracing/tracing_fx.go @@ -0,0 +1,60 @@ +package tracing + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + "go.opentelemetry.io/otel/trace" + "go.uber.org/fx" +) + +var ( + // Module provided to fxlog + // https://uber-go.github.io/fx/modules.html + Module = fx.Module( //nolint:gochecknoglobals + "oteltracingfx", + tracingProviders, + tracingInvokes, + ) + + tracingProviders = fx.Options(fx.Provide( //nolint:gochecknoglobals + ProvideTracingConfig, + NewOtelTracing, + fx.Annotate( + provideTracer, + fx.ParamTags(`optional:"true"`), + fx.As(new(AppTracer)), + fx.As(new(trace.Tracer)), + ), + )) + + tracingInvokes = fx.Options( + fx.Invoke(registerHooks), + ) //nolint:gochecknoglobals +) + +func provideTracer( + tracingOtel *TracingOpenTelemetry, +) AppTracer { + return tracingOtel.appTracer +} + +// we don't want to register any dependencies here, its func body should execute always even we don't request for that, so we should use `invoke` +func registerHooks( + lc fx.Lifecycle, + logger logger.Logger, + tracingOtel *TracingOpenTelemetry, +) { + lc.Append(fx.Hook{ + OnStop: func(ctx context.Context) error { + if err := tracingOtel.Shutdown(ctx); err != nil { + logger.Errorf("error in shutting down trace provider: %v", err) + } else { + logger.Info("trace provider shutdown gracefully") + } + + return nil + }, + }) +} diff --git a/internal/pkg/otel/tracing/tracing_options.go b/internal/pkg/otel/tracing/tracing_options.go new file mode 100644 index 00000000..4bd699b9 --- /dev/null +++ b/internal/pkg/otel/tracing/tracing_options.go @@ -0,0 +1,48 @@ +package tracing + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/iancoleman/strcase" +) + +type OTLPProvider struct { + Name string `mapstructure:"name"` + Enabled bool `mapstructure:"enabled"` + OTLPEndpoint string `mapstructure:"otlpEndpoint"` + OTLPHeaders map[string]string `mapstructure:"otlpHeaders"` +} + +type TracingOptions struct { + Enabled bool `mapstructure:"enabled"` + ServiceName string `mapstructure:"serviceName"` + Version string `mapstructure:"version"` + InstrumentationName string `mapstructure:"instrumentationName"` + Id int64 `mapstructure:"id"` + AlwaysOnSampler bool `mapstructure:"alwaysOnSampler"` + ZipkinExporterOptions *ZipkinExporterOptions `mapstructure:"zipkinExporterOptions"` + JaegerExporterOptions *OTLPProvider `mapstructure:"jaegerExporterOptions"` + ElasticApmExporterOptions *OTLPProvider `mapstructure:"elasticApmExporterOptions"` + UptraceExporterOptions *OTLPProvider `mapstructure:"uptraceExporterOptions"` + SignozExporterOptions *OTLPProvider `mapstructure:"signozExporterOptions"` + TempoExporterOptions *OTLPProvider `mapstructure:"tempoExporterOptions"` + UseStdout bool `mapstructure:"useStdout"` + UseOTLP bool `mapstructure:"useOTLP"` + OTLPProviders []OTLPProvider `mapstructure:"otlpProviders"` +} + +type ZipkinExporterOptions struct { + Url string `mapstructure:"url"` +} + +func ProvideTracingConfig( + environment environment.Environment, +) (*TracingOptions, error) { + optionName := strcase.ToLowerCamel( + typeMapper.GetGenericTypeNameByT[TracingOptions](), + ) + + return config.BindConfigKey[*TracingOptions](optionName, environment) +} diff --git a/internal/pkg/otel/tracing/utils.go b/internal/pkg/otel/tracing/utils.go deleted file mode 100644 index 6ca589e7..00000000 --- a/internal/pkg/otel/tracing/utils.go +++ /dev/null @@ -1,159 +0,0 @@ -package tracing - -import ( - "context" - "reflect" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" - - "github.com/ahmetb/go-linq/v3" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - trace2 "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/trace" -) - -type traceContextKeyType int - -const parentSpanKey traceContextKeyType = iota + 1 - -func TraceErrFromContext(ctx context.Context, err error) error { - // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors - span := trace.SpanFromContext(ctx) - defer span.End() - - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(ErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} - -func TraceErrFromSpan(span trace.Span, err error) error { - if err != nil { - stackTraceError := errorUtils.ErrorsWithStack(err) - span.SetStatus(codes.Error, "") - span.SetAttributes(attribute.String(ErrorMessage, stackTraceError)) - span.RecordError(err) - } - - return err -} - -func GetParentSpanContext(span trace.Span) trace.SpanContext { - readWriteSpan, ok := span.(trace2.ReadWriteSpan) - if !ok { - return *new(trace.SpanContext) - } - - return readWriteSpan.Parent() -} - -func ContextWithParentSpan(parent context.Context, span trace.Span) context.Context { - return context.WithValue(parent, parentSpanKey, span) -} - -func ParentSpanFromContext(ctx context.Context) trace.Span { - _, nopSpan := trace.NewNoopTracerProvider().Tracer("").Start(ctx, "") - if ctx == nil { - return nopSpan - } - if span, ok := ctx.Value(parentSpanKey).(trace.Span); ok { - return span - } - return nopSpan -} - -func CopyFromParentSpanAttribute( - ctx context.Context, - span trace.Span, - attributeName string, - parentAttributeName string, -) { - parentAtt := GetParentSpanAttribute(ctx, parentAttributeName) - if reflect.ValueOf(parentAtt).IsZero() { - return - } - span.SetAttributes(attribute.String(attributeName, parentAtt.Value.AsString())) -} - -func CopyFromParentSpanAttributeIfNotSet( - ctx context.Context, - span trace.Span, - attributeName string, - attributeValue string, - parentAttributeName string, -) { - if attributeValue != "" { - span.SetAttributes(attribute.String(attributeName, attributeValue)) - return - } - CopyFromParentSpanAttribute(ctx, span, attributeName, parentAttributeName) -} - -func GetParentSpanAttribute(ctx context.Context, parentAttributeName string) attribute.KeyValue { - parentSpan := ParentSpanFromContext(ctx) - readWriteSpan, ok := parentSpan.(trace2.ReadWriteSpan) - if !ok { - return *new(attribute.KeyValue) - } - att := linq.From(readWriteSpan.Attributes()). - FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == parentAttributeName }) - - return att.(attribute.KeyValue) -} - -func GetSpanAttributeFromCurrentContext(ctx context.Context, attributeName string) attribute.KeyValue { - span := trace.SpanFromContext(ctx) - readWriteSpan, ok := span.(trace2.ReadWriteSpan) - if !ok { - return *new(attribute.KeyValue) - } - att := linq.From(readWriteSpan.Attributes()). - FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == attributeName }) - - return att.(attribute.KeyValue) -} - -func GetSpanAttribute(span trace.Span, attributeName string) attribute.KeyValue { - readWriteSpan, ok := span.(trace2.ReadWriteSpan) - if !ok { - return *new(attribute.KeyValue) - } - att := linq.From(readWriteSpan.Attributes()). - FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == attributeName }) - - return att.(attribute.KeyValue) -} - -func MapsToAttributes(maps map[string]interface{}) []attribute.KeyValue { - var att []attribute.KeyValue - for key, val := range maps { - switch val.(type) { - case string: - att = append(att, attribute.String(key, val.(string))) - case int64: - att = append(att, attribute.Int64(key, val.(int64))) - case int, int32: - att = append(att, attribute.Int(key, val.(int))) - case float64, float32: - att = append(att, attribute.Float64(key, val.(float64))) - case bool: - att = append(att, attribute.Bool(key, val.(bool))) - } - } - - return att -} - -func MetadataToSet(meta metadata.Metadata) attribute.Set { - var keyValue []attribute.KeyValue - for key, val := range meta { - keyValue = append(keyValue, attribute.String(key, val.(string))) - } - return attribute.NewSet(keyValue...) -} diff --git a/internal/pkg/otel/tracing/utils/utils.go b/internal/pkg/otel/tracing/utils/utils.go new file mode 100644 index 00000000..0d5acd15 --- /dev/null +++ b/internal/pkg/otel/tracing/utils/utils.go @@ -0,0 +1,365 @@ +package utils + +import ( + "context" + "net/http" + "reflect" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc/grpcerrors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + problemdetails "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/problemdetails" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/constants/telemetrytags" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" + + "github.com/ahmetb/go-linq/v3" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + trace2 "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/semconv/v1.20.0/httpconv" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" +) + +type traceContextKeyType int + +const parentSpanKey traceContextKeyType = iota + 1 + +// HttpTraceStatusFromSpan create an error span if we have an error and a successful span when error is nil +func HttpTraceStatusFromSpan(span trace.Span, err error) error { + isError := err != nil + + if customErrors.IsCustomError(err) { + httpError := problemdetails.ParseError(err) + + return HttpTraceStatusFromSpanWithCode( + span, + err, + httpError.GetStatus(), + ) + } + + var ( + status int + code codes.Code + description = "" + ) + + if isError { + status = http.StatusInternalServerError + code = codes.Error + description = err.Error() + } else { + status = http.StatusOK + code = codes.Ok + } + + span.SetStatus(code, description) + span.SetAttributes( + semconv.HTTPStatusCode(status), + ) + + if isError { + stackTraceError := errorUtils.ErrorsWithStack(err) + + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + span.RecordError(err) + } + + return err +} + +func TraceStatusFromSpan(span trace.Span, err error) error { + isError := err != nil + + var ( + code codes.Code + description = "" + ) + + if isError { + code = codes.Error + description = err.Error() + } else { + code = codes.Ok + } + + span.SetStatus(code, description) + + if isError { + stackTraceError := errorUtils.ErrorsWithStack(err) + + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + span.RecordError(err) + } + + return err +} + +func TraceErrStatusFromSpan(span trace.Span, err error) error { + isError := err != nil + + span.SetStatus(codes.Error, err.Error()) + + if isError { + stackTraceError := errorUtils.ErrorsWithStack(err) + + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + span.RecordError(err) + } + + return err +} + +// HttpTraceStatusFromSpanWithCode create an error span with specific status code if we have an error and a successful span when error is nil with a specific status +func HttpTraceStatusFromSpanWithCode( + span trace.Span, + err error, + code int, +) error { + if err != nil { + stackTraceError := errorUtils.ErrorsWithStack(err) + + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + span.RecordError(err) + } + + if code > 0 { + // httpconv doesn't exist in semconv v1.21.0, and it moved to `opentelemetry-go-contrib` pkg + // https://github.com/open-telemetry/opentelemetry-go/pull/4362 + // https://github.com/open-telemetry/opentelemetry-go/issues/4081 + // using ClientStatus instead of ServerStatus for consideration of 4xx status as error + span.SetStatus(httpconv.ClientStatus(code)) + span.SetAttributes(semconv.HTTPStatusCode(code)) + } else { + span.SetStatus(codes.Error, "") + span.SetAttributes(semconv.HTTPStatusCode(http.StatusInternalServerError)) + } + + return err +} + +// HttpTraceStatusFromContext create an error span if we have an error and a successful span when error is nil +func HttpTraceStatusFromContext(ctx context.Context, err error) error { + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span := trace.SpanFromContext(ctx) + + defer span.End() + + return HttpTraceStatusFromSpan(span, err) +} + +func TraceStatusFromContext(ctx context.Context, err error) error { + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span := trace.SpanFromContext(ctx) + + defer span.End() + + return TraceStatusFromSpan(span, err) +} + +func TraceErrStatusFromContext(ctx context.Context, err error) error { + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span := trace.SpanFromContext(ctx) + + defer span.End() + + return TraceErrStatusFromSpan(span, err) +} + +// GrpcTraceErrFromSpan setting span with status error with error message +func GrpcTraceErrFromSpan(span trace.Span, err error) error { + isError := err != nil + + span.SetStatus(codes.Error, err.Error()) + + if isError { + stackTraceError := errorUtils.ErrorsWithStack(err) + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + + if customErrors.IsCustomError(err) { + grpcErr := grpcerrors.ParseError(err) + span.SetAttributes( + semconv.RPCGRPCStatusCodeKey.Int(int(grpcErr.GetStatus())), + ) + } + + span.RecordError(err) + } + + return err +} + +// GrpcTraceErrFromSpanWithCode setting span with status error with error message +func GrpcTraceErrFromSpanWithCode(span trace.Span, err error, code int) error { + isError := err != nil + + span.SetStatus(codes.Error, err.Error()) + + if isError { + stackTraceError := errorUtils.ErrorsWithStack(err) + // https://opentelemetry.io/docs/instrumentation/go/manual/#record-errors + span.SetAttributes( + attribute.String(telemetrytags.Exceptions.Message, err.Error()), + attribute.String(telemetrytags.Exceptions.Stacktrace, stackTraceError), + ) + span.SetAttributes(semconv.RPCGRPCStatusCodeKey.Int(code)) + span.RecordError(err) + } + + return err +} + +func GetParentSpanContext(span trace.Span) trace.SpanContext { + readWriteSpan, ok := span.(trace2.ReadWriteSpan) + if !ok { + return *new(trace.SpanContext) + } + + return readWriteSpan.Parent() +} + +func ContextWithParentSpan( + parent context.Context, + span trace.Span, +) context.Context { + return context.WithValue(parent, parentSpanKey, span) +} + +func ParentSpanFromContext(ctx context.Context) trace.Span { + _, nopSpan := trace.NewNoopTracerProvider().Tracer("").Start(ctx, "") + if ctx == nil { + return nopSpan + } + + if span, ok := ctx.Value(parentSpanKey).(trace.Span); ok { + return span + } + + return nopSpan +} + +func CopyFromParentSpanAttribute( + ctx context.Context, + span trace.Span, + attributeName string, + parentAttributeName string, +) { + parentAtt := GetParentSpanAttribute(ctx, parentAttributeName) + if reflect.ValueOf(parentAtt).IsZero() { + return + } + + span.SetAttributes( + attribute.String(attributeName, parentAtt.Value.AsString()), + ) +} + +func CopyFromParentSpanAttributeIfNotSet( + ctx context.Context, + span trace.Span, + attributeName string, + attributeValue string, + parentAttributeName string, +) { + if attributeValue != "" { + span.SetAttributes(attribute.String(attributeName, attributeValue)) + return + } + CopyFromParentSpanAttribute(ctx, span, attributeName, parentAttributeName) +} + +func GetParentSpanAttribute( + ctx context.Context, + parentAttributeName string, +) attribute.KeyValue { + parentSpan := ParentSpanFromContext(ctx) + readWriteSpan, ok := parentSpan.(trace2.ReadWriteSpan) + if !ok { + return *new(attribute.KeyValue) + } + att := linq.From(readWriteSpan.Attributes()). + FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == parentAttributeName }) + + return att.(attribute.KeyValue) +} + +func GetSpanAttributeFromCurrentContext( + ctx context.Context, + attributeName string, +) attribute.KeyValue { + span := trace.SpanFromContext(ctx) + readWriteSpan, ok := span.(trace2.ReadWriteSpan) + if !ok { + return *new(attribute.KeyValue) + } + att := linq.From(readWriteSpan.Attributes()). + FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == attributeName }) + + return att.(attribute.KeyValue) +} + +func GetSpanAttribute( + span trace.Span, + attributeName string, +) attribute.KeyValue { + readWriteSpan, ok := span.(trace2.ReadWriteSpan) + if !ok { + return *new(attribute.KeyValue) + } + + att := linq.From(readWriteSpan.Attributes()). + FirstWithT(func(att attribute.KeyValue) bool { return string(att.Key) == attributeName }) + + return att.(attribute.KeyValue) +} + +func MapsToAttributes(maps map[string]interface{}) []attribute.KeyValue { + var att []attribute.KeyValue + + for key, val := range maps { + switch val.(type) { + case string: + att = append(att, attribute.String(key, val.(string))) + case int64: + att = append(att, attribute.Int64(key, val.(int64))) + case int, int32: + att = append(att, attribute.Int(key, val.(int))) + case float64, float32: + att = append(att, attribute.Float64(key, val.(float64))) + case bool: + att = append(att, attribute.Bool(key, val.(bool))) + } + } + + return att +} + +func MetadataToSet(meta metadata.Metadata) attribute.Set { + var keyValue []attribute.KeyValue + for key, val := range meta { + keyValue = append(keyValue, attribute.String(key, val.(string))) + } + + return attribute.NewSet(keyValue...) +} diff --git a/internal/pkg/postgresgorm/constants/constants.go b/internal/pkg/postgresgorm/constants/constants.go new file mode 100644 index 00000000..d45da51a --- /dev/null +++ b/internal/pkg/postgresgorm/constants/constants.go @@ -0,0 +1,5 @@ +package constants + +type contextKey string + +const TxKey contextKey = "tx_key" diff --git a/internal/pkg/postgresgorm/contracts/gorm_context.go b/internal/pkg/postgresgorm/contracts/gorm_context.go new file mode 100644 index 00000000..3acbd734 --- /dev/null +++ b/internal/pkg/postgresgorm/contracts/gorm_context.go @@ -0,0 +1,12 @@ +package contracts + +import ( + "context" + + "gorm.io/gorm" +) + +type GormContext struct { + Tx *gorm.DB + context.Context +} diff --git a/internal/pkg/postgresgorm/contracts/gorm_dbcontext.go b/internal/pkg/postgresgorm/contracts/gorm_dbcontext.go new file mode 100644 index 00000000..6a78611b --- /dev/null +++ b/internal/pkg/postgresgorm/contracts/gorm_dbcontext.go @@ -0,0 +1,16 @@ +package contracts + +import ( + "context" + + "gorm.io/gorm" +) + +type GormDBContext interface { + WithTx(ctx context.Context) (GormDBContext, error) + WithTxIfExists(ctx context.Context) GormDBContext + RunInTx(ctx context.Context, action ActionFunc) error + DB() *gorm.DB +} + +type ActionFunc func(ctx context.Context, gormContext GormDBContext) error diff --git a/internal/pkg/postgresgorm/gorm_db.go b/internal/pkg/postgresgorm/gorm_db.go new file mode 100644 index 00000000..c5672e1d --- /dev/null +++ b/internal/pkg/postgresgorm/gorm_db.go @@ -0,0 +1,155 @@ +package postgresgorm + +import ( + "database/sql" + "fmt" + + defaultlogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/gromlog" + + "emperror.dev/errors" + "github.com/glebarez/sqlite" + gormPostgres "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/plugin/opentelemetry/tracing" +) + +func NewGorm(cfg *GormOptions) (*gorm.DB, error) { + if cfg.DBName == "" { + return nil, errors.New("DBName is required in the config.") + } + + if cfg.UseSQLLite { + db, err := createSQLLiteDB(cfg.Dns()) + + return db, err + } + + // InMemory doesn't work correctly with transactions - seems when we `Begin` a transaction on gorm.DB (with SQLLite in-memory) our previous gormDB before transaction will remove and the new gormDB with tx will go on the memory + if cfg.UseInMemory { + db, err := createInMemoryDB() + + return db, err + } + + err := createPostgresDB(cfg) + if err != nil { + return nil, err + } + + dataSourceName := fmt.Sprintf( + "host=%s port=%d user=%s dbname=%s password=%s", + cfg.Host, + cfg.Port, + cfg.User, + cfg.DBName, + cfg.Password, + ) + + gormDb, err := gorm.Open( + gormPostgres.Open(dataSourceName), + &gorm.Config{ + Logger: gromlog.NewGormCustomLogger(defaultlogger.GetLogger()), + }, + ) + if err != nil { + return nil, err + } + + // add tracing to gorm + if cfg.EnableTracing { + err = gormDb.Use(tracing.NewPlugin()) + } + + return gormDb, nil +} + +func createInMemoryDB() (*gorm.DB, error) { + // https://gorm.io/docs/connecting_to_the_database.html#SQLite + // https://github.com/glebarez/sqlite + // https://www.connectionstrings.com/sqlite/ + db, err := gorm.Open( + sqlite.Open(":memory:"), + &gorm.Config{ + Logger: gromlog.NewGormCustomLogger(defaultlogger.GetLogger()), + }) + + return db, err +} + +func createSQLLiteDB(dbFilePath string) (*gorm.DB, error) { + // https://gorm.io/docs/connecting_to_the_database.html#SQLite + // https://github.com/glebarez/sqlite + // https://www.connectionstrings.com/sqlite/ + gormSQLLiteDB, err := gorm.Open( + sqlite.Open(dbFilePath), + &gorm.Config{ + Logger: gromlog.NewGormCustomLogger(defaultlogger.GetLogger()), + }) + + return gormSQLLiteDB, err +} + +func NewSQLDB(orm *gorm.DB) (*sql.DB, error) { + return orm.DB() +} + +func createPostgresDB(cfg *GormOptions) error { + var db *sql.DB + + // we should choose a default database in the connection, but because we don't have a database yet we specify postgres default database 'postgres' + dataSourceName := fmt.Sprintf( + "host=%s port=%d user=%s dbname=%s password=%s", + cfg.Host, + cfg.Port, + cfg.User, + "postgres", + cfg.Password, + ) + postgresGormDB, err := gorm.Open( + gormPostgres.Open(dataSourceName), + &gorm.Config{ + Logger: gromlog.NewGormCustomLogger(defaultlogger.GetLogger()), + }, + ) + if err != nil { + return err + } + + db, err = postgresGormDB.DB() + + if err != nil { + return err + } + + rows, err := db.Query( + fmt.Sprintf( + "SELECT 1 FROM pg_catalog.pg_database WHERE datname='%s'", + cfg.DBName, + ), + ) + if err != nil { + return err + } + + var exists int + if rows.Next() { + err = rows.Scan(&exists) + if err != nil { + return err + } + } + + if exists == 1 { + return nil + } + + _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", cfg.DBName)) + if err != nil { + return err + } + + defer db.Close() + + return nil +} diff --git a/internal/pkg/postgresgorm/gorm_options.go b/internal/pkg/postgresgorm/gorm_options.go new file mode 100644 index 00000000..c0dbd574 --- /dev/null +++ b/internal/pkg/postgresgorm/gorm_options.go @@ -0,0 +1,53 @@ +package postgresgorm + +import ( + "fmt" + "path/filepath" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/iancoleman/strcase" +) + +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[GormOptions]()) + +type GormOptions struct { + UseInMemory bool `mapstructure:"useInMemory"` + UseSQLLite bool `mapstructure:"useSqlLite"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + User string `mapstructure:"user"` + DBName string `mapstructure:"dbName"` + SSLMode bool `mapstructure:"sslMode"` + Password string `mapstructure:"password"` + EnableTracing bool `mapstructure:"enableTracing" default:"true"` +} + +func (h *GormOptions) Dns() string { + if h.UseInMemory { + return "" + } + + if h.UseSQLLite { + projectRootDir := environment.GetProjectRootWorkingDirectory() + dbFilePath := filepath.Join(projectRootDir, fmt.Sprintf("%s.db", h.DBName)) + + return dbFilePath + } + + datasource := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", + h.User, + h.Password, + h.Host, + h.Port, + h.DBName, + ) + + return datasource +} + +func provideConfig(environment environment.Environment) (*GormOptions, error) { + return config.BindConfigKey[*GormOptions](optionName, environment) +} diff --git a/internal/pkg/gorm_postgres/gorm_options_test.go b/internal/pkg/postgresgorm/gorm_options_test.go similarity index 87% rename from internal/pkg/gorm_postgres/gorm_options_test.go rename to internal/pkg/postgresgorm/gorm_options_test.go index 3240d833..359360cd 100644 --- a/internal/pkg/gorm_postgres/gorm_options_test.go +++ b/internal/pkg/postgresgorm/gorm_options_test.go @@ -1,4 +1,4 @@ -package gormPostgres +package postgresgorm import ( "testing" diff --git a/internal/pkg/gorm_postgres/gorm_postgres_fx.go b/internal/pkg/postgresgorm/gorm_postgres_fx.go similarity index 83% rename from internal/pkg/gorm_postgres/gorm_postgres_fx.go rename to internal/pkg/postgresgorm/gorm_postgres_fx.go index b25441bb..e994bcd7 100644 --- a/internal/pkg/gorm_postgres/gorm_postgres_fx.go +++ b/internal/pkg/postgresgorm/gorm_postgres_fx.go @@ -1,9 +1,9 @@ -package gormPostgres +package postgresgorm import ( "fmt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "go.uber.org/fx" ) @@ -16,9 +16,10 @@ var Module = fx.Module( provideConfig, NewGorm, NewSQLDB, + fx.Annotate( NewGormHealthChecker, - fx.As(new(health.Health)), + fx.As(new(contracts.Health)), fx.ResultTags(fmt.Sprintf(`group:"%s"`, "healths")), ), ), diff --git a/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext.go b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext.go new file mode 100644 index 00000000..743c3ed5 --- /dev/null +++ b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext.go @@ -0,0 +1,93 @@ +package gormdbcontext + +import ( + "context" + + defaultlogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + + "gorm.io/gorm" +) + +type gormDBContext struct { + db *gorm.DB +} + +func NewGormDBContext(db *gorm.DB) contracts.GormDBContext { + c := &gormDBContext{db: db} + + return c +} + +func (c *gormDBContext) DB() *gorm.DB { + return c.db +} + +// WithTx creates a transactional DBContext with getting tx-gorm from the ctx. This will throw an error if the transaction does not exist. +func (c *gormDBContext) WithTx( + ctx context.Context, +) (contracts.GormDBContext, error) { + tx, err := gormextensions.GetTxFromContext(ctx) + if err != nil { + return nil, err + } + + return NewGormDBContext(tx), nil +} + +// WithTxIfExists creates a transactional DBContext with getting tx-gorm from the ctx. not throw an error if the transaction is not existing and returns an existing database. +func (c *gormDBContext) WithTxIfExists( + ctx context.Context, +) contracts.GormDBContext { + tx := gormextensions.GetTxFromContextIfExists(ctx) + if tx == nil { + return c + } + + return NewGormDBContext(tx) +} + +func (c *gormDBContext) RunInTx( + ctx context.Context, + action contracts.ActionFunc, +) error { + // https://gorm.io/docs/transactions.html#Transaction + tx := c.DB().WithContext(ctx).Begin() + + defaultlogger.GetLogger().Info("beginning database transaction") + + gormContext := gormextensions.SetTxToContext(ctx, tx) + ctx = gormContext + + defer func() { + if r := recover(); r != nil { + tx.WithContext(ctx).Rollback() + + if err, _ := r.(error); err != nil { + defaultlogger.GetLogger().Errorf( + "panic tn the transaction, rolling back transaction with panic err: %+v", + err, + ) + } else { + defaultlogger.GetLogger().Errorf("panic tn the transaction, rolling back transaction with panic message: %+v", r) + } + } + }() + + err := action(ctx, c) + if err != nil { + defaultlogger.GetLogger().Error("rolling back transaction") + tx.WithContext(ctx).Rollback() + + return err + } + + defaultlogger.GetLogger().Info("committing transaction") + + if err = tx.WithContext(ctx).Commit().Error; err != nil { + defaultlogger.GetLogger().Errorf("transaction commit error: %+v", err) + } + + return err +} diff --git a/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_extensions.go b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_extensions.go new file mode 100644 index 00000000..f43459f8 --- /dev/null +++ b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_extensions.go @@ -0,0 +1,277 @@ +package gormdbcontext + +import ( + "context" + "fmt" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + defaultlogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/scopes" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/iancoleman/strcase" + uuid "github.com/satori/go.uuid" +) + +func Exists[TDataModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + id uuid.UUID, +) bool { + var count int64 + + dataModel := typeMapper.GenericInstanceByT[TDataModel]() + + dbContext.DB().WithContext(ctx).Model(dataModel).Scopes(scopes.FilterByID(id)).Count(&count) + + return count > 0 +} + +func FindModelByID[TDataModel interface{}, TModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + id uuid.UUID, +) (TModel, error) { + var dataModel TDataModel + + // https://gorm.io/docs/query.html#Retrieving-objects-with-primary-key + // https://gorm.io/docs/query.html#Struct-amp-Map-Conditions + // https://gorm.io/docs/query.html#Inline-Condition + // https://gorm.io/docs/advanced_query.html + // result := c.WithContext(ctx).First(&dataModel, "id = ?", id) + // result := c.WithContext(ctx).First(&TDataModel{Id: id}) + // result := c.WithContext(ctx).Scopes(scopes.FilterByID(id)).First(&dataModel) + + modelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TModel]()) + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + + result := dbContext.DB().WithContext(ctx).First(&dataModel, id) + if result.Error != nil { + return *new(TModel), customErrors.NewNotFoundErrorWrap( + result.Error, + fmt.Sprintf( + "%s with id `%s` not found in the database", + dataModelName, + id.String(), + ), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + resultModel, err := mapper.Map[TModel](dataModel) + if err != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + err, + fmt.Sprintf("error in the mapping %s", modelName), + ) + } + + return resultModel, nil +} + +func FindDataModelByID[TDataModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + id uuid.UUID, +) (TDataModel, error) { + var dataModel TDataModel + + // https://gorm.io/docs/query.html#Retrieving-objects-with-primary-key + // https://gorm.io/docs/query.html#Struct-amp-Map-Conditions + // https://gorm.io/docs/query.html#Inline-Condition + // https://gorm.io/docs/advanced_query.html + // result := c.WithContext(ctx).First(&dataModel, "id = ?", id) + // result := c.WithContext(ctx).First(&TDataModel{Id: id}) + // result := c.WithContext(ctx).Scopes(scopes.FilterByID(id)).First(&dataModel) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + + result := dbContext.DB().WithContext(ctx).First(&dataModel, id) + if result.Error != nil { + return *new(TDataModel), customErrors.NewNotFoundErrorWrap( + result.Error, + fmt.Sprintf( + "%s with id `%s` not found in the database", + dataModelName, + id.String(), + ), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + return dataModel, nil +} + +// DeleteDataModelByID delete the data-model inner a tx if exists +func DeleteDataModelByID[TDataModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + id uuid.UUID, +) error { + txDBContext := dbContext.WithTxIfExists(ctx) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + + exists := Exists[TDataModel](ctx, dbContext, id) + if !exists { + return customErrors.NewNotFoundError(fmt.Sprintf("%s with id `%s` not found in the database", + dataModelName, + id.String(), + )) + } + + dataModel := typeMapper.GenericInstanceByT[TDataModel]() + + // https://gorm.io/docs/delete.html#Delete-a-Record + // https://gorm.io/docs/delete.html#Find-soft-deleted-records + // result := dbContext.WithContext(ctx).Delete(&TDataModel{Id: id}) + result := txDBContext.DB().WithContext(ctx).Delete(dataModel, id) + if result.Error != nil { + return customErrors.NewInternalServerErrorWrap( + result.Error, + fmt.Sprintf( + "error in deleting %s with id `%s` in the database", + dataModelName, + id.String(), + ), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + return nil +} + +// AddModel add the model inner a tx if exists +func AddModel[TDataModel interface{}, TModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + model TModel, +) (TModel, error) { + txDBContext := dbContext.WithTxIfExists(ctx) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + modelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TModel]()) + + dataModel, err := mapper.Map[TDataModel](model) + if err != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + err, + fmt.Sprintf("error in the mapping %s", dataModelName), + ) + } + + // https://gorm.io/docs/create.html + result := txDBContext.DB().WithContext(ctx).Create(dataModel) + if result.Error != nil { + return *new(TModel), customErrors.NewConflictErrorWrap( + result.Error, + fmt.Sprintf("%s already exists", modelName), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + resultModel, err := mapper.Map[TModel](dataModel) + if err != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + err, + fmt.Sprintf("error in the mapping %s", modelName), + ) + } + + return resultModel, err +} + +// AddDataModel add the data-model inner a tx if exists +func AddDataModel[TDataModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + dataModel TDataModel, +) (TDataModel, error) { + txDBContext := dbContext.WithTxIfExists(ctx) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + + // https://gorm.io/docs/create.html + result := txDBContext.DB().WithContext(ctx).Create(dataModel) + if result.Error != nil { + return *new(TDataModel), customErrors.NewConflictErrorWrap( + result.Error, + fmt.Sprintf("%s already exists", dataModelName), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + return dataModel, nil +} + +// UpdateModel update the model inner a tx if exists +func UpdateModel[TDataModel interface{}, TModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + model TModel, +) (TModel, error) { + txDBContext := dbContext.WithTxIfExists(ctx) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + modelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TModel]()) + + dataModel, err := mapper.Map[TDataModel](model) + if err != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + err, + fmt.Sprintf("error in the mapping %s", dataModelName), + ) + } + + // https://gorm.io/docs/update.html + result := txDBContext.DB().WithContext(ctx).Updates(dataModel) + if result.Error != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + result.Error, + fmt.Sprintf("error in updating the %s", modelName), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + modelResult, err := mapper.Map[TModel](dataModel) + if err != nil { + return *new(TModel), customErrors.NewInternalServerErrorWrap( + err, + fmt.Sprintf("error in the mapping %s", modelName), + ) + } + + return modelResult, err +} + +// UpdateDataModel update the data-model inner a tx if exists +func UpdateDataModel[TDataModel interface{}]( + ctx context.Context, + dbContext contracts.GormDBContext, + dataModel TDataModel, +) (TDataModel, error) { + txDBContext := dbContext.WithTxIfExists(ctx) + + dataModelName := strcase.ToSnake(typeMapper.GetGenericNonePointerTypeNameByT[TDataModel]()) + + // https://gorm.io/docs/update.html + result := txDBContext.DB().WithContext(ctx).Updates(dataModel) + if result.Error != nil { + return *new(TDataModel), customErrors.NewInternalServerErrorWrap( + result.Error, + fmt.Sprintf("error in updating the %s", dataModelName), + ) + } + + defaultlogger.GetLogger().Infof("Number of affected rows are: %d", result.RowsAffected) + + return dataModel, nil +} diff --git a/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_test.go b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_test.go new file mode 100644 index 00000000..354f4581 --- /dev/null +++ b/internal/pkg/postgresgorm/gormdbcontext/gorm_dbcontext_test.go @@ -0,0 +1,327 @@ +//go:build unit +// +build unit + +package gormdbcontext + +import ( + "context" + "os" + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/scopes" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + "github.com/goccy/go-json" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/suite" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + "gorm.io/gorm" +) + +// ProductDataModel data model +type ProductDataModel struct { + Id uuid.UUID `gorm:"primaryKey"` + Name string + Description string + Price float64 + CreatedAt time.Time `gorm:"default:current_timestamp"` + UpdatedAt time.Time + // for soft delete - https://gorm.io/docs/delete.html#Soft-Delete + gorm.DeletedAt +} + +// TableName overrides the table name used by ProductDataModel to `products` - https://gorm.io/docs/conventions.html#TableName +func (p *ProductDataModel) TableName() string { + return "products" +} + +func (p *ProductDataModel) String() string { + j, _ := json.Marshal(p) + + return string(j) +} + +// Product model +type Product struct { + Id uuid.UUID + Name string + Description string + Price float64 + CreatedAt time.Time + UpdatedAt time.Time +} + +// Define the suite +type GormDBContextTestSuite struct { + suite.Suite + items []*ProductDataModel + dbContext contracts.GormDBContext + app *fxtest.App + dbFilePath string +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestGormDBContext(t *testing.T) { + suite.Run(t, new(GormDBContextTestSuite)) +} + +func (s *GormDBContextTestSuite) Test_FindProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + p, err := FindModelByID[*ProductDataModel, *Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + s.Require().NotNil(p) + + s.Assert().Equal(p.Id, id) +} + +func (s *GormDBContextTestSuite) Test_ExistsProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + exist := Exists[*ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + + s.Require().True(exist) +} + +func (s *GormDBContextTestSuite) Test_NoneExistsProductByID() { + s.Require().NotNil(s.dbContext) + + id := uuid.NewV4() + + exist := Exists[*ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + + s.Require().False(exist) +} + +func (s *GormDBContextTestSuite) Test_DeleteProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + err := DeleteDataModelByID[*ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + p, err := FindModelByID[*ProductDataModel, *Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().Error(err) + s.Require().Nil(p) + + // https://gorm.io/docs/delete.html#Find-soft-deleted-records + var softDeletedProduct *ProductDataModel + s.dbContext.DB().Scopes(scopes.FilterAllItemsWithSoftDeleted).First(&softDeletedProduct, id) + s.Require().NotNil(softDeletedProduct) + + var deletedCount int64 + var allCount int64 + + // https://gorm.io/docs/advanced_query.html#Count + s.dbContext.DB().Model(&ProductDataModel{}).Scopes(scopes.FilterAllItemsWithSoftDeleted).Count(&allCount) + s.Equal(allCount, int64(2)) + + s.dbContext.DB().Model(&ProductDataModel{}).Scopes(scopes.SoftDeleted).Count(&deletedCount) + s.Equal(deletedCount, int64(1)) +} + +func (s *GormDBContextTestSuite) Test_CreateProduct() { + s.Require().NotNil(s.dbContext) + + item := &Product{ + Id: uuid.NewV4(), + Name: gofakeit.Name(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + } + + res, err := AddModel[*ProductDataModel, *Product](context.Background(), s.dbContext, item) + s.Require().NoError(err) + + p, err := FindModelByID[*ProductDataModel, *Product]( + context.Background(), + s.dbContext, + item.Id, + ) + s.Require().NoError(err) + s.Require().NotNil(p) + + s.Assert().Equal(p.Id, item.Id) + s.Assert().Equal(p.Id, res.Id) +} + +func (s *GormDBContextTestSuite) Test_UpdateProduct() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + p, err := FindModelByID[*ProductDataModel, *Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + newName := gofakeit.Name() + item := p + item.Name = newName + + res, err := UpdateModel[*ProductDataModel, *Product](context.Background(), s.dbContext, item) + s.Require().NoError(err) + + p2, err := FindModelByID[*ProductDataModel, *Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + s.Assert().Equal(item.Name, p2.Name) + s.Assert().Equal(res.Name, p2.Name) +} + +// TestSuite Hooks + +func (s *GormDBContextTestSuite) SetupTest() { + err := ConfigureProductsMappings() + s.Require().NoError(err) + + var gormDBContext contracts.GormDBContext + var gormOptions *gormPostgres.GormOptions + + app := fxtest.New( + s.T(), + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + gormPostgres.Module, + fx.Provide(NewGormDBContext), + fx.Decorate( + func(cfg *gormPostgres.GormOptions) (*gormPostgres.GormOptions, error) { + // using sql-lite with a database file + cfg.UseSQLLite = true + + return cfg, nil + }, + ), + fx.Populate(&gormDBContext), + fx.Populate(&gormOptions), + ).RequireStart() + + s.app = app + s.dbContext = gormDBContext + s.dbFilePath = gormOptions.Dns() + + s.initDB() +} + +func (s *GormDBContextTestSuite) TearDownTest() { + err := s.cleanupDB() + s.Require().NoError(err) + + mapper.ClearMappings() + + s.app.RequireStop() +} + +func (s *GormDBContextTestSuite) initDB() { + err := migrateGorm(s.dbContext.DB()) + s.Require().NoError(err) + + products, err := seedData(s.dbContext.DB()) + s.Require().NoError(err) + + s.items = products +} + +func (s *GormDBContextTestSuite) cleanupDB() error { + sqldb, _ := s.dbContext.DB().DB() + e := sqldb.Close() + s.Require().NoError(e) + + // removing sql-lite file + err := os.Remove(s.dbFilePath) + + return err +} + +func migrateGorm(db *gorm.DB) error { + err := db.AutoMigrate(&ProductDataModel{}) + if err != nil { + return err + } + + return nil +} + +func seedData(gormDB *gorm.DB) ([]*ProductDataModel, error) { + products := []*ProductDataModel{ + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + } + + // seed data + err := gormDB.CreateInBatches(products, len(products)).Error + if err != nil { + return nil, errors.Wrap(err, "error in seed database") + } + + return products, nil +} + +func ConfigureProductsMappings() error { + err := mapper.CreateMap[*ProductDataModel, *Product]() + if err != nil { + return err + } + + err = mapper.CreateMap[*Product, *ProductDataModel]() + if err != nil { + return err + } + + return nil +} diff --git a/internal/pkg/gorm_postgres/health.go b/internal/pkg/postgresgorm/health.go similarity index 80% rename from internal/pkg/gorm_postgres/health.go rename to internal/pkg/postgresgorm/health.go index 16838f76..539a81a2 100644 --- a/internal/pkg/gorm_postgres/health.go +++ b/internal/pkg/postgresgorm/health.go @@ -1,17 +1,17 @@ -package gormPostgres +package postgresgorm import ( "context" "database/sql" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" ) type gormHealthChecker struct { client *sql.DB } -func NewGormHealthChecker(client *sql.DB) health.Health { +func NewGormHealthChecker(client *sql.DB) contracts.Health { return &gormHealthChecker{client} } diff --git a/internal/pkg/postgresgorm/helpers/gormextensions/gorm_extensions.go b/internal/pkg/postgresgorm/helpers/gormextensions/gorm_extensions.go new file mode 100644 index 00000000..3780df15 --- /dev/null +++ b/internal/pkg/postgresgorm/helpers/gormextensions/gorm_extensions.go @@ -0,0 +1,74 @@ +package gormextensions + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/constants" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/scopes" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + + "emperror.dev/errors" + "gorm.io/gorm" +) + +func GetTxFromContext(ctx context.Context) (*gorm.DB, error) { + gCtx, gCtxOk := ctx.(*contracts.GormContext) + if gCtxOk { + return gCtx.Tx, nil + } + + tx, ok := ctx.Value(constants.TxKey).(*gorm.DB) + if !ok { + return nil, errors.New("Transaction not found in context") + } + + return tx, nil +} + +func GetTxFromContextIfExists(ctx context.Context) *gorm.DB { + gCtx, gCtxOk := ctx.(*contracts.GormContext) + if gCtxOk { + return gCtx.Tx + } + + tx, ok := ctx.Value(constants.TxKey).(*gorm.DB) + if !ok { + return nil + } + + return tx +} + +func SetTxToContext(ctx context.Context, tx *gorm.DB) *contracts.GormContext { + newCtx := context.WithValue(ctx, constants.TxKey, tx) + gormContext := &contracts.GormContext{Tx: tx, Context: newCtx} + ctx = gormContext + + return gormContext +} + +// Ref: https://dev.to/rafaelgfirmino/pagination-using-gorm-scopes-3k5f + +func Paginate[TDataModel any, TEntity any]( + ctx context.Context, + listQuery *utils.ListQuery, + db *gorm.DB, +) (*utils.ListResult[TEntity], error) { + var ( + items []TEntity + totalRows int64 + ) + + // https://gorm.io/docs/advanced_query.html#Smart-Select-Fields + if err := db.Scopes(scopes.FilterPaginate[TDataModel](ctx, listQuery)).Find(&items).Error; err != nil { + return nil, errors.WrapIf(err, "error in finding products.") + } + + return utils.NewListResult[TEntity]( + items, + listQuery.GetSize(), + listQuery.GetPage(), + totalRows, + ), nil +} diff --git a/internal/pkg/postgresgorm/pipelines/mediator_transaction_pipeline.go b/internal/pkg/postgresgorm/pipelines/mediator_transaction_pipeline.go new file mode 100644 index 00000000..3f360c05 --- /dev/null +++ b/internal/pkg/postgresgorm/pipelines/mediator_transaction_pipeline.go @@ -0,0 +1,94 @@ +package pipelines + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + + "github.com/mehdihadeli/go-mediatr" + "gorm.io/gorm" +) + +type mediatorTransactionPipeline struct { + logger logger.Logger + db *gorm.DB +} + +func NewMediatorTransactionPipeline( + l logger.Logger, + db *gorm.DB, +) mediatr.PipelineBehavior { + return &mediatorTransactionPipeline{ + logger: l, + db: db, + } +} + +func (m *mediatorTransactionPipeline) Handle( + ctx context.Context, + request interface{}, + next mediatr.RequestHandlerFunc, +) (interface{}, error) { + requestName := typeMapper.GetSnakeTypeName(request) + + txRequest, ok := request.(cqrs.TxRequest) + if !ok || !txRequest.isTxRequest() { + return next(ctx) + } + + var result interface{} + + // https://gorm.io/docs/transactions.html#Transaction + tx := m.db.WithContext(ctx).Begin() + + m.logger.Infof( + "beginning database transaction for request `%s`", + requestName, + ) + + gormContext := gormextensions.SetTxToContext(ctx, tx) + ctx = gormContext + + defer func() { + if r := recover(); r != nil { + tx.WithContext(ctx).Rollback() + + if err, _ := r.(error); err != nil { + m.logger.Errorf( + "panic tn the transaction, rolling back transaction with panic err: %+v", + err, + ) + } else { + m.logger.Errorf("panic tn the transaction, rolling back transaction with panic message: %+v", r) + } + } + }() + + middlewareResponse, err := next(ctx) + result = middlewareResponse + + if err != nil { + m.logger.Errorf( + "rolling back transaction for request `%s`", + requestName, + ) + tx.WithContext(ctx).Rollback() + + return nil, err + } + + m.logger.Infof("committing transaction for request `%s`", requestName) + + if err = tx.WithContext(ctx).Commit().Error; err != nil { + m.logger.Errorf("transaction commit error: ", err) + } + + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/internal/pkg/gorm_postgres/repository/gorm_generic_repository.go b/internal/pkg/postgresgorm/repository/gorm_generic_repository.go similarity index 61% rename from internal/pkg/gorm_postgres/repository/gorm_generic_repository.go rename to internal/pkg/postgresgorm/repository/gorm_generic_repository.go index f0809770..6526bf27 100644 --- a/internal/pkg/gorm_postgres/repository/gorm_generic_repository.go +++ b/internal/pkg/postgresgorm/repository/gorm_generic_repository.go @@ -4,14 +4,15 @@ import ( "context" "fmt" "reflect" + "strings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data/specification" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflection_helper" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflectionhelper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "emperror.dev/errors" @@ -35,21 +36,27 @@ func NewGenericGormRepositoryWithDataModel[TDataModel interface{}, TEntity inter } // NewGenericGormRepository create new gorm generic repository -func NewGenericGormRepository[TEntity interface{}](db *gorm.DB) data.GenericRepository[TEntity] { +func NewGenericGormRepository[TEntity interface{}]( + db *gorm.DB, +) data.GenericRepository[TEntity] { return &gormGenericRepository[TEntity, TEntity]{ db: db, } } -func (r *gormGenericRepository[TDataModel, TEntity]) Add(ctx context.Context, entity TEntity) error { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (r *gormGenericRepository[TDataModel, TEntity]) Add( + ctx context.Context, + entity TEntity, +) error { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() if modelType == dataModelType { err := r.db.WithContext(ctx).Create(entity).Error if err != nil { return err } + return nil } else { dataModel, err := mapper.Map[TDataModel](entity) @@ -66,10 +73,14 @@ func (r *gormGenericRepository[TDataModel, TEntity]) Add(ctx context.Context, en } reflectionHelper.SetValue[TEntity](entity, e) } + return nil } -func (r *gormGenericRepository[TDataModel, TEntity]) AddAll(ctx context.Context, entities []TEntity) error { +func (r *gormGenericRepository[TDataModel, TEntity]) AddAll( + ctx context.Context, + entities []TEntity, +) error { for _, entity := range entities { err := r.Add(ctx, entity) if err != nil { @@ -80,23 +91,35 @@ func (r *gormGenericRepository[TDataModel, TEntity]) AddAll(ctx context.Context, return nil } -func (r *gormGenericRepository[TDataModel, TEntity]) GetById(ctx context.Context, id uuid.UUID) (TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (r *gormGenericRepository[TDataModel, TEntity]) GetById( + ctx context.Context, + id uuid.UUID, +) (TEntity, error) { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() + if modelType == dataModelType { var model TEntity if err := r.db.WithContext(ctx).First(&model, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return *new(TEntity), customErrors.NewNotFoundErrorWrap( err, - fmt.Sprintf("can't find the entity with id %s into the database.", id.String()), + fmt.Sprintf( + "can't find the entity with id %s into the database.", + id.String(), + ), ) } + return *new(TEntity), errors.WrapIf( err, - fmt.Sprintf("can't find the entity with id %s into the database.", id.String()), + fmt.Sprintf( + "can't find the entity with id %s into the database.", + id.String(), + ), ) } + return model, nil } else { var dataModel TDataModel @@ -104,12 +127,14 @@ func (r *gormGenericRepository[TDataModel, TEntity]) GetById(ctx context.Context if errors.Is(err, gorm.ErrRecordNotFound) { return *new(TEntity), customErrors.NewNotFoundErrorWrap(err, fmt.Sprintf("can't find the entity with id %s into the database.", id.String())) } + return *new(TEntity), errors.WrapIf(err, fmt.Sprintf("can't find the entity with id %s into the database.", id.String())) } entity, err := mapper.Map[TEntity](dataModel) if err != nil { return *new(TEntity), err } + return entity, nil } } @@ -118,25 +143,16 @@ func (r *gormGenericRepository[TDataModel, TEntity]) GetAll( ctx context.Context, listQuery *utils.ListQuery, ) (*utils.ListResult[TEntity], error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() - if modelType == dataModelType { - result, err := gormPostgres.Paginate[TEntity](ctx, listQuery, r.db) - if err != nil { - return nil, err - } - return result, nil - } else { - result, err := gormPostgres.Paginate[TDataModel](ctx, listQuery, r.db) - if err != nil { - return nil, err - } - models, err := utils.ListResultToListResultDto[TEntity](result) - if err != nil { - return nil, err - } - return models, nil + result, err := gormPostgres.Paginate[TDataModel, TEntity]( + ctx, + listQuery, + r.db, + ) + if err != nil { + return nil, err } + + return result, nil } func (r *gormGenericRepository[TDataModel, TEntity]) Search( @@ -144,56 +160,40 @@ func (r *gormGenericRepository[TDataModel, TEntity]) Search( searchTerm string, listQuery *utils.ListQuery, ) (*utils.ListResult[TEntity], error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() - if modelType == dataModelType { - fields := reflectionHelper.GetAllFields(typeMapper.GetTypeFromGeneric[TEntity]()) - query := r.db + fields := reflectionHelper.GetAllFields( + typeMapper.GetGenericTypeByT[TDataModel](), + ) + query := r.db - for _, field := range fields { - if field.Type.Kind() != reflect.String { - continue - } - f := strcase.ToSnake(field.Name) - whereQuery := fmt.Sprintf("%s IN (?)", f) - query = r.db.Where(whereQuery, searchTerm) + for _, field := range fields { + if field.Type.Kind() != reflect.String { + continue } - result, err := gormPostgres.Paginate[TEntity](ctx, listQuery, query) - if err != nil { - return nil, err - } - return result, nil - } else { - query := r.db - fields := reflectionHelper.GetAllFields(typeMapper.GetTypeFromGeneric[TDataModel]()) + query = query.Or( + fmt.Sprintf("%s LIKE ?", strcase.ToSnake(field.Name)), + "%"+strings.ToLower(searchTerm)+"%", + ) + } - for _, field := range fields { - if field.Type.Kind() != reflect.String { - continue - } - f := strcase.ToSnake(field.Name) - whereQuery := fmt.Sprintf("%s IN (?)", f) - query = r.db.WithContext(ctx).Where(whereQuery, searchTerm) - } - result, err := gormPostgres.Paginate[TDataModel](ctx, listQuery, query) - if err != nil { - return nil, err - } - models, err := utils.ListResultToListResultDto[TEntity](result) - if err != nil { - return nil, err - } - return models, nil + result, err := gormPostgres.Paginate[TDataModel, TEntity]( + ctx, + listQuery, + query, + ) + if err != nil { + return nil, err } + + return result, nil } func (r *gormGenericRepository[TDataModel, TEntity]) GetByFilter( ctx context.Context, filters map[string]interface{}, ) ([]TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() if modelType == dataModelType { var models []TEntity err := r.db.WithContext(ctx).Where(filters).Find(&models).Error @@ -229,9 +229,12 @@ func (r *gormGenericRepository[TDataModel, TEntity]) FirstOrDefault( return *new(TEntity), nil } -func (r *gormGenericRepository[TDataModel, TEntity]) Update(ctx context.Context, entity TEntity) error { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() +func (r *gormGenericRepository[TDataModel, TEntity]) Update( + ctx context.Context, + entity TEntity, +) error { + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() if modelType == dataModelType { err := r.db.WithContext(ctx).Save(entity).Error if err != nil { @@ -256,7 +259,10 @@ func (r *gormGenericRepository[TDataModel, TEntity]) Update(ctx context.Context, return nil } -func (r gormGenericRepository[TDataModel, TEntity]) UpdateAll(ctx context.Context, entities []TEntity) error { +func (r *gormGenericRepository[TDataModel, TEntity]) UpdateAll( + ctx context.Context, + entities []TEntity, +) error { for _, e := range entities { err := r.Update(ctx, e) if err != nil { @@ -267,7 +273,10 @@ func (r gormGenericRepository[TDataModel, TEntity]) UpdateAll(ctx context.Contex return nil } -func (r *gormGenericRepository[TDataModel, TEntity]) Delete(ctx context.Context, id uuid.UUID) error { +func (r *gormGenericRepository[TDataModel, TEntity]) Delete( + ctx context.Context, + id uuid.UUID, +) error { entity, err := r.GetById(ctx, id) if err != nil { return err @@ -286,11 +295,15 @@ func (r *gormGenericRepository[TDataModel, TEntity]) SkipTake( skip int, take int, ) ([]TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() if modelType == dataModelType { var models []TEntity - err := r.db.WithContext(ctx).Offset(skip).Limit(take).Find(&models).Error + err := r.db.WithContext(ctx). + Offset(skip). + Limit(take). + Find(&models). + Error if err != nil { return nil, err } @@ -309,7 +322,9 @@ func (r *gormGenericRepository[TDataModel, TEntity]) SkipTake( } } -func (r *gormGenericRepository[TDataModel, TEntity]) Count(ctx context.Context) int64 { +func (r *gormGenericRepository[TDataModel, TEntity]) Count( + ctx context.Context, +) int64 { var dataModel TDataModel var count int64 r.db.WithContext(ctx).Model(&dataModel).Count(&count) @@ -320,11 +335,14 @@ func (r *gormGenericRepository[TDataModel, TEntity]) Find( ctx context.Context, specification specification.Specification, ) ([]TEntity, error) { - dataModelType := typeMapper.GetTypeFromGeneric[TDataModel]() - modelType := typeMapper.GetTypeFromGeneric[TEntity]() + dataModelType := typeMapper.GetGenericTypeByT[TDataModel]() + modelType := typeMapper.GetGenericTypeByT[TEntity]() if modelType == dataModelType { var models []TEntity - err := r.db.WithContext(ctx).Where(specification.GetQuery(), specification.GetValues()...).Find(&models).Error + err := r.db.WithContext(ctx). + Where(specification.GetQuery(), specification.GetValues()...). + Find(&models). + Error if err != nil { return nil, err } diff --git a/internal/pkg/postgresgorm/repository/gorm_generic_repository_test.go b/internal/pkg/postgresgorm/repository/gorm_generic_repository_test.go new file mode 100644 index 00000000..aa89b6a3 --- /dev/null +++ b/internal/pkg/postgresgorm/repository/gorm_generic_repository_test.go @@ -0,0 +1,496 @@ +package repository + +import ( + "context" + "log" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + gorm2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/gorm" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + + "github.com/brianvoe/gofakeit/v6" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "gorm.io/gorm" + + _ "github.com/lib/pq" // postgres driver +) + +// Product is a domain_events entity +type Product struct { + ID uuid.UUID + Name string + Weight int + IsAvailable bool +} + +// ProductGorm is DTO used to map Product entity to database +type ProductGorm struct { + ID uuid.UUID `gorm:"primaryKey;column:id"` + Name string `gorm:"column:name"` + Weight int `gorm:"column:weight"` + IsAvailable bool `gorm:"column:is_available"` +} + +func (v *ProductGorm) TableName() string { + return "products_gorm" +} + +type gormGenericRepositoryTest struct { + suite.Suite + DB *gorm.DB + productRepository data.GenericRepository[*ProductGorm] + productRepositoryWithDataModel data.GenericRepositoryWithDataModel[*ProductGorm, *Product] + products []*ProductGorm +} + +func TestGormGenericRepository(t *testing.T) { + suite.Run( + t, + &gormGenericRepositoryTest{}, + ) +} + +func (c *gormGenericRepositoryTest) SetupSuite() { + opts, err := gorm2.NewGormTestContainers(defaultLogger.GetLogger()). + PopulateContainerOptions(context.Background(), c.T()) + c.Require().NoError(err) + + gormDB, err := postgresgorm.NewGorm(opts) + c.Require().NoError(err) + c.DB = gormDB + + err = migrationDatabase(gormDB) + c.Require().NoError(err) + + c.productRepository = NewGenericGormRepository[*ProductGorm](gormDB) + c.productRepositoryWithDataModel = NewGenericGormRepositoryWithDataModel[*ProductGorm, *Product]( + gormDB, + ) + + err = mapper.CreateMap[*ProductGorm, *Product]() + if err != nil { + log.Fatal(err) + } + + err = mapper.CreateMap[*Product, *ProductGorm]() + if err != nil { + log.Fatal(err) + } +} + +func (c *gormGenericRepositoryTest) SetupTest() { + p, err := seedData(context.Background(), c.DB) + c.Require().NoError(err) + c.products = p +} + +func (c *gormGenericRepositoryTest) TearDownTest() { + err := c.cleanupPostgresData() + c.Require().NoError(err) +} + +func (c *gormGenericRepositoryTest) Test_Add() { + ctx := context.Background() + + product := &ProductGorm{ + ID: uuid.NewV4(), + Name: gofakeit.Name(), + Weight: gofakeit.Number(100, 1000), + IsAvailable: true, + } + + err := c.productRepository.Add(ctx, product) + c.Require().NoError(err) + + p, err := c.productRepository.GetById(ctx, product.ID) + if err != nil { + return + } + + c.Assert().NotNil(p) + c.Assert().Equal(product.ID, p.ID) +} + +func (c *gormGenericRepositoryTest) Test_Add_With_Data_Model() { + ctx := context.Background() + + product := &Product{ + ID: uuid.NewV4(), + Name: gofakeit.Name(), + Weight: gofakeit.Number(100, 1000), + IsAvailable: true, + } + + err := c.productRepositoryWithDataModel.Add(ctx, product) + c.Require().NoError(err) + + p, err := c.productRepositoryWithDataModel.GetById(ctx, product.ID) + if err != nil { + return + } + + c.Assert().NotNil(p) + c.Assert().Equal(product.ID, p.ID) +} + +func (c *gormGenericRepositoryTest) Test_Get_By_Id() { + ctx := context.Background() + + all, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + + p := all.Items[0] + + testCases := []struct { + Name string + ProductId uuid.UUID + ExpectResult *ProductGorm + }{ + { + Name: p.Name, + ProductId: p.ID, + ExpectResult: p, + }, + { + Name: "NonExistingProduct", + ProductId: uuid.NewV4(), + ExpectResult: nil, + }, + } + + for _, s := range testCases { + c.T().Run(s.Name, func(t *testing.T) { + t.Parallel() + res, err := c.productRepository.GetById(ctx, s.ProductId) + if s.ExpectResult == nil { + assert.Error(t, err) + assert.True(t, customErrors.IsNotFoundError(err)) + assert.Nil(t, res) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + assert.Equal(t, p.ID, res.ID) + } + }) + } +} + +func (c *gormGenericRepositoryTest) Test_Get_By_Id_With_Data_Model() { + ctx := context.Background() + + all, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + if err != nil { + return + } + p := all.Items[0] + + testCases := []struct { + Name string + ProductId uuid.UUID + ExpectResult *Product + }{ + { + Name: p.Name, + ProductId: p.ID, + ExpectResult: p, + }, + { + Name: "NonExistingProduct", + ProductId: uuid.NewV4(), + ExpectResult: nil, + }, + } + + for _, s := range testCases { + c.T().Run(s.Name, func(t *testing.T) { + t.Parallel() + res, err := c.productRepositoryWithDataModel.GetById( + ctx, + s.ProductId, + ) + + if s.ExpectResult == nil { + assert.Error(t, err) + assert.True(t, customErrors.IsNotFoundError(err)) + assert.Nil(t, res) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + assert.Equal(t, p.ID, res.ID) + } + }) + } +} + +func (c *gormGenericRepositoryTest) Test_Get_All() { + ctx := context.Background() + + models, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + + c.Assert().NotEmpty(models.Items) +} + +func (c *gormGenericRepositoryTest) Test_Get_All_With_Data_Model() { + ctx := context.Background() + + models, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + c.Assert().NotEmpty(models.Items) +} + +func (c *gormGenericRepositoryTest) Test_Search() { + ctx := context.Background() + + models, err := c.productRepository.Search( + ctx, + c.products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + c.Assert().NotEmpty(models.Items) + c.Assert().Equal(len(models.Items), 1) +} + +func (c *gormGenericRepositoryTest) Test_Search_With_Data_Model() { + ctx := context.Background() + + models, err := c.productRepositoryWithDataModel.Search( + ctx, + c.products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + c.Assert().NotEmpty(models.Items) + c.Assert().Equal(len(models.Items), 1) +} + +func (c *gormGenericRepositoryTest) Test_Where() { + ctx := context.Background() + + models, err := c.productRepository.GetByFilter( + ctx, + map[string]interface{}{"name": c.products[0].Name}, + ) + c.Require().NoError(err) + + c.Assert().NotEmpty(models) + c.Assert().Equal(len(models), 1) +} + +func (c *gormGenericRepositoryTest) Test_Where_With_Data_Model() { + ctx := context.Background() + + models, err := c.productRepositoryWithDataModel.GetByFilter( + ctx, + map[string]interface{}{"name": c.products[0].Name}, + ) + c.Require().NoError(err) + + c.Assert().NotEmpty(models) + c.Assert().Equal(len(models), 1) +} + +func (c *gormGenericRepositoryTest) Test_Update() { + ctx := context.Background() + + products, err := c.productRepository.GetAll(ctx, utils.NewListQuery(10, 1)) + c.Require().NoError(err) + + product := products.Items[0] + + product.Name = "product2_updated" + err = c.productRepository.Update(ctx, product) + c.Require().NoError(err) + + single, err := c.productRepository.GetById(ctx, product.ID) + c.Require().NoError(err) + + c.Assert().NotNil(single) + c.Assert().Equal("product2_updated", single.Name) +} + +func (c *gormGenericRepositoryTest) Test_Update_With_Data_Model() { + ctx := context.Background() + + products, err := c.productRepositoryWithDataModel.GetAll( + ctx, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + product := products.Items[0] + + product.Name = "product2_updated" + err = c.productRepositoryWithDataModel.Update(ctx, product) + c.Require().NoError(err) + + single, err := c.productRepositoryWithDataModel.GetById(ctx, product.ID) + c.Require().NoError(err) + + c.Assert().NotNil(single) + c.Assert().Equal("product2_updated", single.Name) +} + +//func Test_Delete(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepository(ctx, t) +// +// products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) +// if err != nil { +// t.Fatal(err) +// } +// product := products.Items[0] +// +// err = repository.Delete(ctx, product.ID) +// if err != nil { +// return +// } +// +// single, err := repository.GetById(ctx, product.ID) +// assert.Nil(t, single) +//} +// +//func Test_Delete_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) +// +// products, err := repository.GetAll(ctx, utils.NewListQuery(10, 1)) +// if err != nil { +// t.Fatal(err) +// } +// product := products.Items[0] +// +// err = repository.Delete(ctx, product.ID) +// if err != nil { +// return +// } +// +// single, err := repository.GetById(ctx, product.ID) +// assert.Nil(t, single) +//} +// +//func Test_Count(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepository(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// count := repository.Count(ctx) +// +// assert.Equal(t, count, int64(2)) +//} +// +//func Test_Count_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// count := repository.Count(ctx) +// +// assert.Equal(t, count, int64(2)) +//} +// +//func Test_Find(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepository(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.Find( +// ctx, +// specification.And( +// specification.Equal("is_available", true), +// specification.Equal("name", "seed_product1"), +// ), +// ) +// if err != nil { +// return +// } +// assert.Equal(t, len(entities), 1) +//} +// +//func Test_Find_With_Data_Model(t *testing.T) { +// ctx := context.Background() +// repository, err := setupGenericGormRepositoryWithDataModel(ctx, t) +// if err != nil { +// t.Fatal(err) +// } +// +// entities, err := repository.Find( +// ctx, +// specification.And( +// specification.Equal("is_available", true), +// specification.Equal("name", "seed_product1"), +// ), +// ) +// if err != nil { +// return +// } +// assert.Equal(t, len(entities), 1) +//} + +func (c *gormGenericRepositoryTest) cleanupPostgresData() error { + tables := []string{"products_gorm"} + // Iterate over the tables and delete all records + for _, table := range tables { + err := c.DB.Exec("DELETE FROM " + table).Error + + return err + } + + return nil +} + +func migrationDatabase(db *gorm.DB) error { + err := db.AutoMigrate(ProductGorm{}) + if err != nil { + return err + } + + return nil +} + +func seedData(ctx context.Context, db *gorm.DB) ([]*ProductGorm, error) { + seedProducts := []*ProductGorm{ + { + ID: uuid.NewV4(), + Name: "seed_product1", + Weight: 100, + IsAvailable: true, + }, + { + ID: uuid.NewV4(), + Name: "seed_product2", + Weight: 100, + IsAvailable: true, + }, + } + + err := db.WithContext(ctx).Create(seedProducts).Error + if err != nil { + return nil, err + } + + return seedProducts, nil +} diff --git a/internal/pkg/postgresgorm/scopes/scopes.go b/internal/pkg/postgresgorm/scopes/scopes.go new file mode 100644 index 00000000..0f7620f0 --- /dev/null +++ b/internal/pkg/postgresgorm/scopes/scopes.go @@ -0,0 +1,88 @@ +package scopes + +import ( + "context" + "fmt" + "strings" + + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + + uuid "github.com/satori/go.uuid" + "gorm.io/gorm" +) + +// https://gorm.io/docs/advanced_query.html#Scopes +// https://gorm.io/docs/scopes.html + +// After scopes, we should have a runner function like Find, Update, Delete + +func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { + return db.Where("amount > ?", 1000) +} + +// FilterAllItemsWithSoftDeleted returns soft-deleted and none soft-deleted items +func FilterAllItemsWithSoftDeleted(db *gorm.DB) *gorm.DB { + // https://gorm.io/docs/delete.html#Find-soft-deleted-records + return db.Unscoped() +} + +// SoftDeleted returns only soft-deleted items +func SoftDeleted(db *gorm.DB) *gorm.DB { + return db.Unscoped().Where("deleted_at IS NOT NULL") +} + +func FilterByTitle(title string) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("title = ?", title) + } +} + +func FilterByID(id uuid.UUID) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Where("id = ?", id) + } +} + +func FilterPaginate[TDataModel any]( + ctx context.Context, + listQuery *utils.ListQuery, +) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + var totalRows int64 + + dataModel := typeMapper.GenericInstanceByT[TDataModel]() + // https://gorm.io/docs/advanced_query.html + db.WithContext(ctx).Model(dataModel).Count(&totalRows) + + // generate where query + query := db.WithContext(ctx). + Model(dataModel). + Offset(listQuery.GetOffset()). + Limit(listQuery.GetLimit()). + Order(listQuery.GetOrderBy()) + + if listQuery.Filters != nil { + for _, filter := range listQuery.Filters { + column := filter.Field + action := filter.Comparison + value := filter.Value + + switch action { + case "equals": + whereQuery := fmt.Sprintf("%s = ?", column) + query = query.Where(whereQuery, value) + case "contains": + whereQuery := fmt.Sprintf("%s LIKE ?", column) + query = query.Where(whereQuery, "%"+value+"%") + case "in": + whereQuery := fmt.Sprintf("%s IN (?)", column) + queryArray := strings.Split(value, ",") + query = query.Where(whereQuery, queryArray) + } + } + } + + return query + } +} diff --git a/internal/pkg/postgresmessaging/messagepersistence/message_persistence_dbcontext.go b/internal/pkg/postgresmessaging/messagepersistence/message_persistence_dbcontext.go new file mode 100644 index 00000000..43da83a6 --- /dev/null +++ b/internal/pkg/postgresmessaging/messagepersistence/message_persistence_dbcontext.go @@ -0,0 +1,22 @@ +package messagepersistence + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + + "gorm.io/gorm" +) + +type PostgresMessagePersistenceDBContext struct { + // our dbcontext base + contracts.GormDBContext +} + +func NewPostgresMessagePersistenceDBContext( + db *gorm.DB, +) *PostgresMessagePersistenceDBContext { + // initialize base GormContext + c := &PostgresMessagePersistenceDBContext{GormDBContext: gormdbcontext.NewGormDBContext(db)} + + return c +} diff --git a/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service.go b/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service.go new file mode 100644 index 00000000..6f535806 --- /dev/null +++ b/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service.go @@ -0,0 +1,287 @@ +package messagepersistence + +import ( + "context" + "errors" + "fmt" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/persistmessage" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + + uuid "github.com/satori/go.uuid" +) + +type postgresMessagePersistenceService struct { + messagingDBContext *PostgresMessagePersistenceDBContext + messageSerializer contratcs.MessageSerializer + logger logger.Logger +} + +func (m *postgresMessagePersistenceService) Process(messageID string, ctx context.Context) error { + // TODO implement me + panic("implement me") +} + +func (m *postgresMessagePersistenceService) ProcessAll(ctx context.Context) error { + // TODO implement me + panic("implement me") +} + +func (m *postgresMessagePersistenceService) AddPublishMessage( + messageEnvelope types.MessageEnvelopeTMessage, + ctx context.Context, +) error { + // TODO implement me + panic("implement me") +} + +func (m *postgresMessagePersistenceService) AddReceivedMessage( + messageEnvelope types.MessageEnvelope, + ctx context.Context, +) error { + // TODO implement me + panic("implement me") +} + +func (m *postgresMessagePersistenceService) AddMessageCore( + ctx context.Context, + messageEnvelope types.MessageEnvelope, + deliveryType persistmessage.MessageDeliveryType, +) error { + if messageEnvelope.Message == nil { + return errors.New("messageEnvelope.Message is nil") + } + + var id string + switch message := messageEnvelope.Message.(type) { + case types.IMessage: + id = message.GeMessageId() + // case IInternalCommand: + // id = message.InternalCommandId + default: + id = uuid.NewV4().String() + } + + data, err := m.messageSerializer.SerializeEnvelop(messageEnvelope) + if err != nil { + return err + } + + uuidId, err := uuid.FromString(id) + if err != nil { + return err + } + + storeMessage := persistmessage.NewStoreMessage( + uuidId, + messageEnvelope.Message.GetMessageFullTypeName(), + string(data.Data), + deliveryType, + ) + + err := _messagePersistenceRepository.AddAsync(storeMessage, cancellationToken) + if err != nil { + return err + } + + _logger.LogInformation( + "Message with id: %v and delivery type: %v saved in persistence message store", + id, + deliveryType, + ) + + return nil +} + +func NewPostgresMessageService( + postgresMessagePersistenceDBContext *PostgresMessagePersistenceDBContext, + l logger.Logger, +) persistmessage.MessagePersistenceService { + return &postgresMessagePersistenceService{ + messagingDBContext: postgresMessagePersistenceDBContext, + logger: l, + } +} + +func (m *postgresMessagePersistenceService) Add( + ctx context.Context, + storeMessage *persistmessage.StoreMessage, +) error { + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + + // https://gorm.io/docs/create.html + result := dbContext.WithContext(ctx).Create(storeMessage) + if result.Error != nil { + return customErrors.NewConflictErrorWrap( + result.Error, + "storeMessage already exists", + ) + } + + m.logger.Infof("Number of affected rows are: %d", result.RowsAffected) + + return nil +} + +func (m *postgresMessagePersistenceService) Update( + ctx context.Context, + storeMessage *persistmessage.StoreMessage, +) error { + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + + // https://gorm.io/docs/update.html + result := dbContext.WithContext(ctx).Updates(storeMessage) + if result.Error != nil { + return customErrors.NewInternalServerErrorWrap( + result.Error, + "error in updating the storeMessage", + ) + } + + m.logger.Infof("Number of affected rows are: %d", result.RowsAffected) + + return nil +} + +func (m *postgresMessagePersistenceService) ChangeState( + ctx context.Context, + messageID uuid.UUID, + status persistmessage.MessageStatus, +) error { + storeMessage, err := m.GetById(ctx, messageID) + if err != nil { + return customErrors.NewNotFoundErrorWrap( + err, + fmt.Sprintf( + "storeMessage with id `%s` not found in the database", + messageID.String(), + ), + ) + } + + storeMessage.MessageStatus = status + err = m.Update(ctx, storeMessage) + + return err +} + +func (m *postgresMessagePersistenceService) GetAllActive( + ctx context.Context, +) ([]*persistmessage.StoreMessage, error) { + var storeMessages []*persistmessage.StoreMessage + + predicate := func(sm *persistmessage.StoreMessage) bool { + return sm.MessageStatus == persistmessage.Stored + } + + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + result := dbContext.WithContext(ctx).Where(predicate).Find(&storeMessages) + if result.Error != nil { + return nil, result.Error + } + + return storeMessages, nil +} + +func (m *postgresMessagePersistenceService) GetByFilter( + ctx context.Context, + predicate func(*persistmessage.StoreMessage) bool, +) ([]*persistmessage.StoreMessage, error) { + var storeMessages []*persistmessage.StoreMessage + + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + result := dbContext.WithContext(ctx).Where(predicate).Find(&storeMessages) + + if result.Error != nil { + return nil, result.Error + } + + return storeMessages, nil +} + +func (m *postgresMessagePersistenceService) GetById( + ctx context.Context, + id uuid.UUID, +) (*persistmessage.StoreMessage, error) { + var storeMessage *persistmessage.StoreMessage + + // https://gorm.io/docs/query.html#Retrieving-objects-with-primary-key + // https://gorm.io/docs/query.html#Struct-amp-Map-Conditions + // https://gorm.io/docs/query.html#Inline-Condition + // https://gorm.io/docs/advanced_query.html + result := m.messagingDBContext.WithContext(ctx). + Find(&storeMessage, id) + if result.Error != nil { + return nil, customErrors.NewNotFoundErrorWrap( + result.Error, + fmt.Sprintf( + "storeMessage with id `%s` not found in the database", + id.String(), + ), + ) + } + + m.logger.Infof("Number of affected rows are: %d", result.RowsAffected) + + return storeMessage, nil +} + +func (m *postgresMessagePersistenceService) Remove( + ctx context.Context, + storeMessage *persistmessage.StoreMessage, +) (bool, error) { + id := storeMessage.ID + + storeMessage, err := m.GetById(ctx, id) + if err != nil { + return false, customErrors.NewNotFoundErrorWrap( + err, + fmt.Sprintf( + "storeMessage with id `%s` not found in the database", + id.String(), + ), + ) + } + + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + + result := dbContext.WithContext(ctx).Delete(storeMessage, id) + if result.Error != nil { + return false, customErrors.NewInternalServerErrorWrap( + result.Error, + fmt.Sprintf( + "error in deleting storeMessage with id `%s` in the database", + id.String(), + ), + ) + } + + m.logger.Infof("Number of affected rows are: %d", result.RowsAffected) + + return true, nil +} + +func (m *postgresMessagePersistenceService) CleanupMessages( + ctx context.Context, +) error { + predicate := func(sm *persistmessage.StoreMessage) bool { + return sm.MessageStatus == persistmessage.Processed + } + + dbContext := m.messagingDBContext.WithTxIfExists(ctx) + + result := dbContext.WithContext(ctx). + Where(predicate). + Delete(&persistmessage.StoreMessage{}) + + if result.Error != nil { + return result.Error + } + + m.logger.Infof("Number of affected rows are: %d", result.RowsAffected) + + return nil +} diff --git a/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service_test.go b/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service_test.go new file mode 100644 index 00000000..f9da304c --- /dev/null +++ b/internal/pkg/postgresmessaging/messagepersistence/postgres_message_service_test.go @@ -0,0 +1,222 @@ +//go:build unit +// +build unit + +package messagepersistence + +import ( + "context" + "os" + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/persistmessage" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + + "emperror.dev/errors" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/suite" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + "gorm.io/gorm" +) + +type postgresMessageServiceTest struct { + suite.Suite + DB *gorm.DB + logger logger.Logger + messagingRepository persistmessage.MessagePersistenceService + dbContext *PostgresMessagePersistenceDBContext + storeMessages []*persistmessage.StoreMessage + ctx context.Context + dbFilePath string + app *fxtest.App +} + +func TestPostgresMessageService(t *testing.T) { + suite.Run( + t, + &postgresMessageServiceTest{logger: defaultLogger.GetLogger()}, + ) +} + +//func (c *postgresMessageServiceTest) SetupSuite() { +// opts, err := gorm2.NewGormTestContainers(defaultLogger.GetLogger()). +// PopulateContainerOptions(context.Background(), c.T()) +// c.Require().NoError(err) +// +// gormDB, err := postgresgorm.NewGorm(opts) +// c.Require().NoError(err) +// c.DB = gormDB +// +// err = migrationDatabase(gormDB) +// c.Require().NoError(err) +// +// c.dbContext = NewPostgresMessagePersistenceDBContext(gormDB) +// c.messagingRepository = NewPostgresMessageService( +// c.dbContext, +// defaultLogger.GetLogger(), +// ) +//} + +func (c *postgresMessageServiceTest) SetupTest() { + var gormDBContext *PostgresMessagePersistenceDBContext + var gormOptions *postgresgorm.GormOptions + + app := fxtest.New( + c.T(), + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + postgresgorm.Module, + fx.Decorate( + func(cfg *postgresgorm.GormOptions) (*postgresgorm.GormOptions, error) { + // using sql-lite with a database file + cfg.UseSQLLite = true + + return cfg, nil + }, + ), + fx.Provide(NewPostgresMessagePersistenceDBContext), + fx.Populate(&gormDBContext), + fx.Populate(&gormOptions), + ).RequireStart() + + c.dbContext = gormDBContext + c.dbFilePath = gormOptions.Dns() + c.app = app + + c.initDB() +} + +func (c *postgresMessageServiceTest) TearDownTest() { + err := c.cleanupDB() + c.Require().NoError(err) + + mapper.ClearMappings() + + c.app.RequireStop() +} + +//func (c *postgresMessageServiceTest) SetupTest() { +// ctx := context.Background() +// c.ctx = ctx +// p, err := seedData(context.Background(), c.DB) +// c.Require().NoError(err) +// c.storeMessages = p +//} +// +//func (c *postgresMessageServiceTest) TearDownTest() { +// err := c.cleanupPostgresData() +// c.Require().NoError(err) +//} + +func (c *postgresMessageServiceTest) BeginTx() { + c.logger.Info("starting transaction") + tx := c.dbContext.DB().Begin() + gormContext := gormextensions.SetTxToContext(c.ctx, tx) + c.ctx = gormContext +} + +func (c *postgresMessageServiceTest) CommitTx() { + tx := gormextensions.GetTxFromContextIfExists(c.ctx) + if tx != nil { + c.logger.Info("committing transaction") + tx.Commit() + } +} + +func (c *postgresMessageServiceTest) Test_Add() { + message := &persistmessage.StoreMessage{ + ID: uuid.NewV4(), + MessageStatus: persistmessage.Processed, + Data: "test data 3", + DataType: "string", + CreatedAt: time.Now(), + DeliveryType: persistmessage.Outbox, + } + + c.BeginTx() + err := c.messagingRepository.Add(c.ctx, message) + c.CommitTx() + + c.Require().NoError(err) + + m, err := c.messagingRepository.GetById(c.ctx, message.ID) + if err != nil { + return + } + + c.Assert().NotNil(m) + c.Assert().Equal(message.ID, m.ID) +} + +func (c *postgresMessageServiceTest) initDB() { + err := migrateGorm(c.dbContext.DB()) + c.Require().NoError(err) + + storeMessages, err := seedData(c.dbContext.DB()) + c.Require().NoError(err) + + c.storeMessages = storeMessages +} + +func (c *postgresMessageServiceTest) cleanupDB() error { + sqldb, _ := c.dbContext.DB().DB() + e := sqldb.Close() + c.Require().NoError(e) + + // removing sql-lite file + err := os.Remove(c.dbFilePath) + + return err +} + +func migrateGorm(db *gorm.DB) error { + err := db.AutoMigrate(&persistmessage.StoreMessage{}) + if err != nil { + return err + } + + return nil +} + +func seedData( + db *gorm.DB, +) ([]*persistmessage.StoreMessage, error) { + messages := []*persistmessage.StoreMessage{ + { + ID: uuid.NewV4(), + MessageStatus: persistmessage.Processed, + Data: "test data", + DataType: "string", + CreatedAt: time.Now(), + DeliveryType: persistmessage.Outbox, + }, + { + ID: uuid.NewV4(), + MessageStatus: persistmessage.Processed, + Data: "test data 2", + DataType: "string", + CreatedAt: time.Now(), + DeliveryType: persistmessage.Outbox, + }, + } + + // seed data + err := db.CreateInBatches(messages, len(messages)).Error + if err != nil { + return nil, errors.Wrap(err, "error in seed database") + } + + return messages, nil +} diff --git a/internal/pkg/postgresmessaging/postgres_messaging_fx.go b/internal/pkg/postgresmessaging/postgres_messaging_fx.go new file mode 100644 index 00000000..b62b33fa --- /dev/null +++ b/internal/pkg/postgresmessaging/postgres_messaging_fx.go @@ -0,0 +1,24 @@ +package postgresmessaging + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/persistmessage" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresmessaging/messagepersistence" + + "go.uber.org/fx" + "gorm.io/gorm" +) + +var Module = fx.Module( + "postgresmessagingfx", + fx.Provide( + messagepersistence.NewPostgresMessagePersistenceDBContext, + messagepersistence.NewPostgresMessageService, + ), + fx.Invoke(migrateMessaging), +) + +func migrateMessaging(db *gorm.DB) error { + err := db.Migrator().AutoMigrate(&persistmessage.StoreMessage{}) + + return err +} diff --git a/internal/pkg/postgres_pgx/interface.go b/internal/pkg/postgrespgx/interface.go similarity index 100% rename from internal/pkg/postgres_pgx/interface.go rename to internal/pkg/postgrespgx/interface.go diff --git a/internal/pkg/postgres_pgx/postgres.go b/internal/pkg/postgrespgx/postgres.go similarity index 100% rename from internal/pkg/postgres_pgx/postgres.go rename to internal/pkg/postgrespgx/postgres.go diff --git a/internal/pkg/postgres_pgx/postgres_pgx_fx.go b/internal/pkg/postgrespgx/postgres_pgx_fx.go similarity index 100% rename from internal/pkg/postgres_pgx/postgres_pgx_fx.go rename to internal/pkg/postgrespgx/postgres_pgx_fx.go diff --git a/internal/pkg/postgres_pgx/postgres_pgx_options.go b/internal/pkg/postgrespgx/postgres_pgx_options.go similarity index 76% rename from internal/pkg/postgres_pgx/postgres_pgx_options.go rename to internal/pkg/postgrespgx/postgres_pgx_options.go index 936e50dd..61d6dcd4 100644 --- a/internal/pkg/postgres_pgx/postgres_pgx_options.go +++ b/internal/pkg/postgrespgx/postgres_pgx_options.go @@ -2,13 +2,13 @@ package postgres import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[PostgresPgxOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[PostgresPgxOptions]()) type PostgresPgxOptions struct { Host string `mapstructure:"host"` @@ -20,6 +20,6 @@ type PostgresPgxOptions struct { LogLevel int `mapstructure:"logLevel"` } -func provideConfig(environment environemnt.Environment) (*PostgresPgxOptions, error) { +func provideConfig(environment environment.Environment) (*PostgresPgxOptions, error) { return config.BindConfigKey[*PostgresPgxOptions](optionName, environment) } diff --git a/internal/pkg/postgres_sqlx/postgres.go b/internal/pkg/postgressqlx/postgres.go similarity index 100% rename from internal/pkg/postgres_sqlx/postgres.go rename to internal/pkg/postgressqlx/postgres.go diff --git a/internal/pkg/postgres_sqlx/postgres_sqlx_fx.go b/internal/pkg/postgressqlx/postgres_sqlx_fx.go similarity index 100% rename from internal/pkg/postgres_sqlx/postgres_sqlx_fx.go rename to internal/pkg/postgressqlx/postgres_sqlx_fx.go diff --git a/internal/pkg/postgres_sqlx/postgres_sqlx_options.go b/internal/pkg/postgressqlx/postgres_sqlx_options.go similarity index 75% rename from internal/pkg/postgres_sqlx/postgres_sqlx_options.go rename to internal/pkg/postgressqlx/postgres_sqlx_options.go index 79519c97..4794790f 100644 --- a/internal/pkg/postgres_sqlx/postgres_sqlx_options.go +++ b/internal/pkg/postgressqlx/postgres_sqlx_options.go @@ -2,13 +2,13 @@ package postgressqlx import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[PostgresSqlxOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[PostgresSqlxOptions]()) type PostgresSqlxOptions struct { Host string `mapstructure:"host"` @@ -19,6 +19,6 @@ type PostgresSqlxOptions struct { Password string `mapstructure:"password"` } -func provideConfig(environment environemnt.Environment) (*PostgresSqlxOptions, error) { +func provideConfig(environment environment.Environment) (*PostgresSqlxOptions, error) { return config.BindConfigKey[*PostgresSqlxOptions](optionName, environment) } diff --git a/internal/pkg/rabbitmq/bus/rabbitmq-bus.go b/internal/pkg/rabbitmq/bus/rabbitmq-bus.go index 42dec919..958870ad 100644 --- a/internal/pkg/rabbitmq/bus/rabbitmq-bus.go +++ b/internal/pkg/rabbitmq/bus/rabbitmq-bus.go @@ -6,23 +6,20 @@ import ( "reflect" "sync" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" - consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer" consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" - producer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/consumercontracts" producerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/producercontracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/rabbitmqErrors" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "emperror.dev/errors" "github.com/samber/lo" @@ -34,23 +31,21 @@ type RabbitmqBus interface { } type rabbitmqBus struct { - messageTypeConsumers map[reflect.Type][]consumer.Consumer + messageTypeConsumers map[reflect.Type][]consumer2.Consumer producer producer.Producer rabbitmqConfiguration *configurations.RabbitMQConfiguration - rabbitmqConfig *config.RabbitmqOptions rabbitmqConfigBuilder configurations.RabbitMQConfigurationBuilder logger logger.Logger - serializer serializer.EventSerializer - rabbitmqConnection types2.IConnection + consumerFactory consumercontracts.ConsumerFactory + producerFactory producercontracts.ProducerFactory isConsumedNotifications []func(message types.IMessage) isProducedNotifications []func(message types.IMessage) } func NewRabbitmqBus( - cfg *config.RabbitmqOptions, - serializer serializer.EventSerializer, logger logger.Logger, - connection types2.IConnection, + consumerFactory consumercontracts.ConsumerFactory, + producerFactory producercontracts.ProducerFactory, rabbitmqBuilderFunc configurations.RabbitMQConfigurationBuilderFuc, ) (RabbitmqBus, error) { builder := configurations.NewRabbitMQConfigurationBuilder() @@ -61,15 +56,16 @@ func NewRabbitmqBus( rabbitmqConfiguration := builder.Build() rabbitBus := &rabbitmqBus{ logger: logger, - serializer: serializer, rabbitmqConfiguration: rabbitmqConfiguration, - rabbitmqConfig: cfg, + consumerFactory: consumerFactory, + producerFactory: producerFactory, rabbitmqConfigBuilder: builder, - messageTypeConsumers: map[reflect.Type][]consumer.Consumer{}, - rabbitmqConnection: connection, + messageTypeConsumers: map[reflect.Type][]consumer2.Consumer{}, } - producersConfigurationMap := make(map[string]*producerConfigurations.RabbitMQProducerConfiguration) + producersConfigurationMap := make( + map[string]*producerConfigurations.RabbitMQProducerConfiguration, + ) lo.ForEach( rabbitBus.rabbitmqConfiguration.ProducersConfigurations, func(config *producerConfigurations.RabbitMQProducerConfiguration, index int) { @@ -78,7 +74,9 @@ func NewRabbitmqBus( }, ) - consumersConfigurationMap := make(map[string]*consumerConfigurations.RabbitMQConsumerConfiguration) + consumersConfigurationMap := make( + map[string]*consumerConfigurations.RabbitMQConsumerConfiguration, + ) lo.ForEach( rabbitBus.rabbitmqConfiguration.ConsumersConfigurations, func(config *consumerConfigurations.RabbitMQConsumerConfiguration, index int) { @@ -88,11 +86,8 @@ func NewRabbitmqBus( ) for _, consumerConfiguration := range consumersConfigurationMap { - mqConsumer, err := consumer2.NewRabbitMQConsumer( - rabbitBus.rabbitmqConnection, + mqConsumer, err := consumerFactory.CreateConsumer( consumerConfiguration, - rabbitBus.serializer, - rabbitBus.logger, // IsConsumed Notification func(message types.IMessage) { if rabbitBus.isConsumedNotifications != nil { @@ -111,11 +106,8 @@ func NewRabbitmqBus( ) } - mqProducer, err := producer2.NewRabbitMQProducer( - rabbitBus.rabbitmqConnection, + mqProducer, err := producerFactory.CreateProducer( producersConfigurationMap, - rabbitBus.logger, - rabbitBus.serializer, // IsProduced Notification func(message types.IMessage) { if rabbitBus.isProducedNotifications != nil { @@ -144,11 +136,14 @@ func (r *rabbitmqBus) IsProduced(h func(message types.IMessage)) { // ConnectConsumer Add a new consumer to existing message type consumers. if there is no consumer, will create a new consumer for the message type func (r *rabbitmqBus) ConnectConsumer( messageType types.IMessage, - consumer consumer.Consumer, + consumer consumer2.Consumer, ) error { typeName := utils.GetMessageBaseReflectType(messageType) - r.messageTypeConsumers[typeName] = append(r.messageTypeConsumers[typeName], consumer) + r.messageTypeConsumers[typeName] = append( + r.messageTypeConsumers[typeName], + consumer, + ) return nil } @@ -160,16 +155,15 @@ func (r *rabbitmqBus) ConnectRabbitMQConsumer( ) error { typeName := utils.GetMessageBaseReflectType(messageType) - builder := consumerConfigurations.NewRabbitMQConsumerConfigurationBuilder(messageType) + builder := consumerConfigurations.NewRabbitMQConsumerConfigurationBuilder( + messageType, + ) if consumerBuilderFunc != nil { consumerBuilderFunc(builder) } consumerConfig := builder.Build() - mqConsumer, err := consumer2.NewRabbitMQConsumer( - r.rabbitmqConnection, + mqConsumer, err := r.consumerFactory.CreateConsumer( consumerConfig, - r.serializer, - r.logger, // IsConsumed Notification func(message types.IMessage) { if len(r.isConsumedNotifications) > 0 { @@ -185,7 +179,10 @@ func (r *rabbitmqBus) ConnectRabbitMQConsumer( return err } - r.messageTypeConsumers[typeName] = append(r.messageTypeConsumers[typeName], mqConsumer) + r.messageTypeConsumers[typeName] = append( + r.messageTypeConsumers[typeName], + mqConsumer, + ) return nil } @@ -193,7 +190,7 @@ func (r *rabbitmqBus) ConnectRabbitMQConsumer( // ConnectConsumerHandler Add handler to existing consumer. creates new consumer if not exist func (r *rabbitmqBus) ConnectConsumerHandler( messageType types.IMessage, - consumerHandler consumer.ConsumerHandler, + consumerHandler consumer2.ConsumerHandler, ) error { typeName := utils.GetMessageBaseReflectType(messageType) @@ -206,15 +203,12 @@ func (r *rabbitmqBus) ConnectConsumerHandler( } else { // if there is no consumer for a message type, we should create new one and add handler to the consumer consumerBuilder := consumerConfigurations.NewRabbitMQConsumerConfigurationBuilder(messageType) - consumerBuilder.WithHandlers(func(builder consumer.ConsumerHandlerConfigurationBuilder) { + consumerBuilder.WithHandlers(func(builder consumer2.ConsumerHandlerConfigurationBuilder) { builder.AddHandler(consumerHandler) }) consumerConfig := consumerBuilder.Build() - mqConsumer, err := consumer2.NewRabbitMQConsumer( - r.rabbitmqConnection, + mqConsumer, err := r.consumerFactory.CreateConsumer( consumerConfig, - r.serializer, - r.logger, // IsConsumed Notification func(message types.IMessage) { if len(r.isConsumedNotifications) > 0 { @@ -238,7 +232,7 @@ func (r *rabbitmqBus) ConnectConsumerHandler( func (r *rabbitmqBus) Start(ctx context.Context) error { r.logger.Infof( "rabbitmq is running on host: %s", - r.rabbitmqConnection.Raw().LocalAddr().String(), + r.consumerFactory.Connection().Raw().LocalAddr().String(), ) for messageType, consumers := range r.messageTypeConsumers { @@ -286,7 +280,7 @@ func (r *rabbitmqBus) Stop() error { for _, c := range consumers { waitGroup.Add(1) - go func(c consumer.Consumer) { + go func(c consumer2.Consumer) { defer waitGroup.Done() err := c.Stop() @@ -323,5 +317,10 @@ func (r *rabbitmqBus) PublishMessageWithTopicName( meta metadata.Metadata, topicOrExchangeName string, ) error { - return r.producer.PublishMessageWithTopicName(ctx, message, meta, topicOrExchangeName) + return r.producer.PublishMessageWithTopicName( + ctx, + message, + meta, + topicOrExchangeName, + ) } diff --git a/internal/pkg/rabbitmq/bus/rabbitmq_bus_test.go b/internal/pkg/rabbitmq/bus/rabbitmq_bus_test.go index 57258b13..17d83af8 100644 --- a/internal/pkg/rabbitmq/bus/rabbitmq_bus_test.go +++ b/internal/pkg/rabbitmq/bus/rabbitmq_bus_test.go @@ -5,17 +5,19 @@ import ( "fmt" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + pipeline2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + types3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + defaultlogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/factory" + producerfactory "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer" producerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging/consumer" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" @@ -26,42 +28,76 @@ import ( func Test_AddRabbitMQ(t *testing.T) { testUtils.SkipCI(t) + ctx := context.Background() fakeConsumer2 := consumer.NewRabbitMQFakeTestConsumerHandler[*ProducerConsumerMessage]() fakeConsumer3 := consumer.NewRabbitMQFakeTestConsumerHandler[*ProducerConsumerMessage]() - defaultLogger2.SetupDefaultLogger() - serializer := serializer.NewDefaultEventSerializer(json.NewDefaultSerializer()) + serializer := json.NewDefaultEventJsonSerializer( + json.NewDefaultJsonSerializer(), + ) - rabbitmqOptions := &config.RabbitmqOptions{ - RabbitmqHostOptions: &config.RabbitmqHostOptions{ - UserName: "guest", - Password: "guest", - HostName: "localhost", - Port: 5672, - }, + //rabbitmqOptions := &config.RabbitmqOptions{ + // RabbitmqHostOptions: &config.RabbitmqHostOptions{ + // UserName: "guest", + // Password: "guest", + // HostName: "localhost", + // Port: 5672, + // }, + //} + + rabbitmqHostOption, err := rabbitmq.NewRabbitMQTestContainers(defaultlogger.GetLogger()). + PopulateContainerOptions(ctx, t) + require.NoError(t, err) + + options := &config.RabbitmqOptions{ + RabbitmqHostOptions: rabbitmqHostOption, } - conn, err := types.NewRabbitMQConnection(rabbitmqOptions) + + conn, err := types.NewRabbitMQConnection(options) require.NoError(t, err) - b, err := NewRabbitmqBus(rabbitmqOptions, serializer, defaultLogger2.Logger, conn, + consumerFactory := factory.NewConsumerFactory( + options, + conn, + serializer, + defaultlogger.GetLogger(), + ) + producerFactory := producerfactory.NewProducerFactory( + options, + conn, + serializer, + defaultlogger.GetLogger(), + ) + + b, err := NewRabbitmqBus( + defaultlogger.GetLogger(), + consumerFactory, + producerFactory, func(builder configurations.RabbitMQConfigurationBuilder) { builder.AddProducer( ProducerConsumerMessage{}, func(builder producerConfigurations.RabbitMQProducerConfigurationBuilder) { }, ) - builder.AddConsumer(ProducerConsumerMessage{}, + builder.AddConsumer( + ProducerConsumerMessage{}, func(builder consumerConfigurations.RabbitMQConsumerConfigurationBuilder) { builder.WithHandlers(func(consumerHandlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { - consumerHandlerBuilder.AddHandler(NewTestMessageHandler()) - consumerHandlerBuilder.AddHandler(NewTestMessageHandler2()) + consumerHandlerBuilder.AddHandler( + NewTestMessageHandler(), + ) + consumerHandlerBuilder.AddHandler( + NewTestMessageHandler2(), + ) }). - WIthPipelines(func(consumerPipelineBuilder pipeline.ConsumerPipelineConfigurationBuilder) { + WIthPipelines(func(consumerPipelineBuilder pipeline2.ConsumerPipelineConfigurationBuilder) { consumerPipelineBuilder.AddPipeline(NewPipeline1()) }) - }) - }) + }, + ) + }, + ) require.NoError(t, err) @@ -78,7 +114,6 @@ func Test_AddRabbitMQ(t *testing.T) { err = b.ConnectConsumerHandler(&ProducerConsumerMessage{}, fakeConsumer3) require.NoError(t, err) - ctx := context.Background() err = b.Start(ctx) require.NoError(t, err) @@ -86,7 +121,7 @@ func Test_AddRabbitMQ(t *testing.T) { context.Background(), &ProducerConsumerMessage{ Data: "ssssssssss", - Message: types2.NewMessage(uuid.NewV4().String()), + Message: types3.NewMessage(uuid.NewV4().String()), }, nil, ) @@ -101,14 +136,14 @@ func Test_AddRabbitMQ(t *testing.T) { } type ProducerConsumerMessage struct { - *types2.Message + *types3.Message Data string } func NewProducerConsumerMessage(data string) *ProducerConsumerMessage { return &ProducerConsumerMessage{ Data: data, - Message: types2.NewMessage(uuid.NewV4().String()), + Message: types3.NewMessage(uuid.NewV4().String()), } } @@ -121,7 +156,7 @@ func NewTestMessageHandler() *TestMessageHandler { func (t *TestMessageHandler) Handle( ctx context.Context, - consumeContext types2.MessageConsumeContext, + consumeContext types3.MessageConsumeContext, ) error { message := consumeContext.Message().(*ProducerConsumerMessage) fmt.Println(message) @@ -133,7 +168,7 @@ type TestMessageHandler2 struct{} func (t *TestMessageHandler2) Handle( ctx context.Context, - consumeContext types2.MessageConsumeContext, + consumeContext types3.MessageConsumeContext, ) error { message := consumeContext.Message() fmt.Println(message) @@ -148,20 +183,23 @@ func NewTestMessageHandler2() *TestMessageHandler2 { // /////////////// ConsumerPipeline type Pipeline1 struct{} -func NewPipeline1() pipeline.ConsumerPipeline { +func NewPipeline1() pipeline2.ConsumerPipeline { return &Pipeline1{} } -func (p Pipeline1) Handle( +func (p *Pipeline1) Handle( ctx context.Context, - consumerContext types2.MessageConsumeContext, - next pipeline.ConsumerHandlerFunc, + consumerContext types3.MessageConsumeContext, + next pipeline2.ConsumerHandlerFunc, ) error { fmt.Println("PipelineBehaviourTest.Handled") - fmt.Printf("pipeline got a message with id '%s'", consumerContext.Message().GeMessageId()) + fmt.Printf( + "pipeline got a message with id '%s'", + consumerContext.Message().GeMessageId(), + ) - err := next() + err := next(ctx) if err != nil { return err } diff --git a/internal/pkg/rabbitmq/config/rabbitmq_options.go b/internal/pkg/rabbitmq/config/rabbitmq_options.go index fa3bb8f7..25cbaaee 100644 --- a/internal/pkg/rabbitmq/config/rabbitmq_options.go +++ b/internal/pkg/rabbitmq/config/rabbitmq_options.go @@ -5,8 +5,8 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) @@ -38,8 +38,8 @@ func (h *RabbitmqHostOptions) HttpEndPoint() string { return fmt.Sprintf("http://%s:%d", h.HostName, h.HttpPort) } -func ProvideConfig(environment environemnt.Environment) (*RabbitmqOptions, error) { - optionName := strcase.ToLowerCamel(typeMapper.GetTypeNameByT[RabbitmqOptions]()) +func ProvideConfig(environment environment.Environment) (*RabbitmqOptions, error) { + optionName := strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[RabbitmqOptions]()) cfg, err := config.BindConfigKey[*RabbitmqOptions](optionName, environment) return cfg, err diff --git a/internal/pkg/rabbitmq/configurations/rabbitmq_configuration_builder.go b/internal/pkg/rabbitmq/configurations/rabbitmq_configuration_builder.go index b60fbf51..974dfb65 100644 --- a/internal/pkg/rabbitmq/configurations/rabbitmq_configuration_builder.go +++ b/internal/pkg/rabbitmq/configurations/rabbitmq_configuration_builder.go @@ -1,7 +1,7 @@ package configurations import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" producerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" diff --git a/internal/pkg/rabbitmq/consumer/configurations/consumer_connector.go b/internal/pkg/rabbitmq/consumer/configurations/consumer_connector.go index 23b05c6e..89381a6d 100644 --- a/internal/pkg/rabbitmq/consumer/configurations/consumer_connector.go +++ b/internal/pkg/rabbitmq/consumer/configurations/consumer_connector.go @@ -1,8 +1,8 @@ package configurations import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type RabbitMQConsumerConnector interface { diff --git a/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration.go b/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration.go index df8ccdc0..69bdbf6f 100644 --- a/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration.go +++ b/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration.go @@ -4,10 +4,10 @@ import ( "fmt" "reflect" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/options" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" ) @@ -16,8 +16,8 @@ type RabbitMQConsumerConfiguration struct { Name string ConsumerMessageType reflect.Type Pipelines []pipeline.ConsumerPipeline - Handlers []consumer.ConsumerHandler - *consumer.ConsumerOptions + Handlers []consumer2.ConsumerHandler + *consumer2.ConsumerOptions ConcurrencyLimit int // The prefetch count tells the Rabbit connection how many messages to retrieve from the server per request. PrefetchCount int @@ -35,7 +35,7 @@ func NewDefaultRabbitMQConsumerConfiguration( name := fmt.Sprintf("%s_consumer", utils.GetMessageName(messageType)) return &RabbitMQConsumerConfiguration{ - ConsumerOptions: &consumer.ConsumerOptions{ExitOnError: false, ConsumerId: ""}, + ConsumerOptions: &consumer2.ConsumerOptions{ExitOnError: false, ConsumerId: ""}, ConcurrencyLimit: 1, PrefetchCount: 4, // how many messages we can handle at once NoLocal: false, diff --git a/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration_builder.go b/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration_builder.go index 1e729899..df111bf0 100644 --- a/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration_builder.go +++ b/internal/pkg/rabbitmq/consumer/configurations/rabbitmq_consumer_configuration_builder.go @@ -1,9 +1,9 @@ package configurations import ( - messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" ) diff --git a/internal/pkg/rabbitmq/consumer/consumercontracts/consumer_factory.go b/internal/pkg/rabbitmq/consumer/consumercontracts/consumer_factory.go new file mode 100644 index 00000000..9eb75290 --- /dev/null +++ b/internal/pkg/rabbitmq/consumer/consumercontracts/consumer_factory.go @@ -0,0 +1,17 @@ +package consumercontracts + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + messagingTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" +) + +type ConsumerFactory interface { + CreateConsumer( + consumerConfiguration *configurations.RabbitMQConsumerConfiguration, + isConsumedNotifications ...func(message messagingTypes.IMessage), + ) (consumer.Consumer, error) + + Connection() types.IConnection +} diff --git a/internal/pkg/rabbitmq/consumer/factory/consumer_factory.go b/internal/pkg/rabbitmq/consumer/factory/consumer_factory.go new file mode 100644 index 00000000..70447c6c --- /dev/null +++ b/internal/pkg/rabbitmq/consumer/factory/consumer_factory.go @@ -0,0 +1,51 @@ +package factory + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" + rabbitmqconsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer" + consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/consumercontracts" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" +) + +type consumerFactory struct { + connection types2.IConnection + eventSerializer serializer.EventSerializer + logger logger.Logger + rabbitmqOptions *config.RabbitmqOptions +} + +func NewConsumerFactory( + rabbitmqOptions *config.RabbitmqOptions, + connection types2.IConnection, + eventSerializer serializer.EventSerializer, + l logger.Logger, +) consumercontracts.ConsumerFactory { + return &consumerFactory{ + rabbitmqOptions: rabbitmqOptions, + logger: l, + eventSerializer: eventSerializer, + connection: connection, + } +} + +func (c *consumerFactory) CreateConsumer( + consumerConfiguration *consumerConfigurations.RabbitMQConsumerConfiguration, + isConsumedNotifications ...func(message types.IMessage), +) (consumer.Consumer, error) { + return rabbitmqconsumer.NewRabbitMQConsumer( + c.rabbitmqOptions, + c.connection, + consumerConfiguration, + c.eventSerializer, + c.logger, + isConsumedNotifications...) +} + +func (c *consumerFactory) Connection() types2.IConnection { + return c.connection +} diff --git a/internal/pkg/rabbitmq/consumer/rabbitmq_consumer.go b/internal/pkg/rabbitmq/consumer/rabbitmq_consumer.go index b7e152fb..fe37142f 100644 --- a/internal/pkg/rabbitmq/consumer/rabbitmq_consumer.go +++ b/internal/pkg/rabbitmq/consumer/rabbitmq_consumer.go @@ -6,18 +6,19 @@ import ( "reflect" "time" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + consumertracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/otel/tracing/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + messagingTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - consumeTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - messagingTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/rabbitmqErrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" + errorutils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" "emperror.dev/errors" "github.com/ahmetb/go-linq/v3" @@ -44,8 +45,9 @@ type rabbitMQConsumer struct { handlerDefault consumer.ConsumerHandler channel *amqp091.Channel deliveryRoutines chan struct{} // chan should init before using channel - eventSerializer serializer.EventSerializer + messageSerializer contratcs.MessageSerializer logger logger.Logger + rabbitmqOptions *config.RabbitmqOptions ErrChan chan error handlers []consumer.ConsumerHandler pipelines []pipeline.ConsumerPipeline @@ -54,9 +56,10 @@ type rabbitMQConsumer struct { // NewRabbitMQConsumer create a new generic RabbitMQ consumer func NewRabbitMQConsumer( + rabbitmqOptions *config.RabbitmqOptions, connection types.IConnection, consumerConfiguration *configurations.RabbitMQConsumerConfiguration, - eventSerializer serializer.EventSerializer, + messageSerializer contratcs.MessageSerializer, logger logger.Logger, isConsumedNotifications ...func(message messagingTypes.IMessage), ) (consumer.Consumer, error) { @@ -65,12 +68,18 @@ func NewRabbitMQConsumer( } if consumerConfiguration.ConsumerMessageType == nil { - return nil, errors.New("consumer ConsumerMessageType property is required") + return nil, errors.New( + "consumer ConsumerMessageType property is required", + ) } - deliveryRoutines := make(chan struct{}, consumerConfiguration.ConcurrencyLimit) + deliveryRoutines := make( + chan struct{}, + consumerConfiguration.ConcurrencyLimit, + ) cons := &rabbitMQConsumer{ - eventSerializer: eventSerializer, + messageSerializer: messageSerializer, + rabbitmqOptions: rabbitmqOptions, logger: logger, rabbitmqConsumerOptions: consumerConfiguration, deliveryRoutines: deliveryRoutines, @@ -254,7 +263,7 @@ func (r *rabbitMQConsumer) GetName() string { func (r *rabbitMQConsumer) reConsumeOnDropConnection(ctx context.Context) { go func() { - defer errorUtils.HandlePanic() + defer errorutils.HandlePanic() for { select { case reconnect := <-r.connection.ReconnectedChannel(): @@ -262,10 +271,15 @@ func (r *rabbitMQConsumer) reConsumeOnDropConnection(ctx context.Context) { r.logger.Info("reconsume_on_drop_connection started") err := r.Start(ctx) if err != nil { - r.logger.Error("reconsume_on_drop_connection finished with error: %v", err) + r.logger.Error( + "reconsume_on_drop_connection finished with error: %v", + err, + ) continue } - r.logger.Info("reconsume_on_drop_connection finished successfully") + r.logger.Info( + "reconsume_on_drop_connection finished successfully", + ) return } } @@ -273,7 +287,10 @@ func (r *rabbitMQConsumer) reConsumeOnDropConnection(ctx context.Context) { }() } -func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091.Delivery) { +func (r *rabbitMQConsumer) handleReceived( + ctx context.Context, + delivery amqp091.Delivery, +) { // for ensuring our handlers execute completely after shutdown r.deliveryRoutines <- struct{}{} @@ -284,7 +301,7 @@ func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091. meta = metadata.MapToMetadata(delivery.Headers) } - consumerTraceOption := &consumeTracing.ConsumerTracingOptions{ + consumerTraceOption := &consumertracing.ConsumerTracingOptions{ MessagingSystem: "rabbitmq", DestinationKind: "queue", Destination: r.rabbitmqConsumerOptions.QueueOptions.Name, @@ -292,7 +309,7 @@ func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091. semconv.MessagingRabbitmqDestinationRoutingKey(delivery.RoutingKey), }, } - ctx, beforeConsumeSpan := consumeTracing.StartConsumerSpan( + ctx, beforeConsumeSpan := consumertracing.StartConsumerSpan( ctx, &meta, string(delivery.Body), @@ -301,7 +318,9 @@ func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091. consumeContext, err := r.createConsumeContext(delivery) if err != nil { - r.logger.Error(consumeTracing.FinishConsumerSpan(beforeConsumeSpan, err)) + r.logger.Error( + consumertracing.FinishConsumerSpan(beforeConsumeSpan, err), + ) return } @@ -314,11 +333,11 @@ func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091. if err := delivery.Ack(false); err != nil { r.logger.Error( "error sending ACK to RabbitMQ consumer: %v", - consumeTracing.FinishConsumerSpan(beforeConsumeSpan, err), + consumertracing.FinishConsumerSpan(beforeConsumeSpan, err), ) return } - _ = consumeTracing.FinishConsumerSpan(beforeConsumeSpan, nil) + _ = consumertracing.FinishConsumerSpan(beforeConsumeSpan, nil) if len(r.isConsumedNotifications) > 0 { for _, notification := range r.isConsumedNotifications { if notification != nil { @@ -332,11 +351,11 @@ func (r *rabbitMQConsumer) handleReceived(ctx context.Context, delivery amqp091. if err := delivery.Nack(false, true); err != nil { r.logger.Error( "error in sending Nack to RabbitMQ consumer: %v", - consumeTracing.FinishConsumerSpan(beforeConsumeSpan, err), + consumertracing.FinishConsumerSpan(beforeConsumeSpan, err), ) return } - _ = consumeTracing.FinishConsumerSpan(beforeConsumeSpan, nil) + _ = consumertracing.FinishConsumerSpan(beforeConsumeSpan, nil) } } @@ -379,7 +398,7 @@ func (r *rabbitMQConsumer) runHandlersWithRetry( if r.pipelines != nil && len(r.pipelines) > 0 { reversPipes := r.reversOrder(r.pipelines) - lastHandler = func() error { + lastHandler = func(ctx context.Context) error { handler := handler.(consumer.ConsumerHandler) return handler.Handle(ctx, messageConsumeContext) } @@ -389,10 +408,14 @@ func (r *rabbitMQConsumer) runHandlersWithRetry( pipeValue := pipe nexValue := next - var handlerFunc pipeline.ConsumerHandlerFunc = func() error { + var handlerFunc pipeline.ConsumerHandlerFunc = func(ctx context.Context) error { genericContext, ok := messageConsumeContext.(messagingTypes.MessageConsumeContext) if ok { - return pipeValue.Handle(ctx, genericContext, nexValue) + return pipeValue.Handle( + ctx, + genericContext, + nexValue, + ) } return pipeValue.Handle( ctx, @@ -404,9 +427,12 @@ func (r *rabbitMQConsumer) runHandlersWithRetry( }) v := aggregateResult.(pipeline.ConsumerHandlerFunc) - err := v() + err := v(ctx) if err != nil { - return errors.Wrap(err, "error handling consumer handlers pipeline") + return errors.Wrap( + err, + "error handling consumer handlers pipeline", + ) } return nil } else { @@ -424,21 +450,11 @@ func (r *rabbitMQConsumer) runHandlersWithRetry( func (r *rabbitMQConsumer) createConsumeContext( delivery amqp091.Delivery, ) (messagingTypes.MessageConsumeContext, error) { - message := r.deserializeData(delivery.ContentType, delivery.Type, delivery.Body) - if reflect.ValueOf(message).IsZero() || reflect.ValueOf(message).IsNil() { - return *new(messagingTypes.MessageConsumeContext), errors.New( - "error in deserialization of payload", - ) - } - m, ok := message.(messagingTypes.IMessage) - if !ok || m.IsMessage() == false { - return nil, errors.New( - fmt.Sprintf( - "message %s is not a message type or message property is nil", - utils.GetMessageBaseReflectType(message), - ), - ) - } + message := r.deserializeData( + delivery.ContentType, + delivery.Type, + delivery.Body, + ) var meta metadata.Metadata if delivery.Headers != nil { @@ -446,7 +462,7 @@ func (r *rabbitMQConsumer) createConsumeContext( } consumeContext := messagingTypes.NewMessageConsumeContext( - message.(messagingTypes.IMessage), + message, meta, delivery.ContentType, delivery.Type, @@ -462,7 +478,7 @@ func (r *rabbitMQConsumer) deserializeData( contentType string, eventType string, body []byte, -) interface{} { +) messagingTypes.IMessage { if contentType == "" { contentType = "application/json" } @@ -474,15 +490,18 @@ func (r *rabbitMQConsumer) deserializeData( if contentType == "application/json" { // r.rabbitmqConsumerOptions.ConsumerMessageType --> actual type - // deserialize, err := r.eventSerializer.DeserializeType(body, r.rabbitmqConsumerOptions.ConsumerMessageType, contentType) - deserialize, err := r.eventSerializer.DeserializeMessage( + // deserialize, err := r.messageSerializer.DeserializeType(body, r.rabbitmqConsumerOptions.ConsumerMessageType, contentType) + deserialize, err := r.messageSerializer.Deserialize( body, eventType, contentType, ) // or this to explicit type deserialization if err != nil { r.logger.Errorf( - fmt.Sprintf("error in deserilizng of type '%s' in the consumer", eventType), + fmt.Sprintf( + "error in deserilizng of type '%s' in the consumer", + eventType, + ), ) return nil } diff --git a/internal/pkg/rabbitmq/consumer/rabbitmq_consumer_test.go b/internal/pkg/rabbitmq/consumer/rabbitmq_consumer_test.go index afeffa1e..c04b068f 100644 --- a/internal/pkg/rabbitmq/consumer/rabbitmq_consumer_test.go +++ b/internal/pkg/rabbitmq/consumer/rabbitmq_consumer_test.go @@ -4,131 +4,125 @@ import ( "context" "fmt" "testing" + "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/pipeline" + types3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/pipeline" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + defaultLogger2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" + rabbitmqConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/factory" + producerfactory "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging/consumer" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/require" ) -func Test_Consume_Message(t *testing.T) { +func Test_Consumer_With_Fake_Message(t *testing.T) { testUtils.SkipCI(t) - defer errorUtils.HandlePanic() ctx := context.Background() - defaultLogger2.SetupDefaultLogger() - eventSerializer := serializer.NewDefaultEventSerializer(json.NewDefaultSerializer()) - - tp, err := tracing.NewOtelTracing(&config2.OpenTelemetryOptions{ - ServiceName: "test", - Enabled: true, - AlwaysOnSampler: true, - JaegerExporterOptions: &config2.JaegerExporterOptions{ - AgentHost: "localhost", - AgentPort: "6831", - }, - ZipkinExporterOptions: &config2.ZipkinExporterOptions{ - Url: "http://localhost:9411/api/v2/spans", - }, - }, environemnt.Development) + + //options := &config.RabbitmqOptions{ + // RabbitmqHostOptions: &config.RabbitmqHostOptions{ + // UserName: "guest", + // Password: "guest", + // HostName: "localhost", + // Port: 5672, + // }, + //} + + rabbitmqHostOption, err := rabbitmq.NewRabbitMQTestContainers(defaultLogger2.GetLogger()). + PopulateContainerOptions(ctx, t) require.NoError(t, err) - defer tp.TracerProvider.Shutdown(ctx) + options := &config.RabbitmqOptions{ + RabbitmqHostOptions: rabbitmqHostOption, + } - conn, err := types.NewRabbitMQConnection(&config.RabbitmqOptions{ - RabbitmqHostOptions: &config.RabbitmqHostOptions{ - UserName: "guest", - Password: "guest", - HostName: "localhost", - Port: 5672, - }, - }) + conn, err := types.NewRabbitMQConnection(options) require.NoError(t, err) - fakeHandler := consumer.NewRabbitMQFakeTestConsumerHandler[ProducerConsumerMessage]() - builder := configurations.NewRabbitMQConsumerConfigurationBuilder(ProducerConsumerMessage{}) - builder.WithHandlers( - func(consumerHandlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { - consumerHandlerBuilder.AddHandler(NewTestMessageHandler()) - consumerHandlerBuilder.AddHandler(fakeHandler) - }, - ) - rabbitmqConsumer, err := NewRabbitMQConsumer( + eventSerializer := json.NewDefaultEventJsonSerializer( + json.NewDefaultJsonSerializer(), + ) + consumerFactory := factory.NewConsumerFactory( + options, conn, - builder.Build(), eventSerializer, - defaultLogger2.Logger, + defaultLogger2.GetLogger(), + ) + producerFactory := producerfactory.NewProducerFactory( + options, + conn, + eventSerializer, + defaultLogger2.GetLogger(), ) - require.NoError(t, err) - if rabbitmqConsumer == nil { - t.Log("RabbitMQ consumer is nil") - return - } - err = rabbitmqConsumer.Start(ctx) - if err != nil { - rabbitmqConsumer.Stop() - } - require.NoError(t, err) + fakeHandler := consumer.NewRabbitMQFakeTestConsumerHandler[ProducerConsumerMessage]() + + rabbitmqBus, err := bus.NewRabbitmqBus( + defaultLogger2.GetLogger(), + consumerFactory, + producerFactory, + func(builder rabbitmqConfigurations.RabbitMQConfigurationBuilder) { + builder.AddConsumer( + ProducerConsumerMessage{}, + func(consumerBuilder configurations.RabbitMQConsumerConfigurationBuilder) { + consumerBuilder.WithHandlers( + func(consumerHandlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { + consumerHandlerBuilder.AddHandler(fakeHandler) + }, + ) + }, + ) + }, + ) + + rabbitmqBus.Start(ctx) + defer rabbitmqBus.Stop() + + time.Sleep(time.Second * 1) - rabbitmqProducer, err := producer.NewRabbitMQProducer( - conn, - nil, - defaultLogger2.Logger, - eventSerializer) require.NoError(t, err) - //time.Sleep(time.Second * 5) - // - //fmt.Println("closing connection") - //conn.Close() - //fmt.Println(conn.IsClosed()) - // - //time.Sleep(time.Second * 10) - //fmt.Println("after 10 second of closing connection") - //fmt.Println(conn.IsClosed()) - - err = rabbitmqProducer.PublishMessage(ctx, NewProducerConsumerMessage("test"), nil) + err = rabbitmqBus.PublishMessage( + ctx, + NewProducerConsumerMessage("test"), + nil, + ) for err != nil { - err = rabbitmqProducer.PublishMessage(ctx, NewProducerConsumerMessage("test"), nil) + err = rabbitmqBus.PublishMessage( + ctx, + NewProducerConsumerMessage("test"), + nil, + ) } err = testUtils.WaitUntilConditionMet(func() bool { return fakeHandler.IsHandled() }) - require.NoError(t, err) - - rabbitmqConsumer.Stop() - conn.Close() - fmt.Println(conn.IsClosed()) - fmt.Println(conn.IsConnected()) + require.NoError(t, err) } type ProducerConsumerMessage struct { - *types2.Message + *types3.Message Data string } func NewProducerConsumerMessage(data string) *ProducerConsumerMessage { return &ProducerConsumerMessage{ Data: data, - Message: types2.NewMessage(uuid.NewV4().String()), + Message: types3.NewMessage(uuid.NewV4().String()), } } @@ -137,7 +131,7 @@ type TestMessageHandler struct{} func (t *TestMessageHandler) Handle( ctx context.Context, - consumeContext types2.MessageConsumeContext, + consumeContext types3.MessageConsumeContext, ) error { message := consumeContext.Message().(*ProducerConsumerMessage) fmt.Println(message) @@ -153,7 +147,7 @@ type TestMessageHandler2 struct{} func (t *TestMessageHandler2) Handle( ctx context.Context, - consumeContext types2.MessageConsumeContext, + consumeContext types3.MessageConsumeContext, ) error { message := consumeContext.Message() fmt.Println(message) @@ -174,16 +168,19 @@ func NewPipeline1() pipeline.ConsumerPipeline { func (p Pipeline1) Handle( ctx context.Context, - consumerContext types2.MessageConsumeContext, + consumerContext types3.MessageConsumeContext, next pipeline.ConsumerHandlerFunc, ) error { fmt.Println("PipelineBehaviourTest.Handled") fmt.Println( - fmt.Sprintf("pipeline got a message with id '%s'", consumerContext.Message().GeMessageId()), + fmt.Sprintf( + "pipeline got a message with id '%s'", + consumerContext.Message().GeMessageId(), + ), ) - err := next() + err := next(ctx) if err != nil { return err } diff --git a/internal/pkg/rabbitmq/health.go b/internal/pkg/rabbitmq/health.go index 9650ad34..f5289f14 100644 --- a/internal/pkg/rabbitmq/health.go +++ b/internal/pkg/rabbitmq/health.go @@ -3,7 +3,7 @@ package rabbitmq import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" "emperror.dev/errors" @@ -13,7 +13,7 @@ type gormHealthChecker struct { connection types.IConnection } -func NewRabbitMQHealthChecker(connection types.IConnection) health.Health { +func NewRabbitMQHealthChecker(connection types.IConnection) contracts.Health { return &gormHealthChecker{connection} } diff --git a/internal/pkg/rabbitmq/producer/configurations/rabbitmq_producer_configuration.go b/internal/pkg/rabbitmq/producer/configurations/rabbitmq_producer_configuration.go index e30747f1..c0aaef52 100644 --- a/internal/pkg/rabbitmq/producer/configurations/rabbitmq_producer_configuration.go +++ b/internal/pkg/rabbitmq/producer/configurations/rabbitmq_producer_configuration.go @@ -3,8 +3,8 @@ package configurations import ( "reflect" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/options" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" ) diff --git a/internal/pkg/rabbitmq/producer/configurations/rabitmq_producer_configuration_builder.go b/internal/pkg/rabbitmq/producer/configurations/rabitmq_producer_configuration_builder.go index 80145b0c..c14541e4 100644 --- a/internal/pkg/rabbitmq/producer/configurations/rabitmq_producer_configuration_builder.go +++ b/internal/pkg/rabbitmq/producer/configurations/rabitmq_producer_configuration_builder.go @@ -1,7 +1,7 @@ package configurations import ( - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" ) diff --git a/internal/pkg/rabbitmq/producer/producer_factory.go b/internal/pkg/rabbitmq/producer/producer_factory.go new file mode 100644 index 00000000..d01b9283 --- /dev/null +++ b/internal/pkg/rabbitmq/producer/producer_factory.go @@ -0,0 +1,46 @@ +package producer + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" + producerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/producercontracts" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" +) + +type producerFactory struct { + connection types2.IConnection + logger logger.Logger + eventSerializer serializer.EventSerializer + rabbitmqOptions *config.RabbitmqOptions +} + +func NewProducerFactory( + rabbitmqOptions *config.RabbitmqOptions, + connection types2.IConnection, + eventSerializer serializer.EventSerializer, + l logger.Logger, +) producercontracts.ProducerFactory { + return &producerFactory{ + rabbitmqOptions: rabbitmqOptions, + logger: l, + connection: connection, + eventSerializer: eventSerializer, + } +} + +func (p *producerFactory) CreateProducer( + rabbitmqProducersConfiguration map[string]*producerConfigurations.RabbitMQProducerConfiguration, + isProducedNotifications ...func(message types.IMessage), +) (producer.Producer, error) { + return NewRabbitMQProducer( + p.rabbitmqOptions, + p.connection, + rabbitmqProducersConfiguration, + p.logger, + p.eventSerializer, + isProducedNotifications...) +} diff --git a/internal/pkg/rabbitmq/producer/producercontracts/producer_factory.go b/internal/pkg/rabbitmq/producer/producercontracts/producer_factory.go new file mode 100644 index 00000000..f34ee7d2 --- /dev/null +++ b/internal/pkg/rabbitmq/producer/producercontracts/producer_factory.go @@ -0,0 +1,14 @@ +package producercontracts + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" +) + +type ProducerFactory interface { + CreateProducer( + rabbitmqProducersConfiguration map[string]*configurations.RabbitMQProducerConfiguration, + isProducedNotifications ...func(message types2.IMessage), + ) (producer.Producer, error) +} diff --git a/internal/pkg/rabbitmq/producer/rabbitmq_producer.go b/internal/pkg/rabbitmq/producer/rabbitmq_producer.go index b9c864f3..530fb436 100644 --- a/internal/pkg/rabbitmq/producer/rabbitmq_producer.go +++ b/internal/pkg/rabbitmq/producer/rabbitmq_producer.go @@ -2,20 +2,19 @@ package producer import ( "context" - "fmt" "time" + messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/messageheader" + producer3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/otel/tracing/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/contratcs" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - messageHeader "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/message_header" - producer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" "emperror.dev/errors" "github.com/rabbitmq/amqp091-go" @@ -26,23 +25,26 @@ import ( type rabbitMQProducer struct { logger logger.Logger + rabbitmqOptions *config.RabbitmqOptions connection types.IConnection - eventSerializer serializer.EventSerializer + messageSerializer contratcs.MessageSerializer producersConfigurations map[string]*configurations.RabbitMQProducerConfiguration isProducedNotifications []func(message types2.IMessage) } func NewRabbitMQProducer( + cfg *config.RabbitmqOptions, connection types.IConnection, rabbitmqProducersConfiguration map[string]*configurations.RabbitMQProducerConfiguration, logger logger.Logger, - eventSerializer serializer.EventSerializer, + eventSerializer contratcs.MessageSerializer, isProducedNotifications ...func(message types2.IMessage), ) (producer.Producer, error) { p := &rabbitMQProducer{ logger: logger, + rabbitmqOptions: cfg, connection: connection, - eventSerializer: eventSerializer, + messageSerializer: eventSerializer, producersConfigurations: rabbitmqProducersConfiguration, } @@ -76,19 +78,12 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( meta metadata.Metadata, topicOrExchangeName string, ) error { - if message.IsMessage() == false { - return errors.New( - fmt.Sprintf( - "message %s is not a message type or message property is nil", - utils.GetMessageBaseReflectType(message), - ), - ) - } - producerConfiguration := r.getProducerConfigurationByMessage(message) if producerConfiguration == nil { - producerConfiguration = configurations.NewDefaultRabbitMQProducerConfiguration(message) + producerConfiguration = configurations.NewDefaultRabbitMQProducerConfiguration( + message, + ) } var exchange string @@ -110,7 +105,7 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( meta = r.getMetadata(message, meta) - producerOptions := &producer2.ProducerTracingOptions{ + producerOptions := &producer3.ProducerTracingOptions{ MessagingSystem: "rabbitmq", DestinationKind: "exchange", Destination: exchange, @@ -119,12 +114,12 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( }, } - serializedObj, err := r.eventSerializer.Serialize(message) + serializedObj, err := r.messageSerializer.Serialize(message) if err != nil { return err } - ctx, beforeProduceSpan := producer2.StartProducerSpan( + ctx, beforeProduceSpan := producer3.StartProducerSpan( ctx, message, &meta, @@ -134,11 +129,14 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( // https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/go/publisher_confirms.go if r.connection == nil { - return producer2.FinishProducerSpan(beforeProduceSpan, errors.New("connection is nil")) + return producer3.FinishProducerSpan( + beforeProduceSpan, + errors.New("connection is nil"), + ) } if r.connection.IsClosed() { - return producer2.FinishProducerSpan( + return producer3.FinishProducerSpan( beforeProduceSpan, errors.New("connection is closed, wait for connection alive"), ) @@ -147,17 +145,17 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( // create a unique channel on the connection and in the end close the channel channel, err := r.connection.Channel() if err != nil { - return producer2.FinishProducerSpan(beforeProduceSpan, err) + return producer3.FinishProducerSpan(beforeProduceSpan, err) } defer channel.Close() err = r.ensureExchange(producerConfiguration, channel, exchange) if err != nil { - return producer2.FinishProducerSpan(beforeProduceSpan, err) + return producer3.FinishProducerSpan(beforeProduceSpan, err) } if err := channel.Confirm(false); err != nil { - return producer2.FinishProducerSpan(beforeProduceSpan, err) + return producer3.FinishProducerSpan(beforeProduceSpan, err) } confirms := make(chan amqp091.Confirmation) @@ -168,7 +166,7 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( MessageId: message.GeMessageId(), Timestamp: time.Now(), Headers: metadata.MetadataToMap(meta), - Type: message.GetEventTypeName(), // typeMapper.GetTypeName(message) - just message type name not full type name because in other side package name for type could be different + Type: message.GetMessageTypeName(), // typeMapper.GetTypeName(message) - just message type name not full type name because in other side package name for type could be different ContentType: serializedObj.ContentType, Body: serializedObj.Data, DeliveryMode: producerConfiguration.DeliveryMode, @@ -188,11 +186,14 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( props, ) if err != nil { - return producer2.FinishProducerSpan(beforeProduceSpan, err) + return producer3.FinishProducerSpan(beforeProduceSpan, err) } if confirmed := <-confirms; !confirmed.Ack { - return producer2.FinishProducerSpan(beforeProduceSpan, errors.New("ack not confirmed")) + return producer3.FinishProducerSpan( + beforeProduceSpan, + errors.New("ack not confirmed"), + ) } if len(r.isProducedNotifications) > 0 { @@ -203,7 +204,7 @@ func (r *rabbitMQProducer) PublishMessageWithTopicName( } } - return producer2.FinishProducerSpan(beforeProduceSpan, err) + return producer3.FinishProducerSpan(beforeProduceSpan, err) } func (r *rabbitMQProducer) getMetadata( @@ -212,13 +213,9 @@ func (r *rabbitMQProducer) getMetadata( ) metadata.Metadata { meta = metadata.FromMetadata(meta) - if message.GetEventTypeName() == "" { - message.SetEventTypeName( - typeMapper.GetTypeName(message), - ) // just message type name not full type name because in other side package name for type could be different) - } - messageHeader.SetMessageType(meta, message.GetEventTypeName()) - messageHeader.SetMessageContentType(meta, r.eventSerializer.ContentType()) + // just message type name not full type name because in other side package name for type could be different + messageHeader.SetMessageType(meta, message.GetMessageTypeName()) + messageHeader.SetMessageContentType(meta, r.messageSerializer.ContentType()) if messageHeader.GetMessageId(meta) == "" { messageHeader.SetMessageId(meta, message.GeMessageId()) diff --git a/internal/pkg/rabbitmq/producer/rabbitmq_producer_test.go b/internal/pkg/rabbitmq/producer/rabbitmq_producer_test.go index 22d89d6f..ea3b367f 100644 --- a/internal/pkg/rabbitmq/producer/rabbitmq_producer_test.go +++ b/internal/pkg/rabbitmq/producer/rabbitmq_producer_test.go @@ -4,15 +4,14 @@ import ( "context" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - types2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/config" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/rabbitmq" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" uuid "github.com/satori/go.uuid" @@ -22,43 +21,56 @@ import ( func Test_Publish_Message(t *testing.T) { testUtils.SkipCI(t) - defaultLogger.SetupDefaultLogger() - eventSerializer := serializer.NewDefaultEventSerializer(json.NewDefaultSerializer()) + eventSerializer := json.NewDefaultEventJsonSerializer( + json.NewDefaultJsonSerializer(), + ) ctx := context.Background() tp, err := tracing.NewOtelTracing( - &config2.OpenTelemetryOptions{ + &tracing.TracingOptions{ ServiceName: "test", Enabled: true, AlwaysOnSampler: true, - JaegerExporterOptions: &config2.JaegerExporterOptions{ - AgentHost: "localhost", - AgentPort: "6831", + ZipkinExporterOptions: &tracing.ZipkinExporterOptions{ + Url: "http://localhost:9411/api/v2/spans", }, }, - environemnt.Development, + environment.Development, ) if err != nil { return } - defer tp.TracerProvider.Shutdown(ctx) - - conn, err := types.NewRabbitMQConnection(&config.RabbitmqOptions{ - RabbitmqHostOptions: &config.RabbitmqHostOptions{ - UserName: "guest", - Password: "guest", - HostName: "localhost", - Port: 5672, - }, - }) + defer tp.Shutdown(ctx) + + //options := &config.RabbitmqOptions{ + // RabbitmqHostOptions: &config.RabbitmqHostOptions{ + // UserName: "guest", + // Password: "guest", + // HostName: "localhost", + // Port: 5672, + // }, + //} + + rabbitmqHostOption, err := rabbitmq.NewRabbitMQTestContainers(defaultLogger.GetLogger()). + PopulateContainerOptions(ctx, t) + require.NoError(t, err) + + options := &config.RabbitmqOptions{ + RabbitmqHostOptions: rabbitmqHostOption, + } + + conn, err := types.NewRabbitMQConnection(options) require.NoError(t, err) - rabbitmqProducer, err := NewRabbitMQProducer( + producerFactory := NewProducerFactory( + options, conn, - nil, - defaultLogger.Logger, eventSerializer, + defaultLogger.GetLogger(), ) + + rabbitmqProducer, err := producerFactory.CreateProducer(nil) + require.NoError(t, err) err = rabbitmqProducer.PublishMessage(ctx, NewProducerMessage("test"), nil) diff --git a/internal/pkg/rabbitmq/rabbitmq_fx.go b/internal/pkg/rabbitmq/rabbitmq_fx.go index d601b5eb..ae594718 100644 --- a/internal/pkg/rabbitmq/rabbitmq_fx.go +++ b/internal/pkg/rabbitmq/rabbitmq_fx.go @@ -5,12 +5,14 @@ import ( "fmt" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + bus2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - bus2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/factory" + producer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" "go.uber.org/fx" @@ -31,20 +33,21 @@ var ( // - order is not important in provide // - provide can have parameter and will resolve if registered // - execute its func only if it requested - rabbitmqProviders = fx.Options(fx.Provide( //nolint:gochecknoglobals - config.ProvideConfig, - ), + rabbitmqProviders = fx.Options( + fx.Provide(config.ProvideConfig), fx.Provide(types.NewRabbitMQConnection), fx.Provide(fx.Annotate( bus.NewRabbitmqBus, - fx.ParamTags(``, ``, ``, ``, `optional:"true"`), + fx.ParamTags(``, ``, ``, `optional:"true"`), fx.As(new(producer.Producer)), fx.As(new(bus2.Bus)), fx.As(new(bus.RabbitmqBus)), )), + fx.Provide(factory.NewConsumerFactory), + fx.Provide(producer2.NewProducerFactory), fx.Provide(fx.Annotate( NewRabbitMQHealthChecker, - fx.As(new(health.Health)), + fx.As(new(contracts.Health)), fx.ResultTags(fmt.Sprintf(`group:"%s"`, "healths")), ))) @@ -52,7 +55,9 @@ var ( // - they execute by their orders // - invokes always execute its func compare to provides that only run when we request for them. // - return value will be discarded and can not be provided - rabbitmqInvokes = fx.Options(fx.Invoke(registerHooks)) //nolint:gochecknoglobals + rabbitmqInvokes = fx.Options( + fx.Invoke(registerHooks), + ) //nolint:gochecknoglobals ) // we don't want to register any dependencies here, its func body should execute always even we don't request for that, so we should use `invoke` @@ -77,7 +82,10 @@ func registerHooks( go func() { // if (ctx.Err() == nil), context not canceled or deadlined if err := bus.Start(lifeTimeCtx); err != nil { - logger.Errorf("(bus.Start) error in running rabbitmq server: {%v}", err) + logger.Errorf( + "(bus.Start) error in running rabbitmq server: {%v}", + err, + ) return } }() diff --git a/internal/pkg/rabbitmq/test/in-memory/rabbitmq_harnes.go b/internal/pkg/rabbitmq/test/in-memory/rabbitmq_harnes.go index 58699134..90ebf9b4 100644 --- a/internal/pkg/rabbitmq/test/in-memory/rabbitmq_harnes.go +++ b/internal/pkg/rabbitmq/test/in-memory/rabbitmq_harnes.go @@ -3,15 +3,15 @@ package in_memory import ( "context" + consumer2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/metadata" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" ) type RabbitmqInMemoryHarnesses struct { publishedMessage []types.IMessage consumedMessage []types.IMessage - consumerHandlers map[types.IMessage][]consumer.ConsumerHandler + consumerHandlers map[types.IMessage][]consumer2.ConsumerHandler } func NewRabbitmqInMemoryHarnesses() *RabbitmqInMemoryHarnesses { @@ -53,7 +53,7 @@ func (r *RabbitmqInMemoryHarnesses) Stop(ctx context.Context) error { func (r *RabbitmqInMemoryHarnesses) ConnectConsumerHandler( messageType types.IMessage, - consumerHandler consumer.ConsumerHandler, + consumerHandler consumer2.ConsumerHandler, ) error { r.consumerHandlers[messageType] = append(r.consumerHandlers[messageType], consumerHandler) return nil @@ -61,7 +61,7 @@ func (r *RabbitmqInMemoryHarnesses) ConnectConsumerHandler( func (r *RabbitmqInMemoryHarnesses) ConnectConsumer( messageType types.IMessage, - consumer consumer.Consumer, + consumer consumer2.Consumer, ) error { return nil } diff --git a/internal/pkg/rabbitmq/test/testcontainer/rabbitmq_test_container.go b/internal/pkg/rabbitmq/test/testcontainer/rabbitmq_test_container.go new file mode 100644 index 00000000..5a59d745 --- /dev/null +++ b/internal/pkg/rabbitmq/test/testcontainer/rabbitmq_test_container.go @@ -0,0 +1,95 @@ +package testcontainer + +import ( + "context" + "fmt" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" + + "github.com/docker/docker/api/types/container" + "github.com/docker/go-connections/nat" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +func CreatingContainerOptions( + ctx context.Context, + t *testing.T, +) (*config.RabbitmqHostOptions, error) { + t.Helper() + + // https://github.com/testcontainers/testcontainers-go + // https://dev.to/remast/go-integration-tests-using-testcontainers-9o5 + containerReq := testcontainers.ContainerRequest{ + Image: fmt.Sprintf( + "%s:%s", + "rabbitmq", + "management", + ), + ExposedPorts: []string{"5672/tcp", "15672/tcp"}, + WaitingFor: wait.ForListeningPort( + nat.Port("5672/tcp"), + ), + HostConfigModifier: func(hostConfig *container.HostConfig) { + hostConfig.AutoRemove = true + }, + Hostname: "localhost", + Env: map[string]string{ + "RABBITMQ_DEFAULT_USER": "guest", + "RABBITMQ_DEFAULT_PASS": "guest", + }, + } + + dbContainer, err := testcontainers.GenericContainer( + context.Background(), + testcontainers.GenericContainerRequest{ + ContainerRequest: containerReq, + Started: true, + }) + if err != nil { + return nil, err + } + + //// Clean up the container after the test is complete + t.Cleanup(func() { + if terr := dbContainer.Terminate(ctx); terr != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + // get a free random host port for rabbitmq `Tcp Port` + hostPort, err := dbContainer.MappedPort( + ctx, + nat.Port("5672/tcp"), + ) + if err != nil { + return nil, err + } + + // https://github.com/michaelklishin/rabbit-hole/issues/74 + // get a free random host port for rabbitmq UI `Http Port` + uiHttpPort, err := dbContainer.MappedPort( + ctx, + nat.Port("15672/tcp"), + ) + if err != nil { + return nil, err + } + + host, err := dbContainer.Host(ctx) + if err != nil { + return nil, err + } + + option := &config.RabbitmqHostOptions{ + UserName: "guest", + Password: "guest", + HostName: host, + VirtualHost: "/", + Port: hostPort.Int(), + HttpPort: uiHttpPort.Int(), + } + + return option, nil +} diff --git a/internal/pkg/rabbitmq/types/connection.go b/internal/pkg/rabbitmq/types/connection.go index a1dc869d..0c0194ac 100644 --- a/internal/pkg/rabbitmq/types/connection.go +++ b/internal/pkg/rabbitmq/types/connection.go @@ -3,9 +3,9 @@ package types import ( "fmt" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" "emperror.dev/errors" "github.com/rabbitmq/amqp091-go" @@ -131,13 +131,17 @@ func (c *internalConnection) handleReconnecting() { select { case err := <-c.errConnectionChan: if err != nil { - defaultLogger.Logger.Info("Rabbitmq Connection Reconnecting started") + defaultLogger.GetLogger(). + Info("Rabbitmq Connection Reconnecting started") err := c.connect() if err != nil { - defaultLogger.Logger.Error(fmt.Sprintf("Error in reconnecting, %s", err)) + defaultLogger.GetLogger(). + Error(fmt.Sprintf("Error in reconnecting, %s", err)) continue } - defaultLogger.Logger.Info("Rabbitmq Connection Reconnected") + + defaultLogger.GetLogger(). + Info("Rabbitmq Connection Reconnected") c.isConnected = true c.reconnectedChan <- struct{}{} continue diff --git a/internal/pkg/redis/health.go b/internal/pkg/redis/health.go index 8ab22b02..b5c25b50 100644 --- a/internal/pkg/redis/health.go +++ b/internal/pkg/redis/health.go @@ -3,7 +3,7 @@ package redis import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "github.com/redis/go-redis/v9" ) @@ -12,7 +12,7 @@ type RedisHealthChecker struct { client *redis.Client } -func NewRedisHealthChecker(client *redis.Client) health.Health { +func NewRedisHealthChecker(client *redis.Client) contracts.Health { return &RedisHealthChecker{client} } diff --git a/internal/pkg/redis/redis.go b/internal/pkg/redis/redis.go index 742c104d..46d9569d 100644 --- a/internal/pkg/redis/redis.go +++ b/internal/pkg/redis/redis.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/redis/go-redis/extra/redisotel/v9" "github.com/redis/go-redis/v9" ) @@ -34,5 +35,9 @@ func NewRedisClient(cfg *RedisOptions) *redis.Client { PoolTimeout: poolTimeout, }) + if cfg.EnableTracing { + _ = redisotel.InstrumentTracing(universalClient) + } + return universalClient } diff --git a/internal/pkg/redis/redis_fx.go b/internal/pkg/redis/redis_fx.go index 266bf24f..02d5f341 100644 --- a/internal/pkg/redis/redis_fx.go +++ b/internal/pkg/redis/redis_fx.go @@ -4,10 +4,9 @@ import ( "context" "fmt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/redis/go-redis/extra/redisotel/v9" "github.com/redis/go-redis/v9" "go.uber.org/fx" ) @@ -15,7 +14,11 @@ import ( var ( // Module provided to fxlog // https://uber-go.github.io/fx/modules.html - Module = fx.Module("redisfx", redisProviders, redisInvokes) //nolint:gochecknoglobals + Module = fx.Module( + "redisfx", + redisProviders, + redisInvokes, + ) //nolint:gochecknoglobals redisProviders = fx.Options(fx.Provide( //nolint:gochecknoglobals NewRedisClient, @@ -29,16 +32,21 @@ var ( //), fx.Annotate( NewRedisHealthChecker, - fx.As(new(health.Health)), + fx.As(new(contracts.Health)), fx.ResultTags(fmt.Sprintf(`group:"%s"`, "healths")), ), provideConfig)) - redisInvokes = fx.Options(fx.Invoke(registerHooks), //nolint:gochecknoglobals - fx.Invoke(EnableTracing)) + redisInvokes = fx.Options( + fx.Invoke(registerHooks), + ) //nolint:gochecknoglobals ) -func registerHooks(lc fx.Lifecycle, client redis.UniversalClient, logger logger.Logger) { +func registerHooks( + lc fx.Lifecycle, + client redis.UniversalClient, + logger logger.Logger, +) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { return client.Ping(ctx).Err() @@ -54,7 +62,3 @@ func registerHooks(lc fx.Lifecycle, client redis.UniversalClient, logger logger. }, }) } - -func EnableTracing(redis *redis.Client) error { - return redisotel.InstrumentTracing(redis) -} diff --git a/internal/pkg/redis/redis_options.go b/internal/pkg/redis/redis_options.go index 788d828c..077a92a6 100644 --- a/internal/pkg/redis/redis_options.go +++ b/internal/pkg/redis/redis_options.go @@ -2,22 +2,23 @@ package redis import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) -var optionName = strcase.ToLowerCamel(typeMapper.GetTypeNameByT[RedisOptions]()) +var optionName = strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[RedisOptions]()) type RedisOptions struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - Password string `mapstructure:"password"` - Database int `mapstructure:"database"` - PoolSize int `mapstructure:"poolSize"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Password string `mapstructure:"password"` + Database int `mapstructure:"database"` + PoolSize int `mapstructure:"poolSize"` + EnableTracing bool `mapstructure:"enableTracing" default:"true"` } -func provideConfig(environment environemnt.Environment) (*RedisOptions, error) { +func provideConfig(environment environment.Environment) (*RedisOptions, error) { return config.BindConfigKey[*RedisOptions](optionName, environment) } diff --git a/internal/pkg/reflection/reflection_helper/reflection_helper.go b/internal/pkg/reflection/reflectionhelper/reflection_helper.go similarity index 100% rename from internal/pkg/reflection/reflection_helper/reflection_helper.go rename to internal/pkg/reflection/reflectionhelper/reflection_helper.go diff --git a/internal/pkg/reflection/reflection_helper/reflection_helper_test.go b/internal/pkg/reflection/reflectionhelper/reflection_helper_test.go similarity index 93% rename from internal/pkg/reflection/reflection_helper/reflection_helper_test.go rename to internal/pkg/reflection/reflectionhelper/reflection_helper_test.go index e2b13bac..48183139 100644 --- a/internal/pkg/reflection/reflection_helper/reflection_helper_test.go +++ b/internal/pkg/reflection/reflectionhelper/reflection_helper_test.go @@ -101,7 +101,7 @@ func Test_Get_Field_Value_For_Exported_Fields_And_Addressable_Struct(t *testing. // field by name only work on struct not pointer type so we should get Elem() v := reflect.ValueOf(p).Elem() - name := GetFieldValue(v.FieldByName("Name")).Interface() + name := GetFieldValue(v.FieldByName("ShortTypeName")).Interface() age := GetFieldValue(v.FieldByName("Age")).Interface() assert.Equal(t, "John", name) @@ -125,7 +125,7 @@ func Test_Get_Field_Value_For_Exported_Fields_And_UnAddressable_Struct(t *testin // field by name only work on struct not pointer type so we should get Elem() v := reflect.ValueOf(&p).Elem() - name := GetFieldValue(v.FieldByName("Name")).Interface() + name := GetFieldValue(v.FieldByName("ShortTypeName")).Interface() age := GetFieldValue(v.FieldByName("Age")).Interface() assert.Equal(t, "John", name) @@ -149,7 +149,7 @@ func Test_Set_Field_For_Exported_Fields_And_Addressable_Struct(t *testing.T) { // field by name only work on struct not pointer type so we should get Elem() v := reflect.ValueOf(p).Elem() - name := GetFieldValue(v.FieldByName("Name")) + name := GetFieldValue(v.FieldByName("ShortTypeName")) age := GetFieldValue(v.FieldByName("Age")) SetFieldValue(name, "John") @@ -179,7 +179,7 @@ func Test_Set_Field_For_Exported_Fields_And_UnAddressable_Struct(t *testing.T) { // field by name only work on struct not pointer type so we should get Elem() v := reflect.ValueOf(&p).Elem() - name := GetFieldValue(v.FieldByName("Name")) + name := GetFieldValue(v.FieldByName("ShortTypeName")) age := GetFieldValue(v.FieldByName("Age")) SetFieldValue(name, "John") @@ -206,14 +206,14 @@ func Test_Set_Field_For_UnExported_Fields_And_UnAddressable_Struct(t *testing.T) func Test_Get_Unexported_Field_From_Method_And_Addressable_Struct(t *testing.T) { p := &PersonPrivate{name: "John", age: 20} - name := GetFieldValueFromMethodAndObject(p, "Name") + name := GetFieldValueFromMethodAndObject(p, "ShortTypeName") assert.Equal(t, "John", name.Interface()) } func Test_Get_Unexported_Field_From_Method_And_UnAddressable_Struct(t *testing.T) { p := PersonPrivate{name: "John", age: 20} - name := GetFieldValueFromMethodAndObject(p, "Name") + name := GetFieldValueFromMethodAndObject(p, "ShortTypeName") assert.Equal(t, "John", name.Interface()) } @@ -224,7 +224,7 @@ func Test_Convert_NoPointer_Type_To_Pointer_Type_With_Addr(t *testing.T) { p := PersonPrivate{name: "John", age: 20} v := reflect.ValueOf(&p).Elem() pointerType := v.Addr() - name := pointerType.MethodByName("Name").Call(nil)[0].Interface() + name := pointerType.MethodByName("ShortTypeName").Call(nil)[0].Interface() age := pointerType.MethodByName("Age").Call(nil)[0].Interface() assert.Equal(t, "John", name) diff --git a/internal/pkg/reflection/type_mappper/type_mapper_test.go b/internal/pkg/reflection/type_mappper/type_mapper_test.go deleted file mode 100644 index 36141d6b..00000000 --- a/internal/pkg/reflection/type_mappper/type_mapper_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package typeMapper - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestTypes(t *testing.T) { - s := Test{A: 10} - s2 := &Test{A: 10} - - q2 := TypeByName("*typeMapper.Test") - q22 := InstanceByTypeName("*typeMapper.Test").(*Test) - q222 := InstancePointerByTypeName("*typeMapper.Test").(*Test) - q3 := TypeByNameAndImplementedInterface[ITest]("*typeMapper.Test") - q4 := InstanceByTypeNameAndImplementedInterface[ITest]("*typeMapper.Test") - - c1 := GetTypeFromGeneric[Test]() - c2 := GetTypeFromGeneric[*Test]() - c3 := GetTypeFromGeneric[ITest]() - - d1 := GetReflectType(Test{}) - d2 := GetReflectType(&Test{}) - d3 := GetReflectType((*ITest)(nil)) - - q := TypeByName("typeMapper.Test") - q1 := InstanceByTypeName("typeMapper.Test").(Test) - q11 := InstancePointerByTypeName("typeMapper.Test").(*Test) - - y := TypeByName("*Test") - y1 := InstanceByTypeName("*Test").(*Test) - y2 := InstancePointerByTypeName("*Test").(*Test) - - z := TypeByName("Test") - z1 := InstanceByTypeName("Test").(Test) - z2 := InstancePointerByTypeName("Test").(*Test) - - r := GenericInstanceByT[*Test]() - r2 := GenericInstanceByT[Test]() - - typeName := GetFullTypeName(s) - typeName2 := GetFullTypeName(s2) - typeName3 := GetTypeName(s2) - typeName4 := GetTypeName(s) - - assert.Equal(t, "typeMapper.Test", typeName) - assert.Equal(t, "*typeMapper.Test", typeName2) - assert.Equal(t, "*Test", typeName3) - assert.Equal(t, "Test", typeName4) - - q1.A = 100 - q22.A = 100 - - assert.NotNil(t, d1) - assert.NotNil(t, d2) - assert.NotNil(t, d3) - assert.NotNil(t, c1) - assert.NotNil(t, c2) - assert.NotNil(t, c3) - assert.NotNil(t, q) - assert.NotNil(t, q1) - assert.NotNil(t, q11) - assert.NotNil(t, q2) - assert.NotNil(t, q22) - assert.NotNil(t, q222) - assert.NotNil(t, q3) - assert.NotNil(t, q4) - assert.NotNil(t, r) - assert.NotNil(t, r2) - assert.NotNil(t, y) - assert.NotNil(t, y1) - assert.NotNil(t, y2) - assert.NotNil(t, z) - assert.NotNil(t, z1) - assert.NotNil(t, z2) - assert.NotZero(t, q1.A) - assert.NotZero(t, q22.A) -} - -type Test struct { - A int -} - -type ITest interface { - Method1() -} - -func (t *Test) Method1() { -} diff --git a/internal/pkg/reflection/type_mappper/type_mapper.go b/internal/pkg/reflection/typemapper/type_mapper.go similarity index 75% rename from internal/pkg/reflection/type_mappper/type_mapper.go rename to internal/pkg/reflection/typemapper/type_mapper.go index 759b56b8..89fae6b4 100644 --- a/internal/pkg/reflection/type_mappper/type_mapper.go +++ b/internal/pkg/reflection/typemapper/type_mapper.go @@ -1,4 +1,4 @@ -package typeMapper +package typemapper // https://stackoverflow.com/a/34722791/581476 // https://stackoverflow.com/questions/7850140/how-do-you-create-a-new-instance-of-a-struct-from-its-type-at-run-time-in-go @@ -9,6 +9,8 @@ import ( "reflect" "strings" "unsafe" + + "github.com/iancoleman/strcase" ) var ( @@ -32,7 +34,8 @@ func discoverTypes() { for _, off := range offs { emptyInterface := (*emptyInterface)(unsafe.Pointer(&typ)) emptyInterface.data = resolveTypeOff(rodata, off) - if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct { + if typ.Kind() == reflect.Ptr && + typ.Elem().Kind() == reflect.Struct { // just discover pointer types, but we also register this pointer type actual struct type to the registry loadedTypePtr := typ loadedType := typ.Elem() @@ -105,9 +108,11 @@ func TypesByName(typeName string) []reflect.Type { return nil } -func TypeByNameAndImplementedInterface[TInterface interface{}](typeName string) reflect.Type { +func TypeByNameAndImplementedInterface[TInterface interface{}]( + typeName string, +) reflect.Type { // https://stackoverflow.com/questions/7132848/how-to-get-the-reflect-type-of-an-interface - implementedInterface := GetTypeFromGeneric[TInterface]() + implementedInterface := GetGenericTypeByT[TInterface]() if types, ok := types[typeName]; ok { for _, t := range types { if t.Implements(implementedInterface) { @@ -115,6 +120,7 @@ func TypeByNameAndImplementedInterface[TInterface interface{}](typeName string) } } } + return nil } @@ -122,7 +128,7 @@ func TypesImplementedInterfaceWithFilterTypes[TInterface interface{}]( types []reflect.Type, ) []reflect.Type { // https://stackoverflow.com/questions/7132848/how-to-get-the-reflect-type-of-an-interface - implementedInterface := GetTypeFromGeneric[TInterface]() + implementedInterface := GetGenericTypeByT[TInterface]() var res []reflect.Type for _, t := range types { @@ -136,7 +142,7 @@ func TypesImplementedInterfaceWithFilterTypes[TInterface interface{}]( func TypesImplementedInterface[TInterface interface{}]() []reflect.Type { // https://stackoverflow.com/questions/7132848/how-to-get-the-reflect-type-of-an-interface - implementedInterface := GetTypeFromGeneric[TInterface]() + implementedInterface := GetGenericTypeByT[TInterface]() var res []reflect.Type for _, t := range types { @@ -152,16 +158,30 @@ func TypesImplementedInterface[TInterface interface{}]() []reflect.Type { // GetFullTypeName returns the full name of the type by its package name func GetFullTypeName(input interface{}) string { + if input == nil { + return "" + } + t := reflect.TypeOf(input) return t.String() } +func GetGenericFullTypeNameByT[T any]() string { + t := reflect.TypeOf((*T)(nil)).Elem() + + return t.String() +} + func GetFullTypeNameByType(typ reflect.Type) string { return typ.String() } // GetTypeName returns the name of the type without its package name func GetTypeName(input interface{}) string { + if input == nil { + return "" + } + t := reflect.TypeOf(input) if t.Kind() != reflect.Ptr { return t.Name() @@ -170,7 +190,33 @@ func GetTypeName(input interface{}) string { return fmt.Sprintf("*%s", t.Elem().Name()) } -func GetTypeNameByT[T any]() string { +func GetSnakeTypeName(input interface{}) string { + if input == nil { + return "" + } + + t := reflect.TypeOf(input) + if t.Kind() != reflect.Ptr { + return t.Name() + } + + return strcase.ToSnake(t.Elem().Name()) +} + +func GetKebabTypeName(input interface{}) string { + if input == nil { + return "" + } + + t := reflect.TypeOf(input) + if t.Kind() != reflect.Ptr { + return t.Name() + } + + return strcase.ToKebab(t.Elem().Name()) +} + +func GetGenericTypeNameByT[T any]() string { t := reflect.TypeOf((*T)(nil)).Elem() if t.Kind() != reflect.Ptr { return t.Name() @@ -179,7 +225,21 @@ func GetTypeNameByT[T any]() string { return fmt.Sprintf("*%s", t.Elem().Name()) } -func GetNonPointerTypeName(input interface{}) string { +func GetGenericNonePointerTypeNameByT[T any]() string { + t := reflect.TypeOf((*T)(nil)).Elem() + if t.Kind() != reflect.Ptr { + return t.Name() + } + + return t.Elem().Name() +} + +// GetNonePointerTypeName returns the name of the type without its package name and its pointer +func GetNonePointerTypeName(input interface{}) string { + if input == nil { + return "" + } + t := reflect.TypeOf(input) if t.Kind() != reflect.Ptr { return t.Name() @@ -189,6 +249,10 @@ func GetNonPointerTypeName(input interface{}) string { } func GetTypeNameByType(typ reflect.Type) string { + if typ == nil { + return "" + } + if typ.Kind() != reflect.Ptr { return typ.Name() } @@ -204,6 +268,19 @@ func TypeByPackageName(pkgPath string, name string) reflect.Type { return nil } +func GetPackageName(value interface{}) string { + inputType := reflect.TypeOf(value) + if inputType.Kind() == reflect.Ptr { + inputType = inputType.Elem() + } + + packagePath := inputType.PkgPath() + + parts := strings.Split(packagePath, "/") + + return parts[len(parts)-1] +} + func TypesByPackageName(pkgPath string, name string) []reflect.Type { if pkgTypes, ok := packages[pkgPath]; ok { return pkgTypes[name] @@ -211,7 +288,7 @@ func TypesByPackageName(pkgPath string, name string) []reflect.Type { return nil } -func GetTypeFromGeneric[T interface{}]() reflect.Type { +func GetGenericTypeByT[T interface{}]() reflect.Type { res := reflect.TypeOf((*T)(nil)).Elem() return res } @@ -244,7 +321,7 @@ func GetBaseReflectType(value interface{}) reflect.Type { func GenericInstanceByT[T any]() T { // https://stackoverflow.com/questions/7132848/how-to-get-the-reflect-type-of-an-interface - typ := GetTypeFromGeneric[T]() + typ := GetGenericTypeByT[T]() return getInstanceFromType(typ).(T) } @@ -261,12 +338,23 @@ func InstanceByTypeName(name string) interface{} { return getInstanceFromType(typ) } -func InstanceByTypeNameAndImplementedInterface[TInterface interface{}](name string) interface{} { +func EmptyInstanceByTypeNameAndImplementedInterface[TInterface interface{}]( + name string, +) interface{} { typ := TypeByNameAndImplementedInterface[TInterface](name) return getInstanceFromType(typ) } +func EmptyInstanceByTypeAndImplementedInterface[TInterface interface{}]( + typ reflect.Type, +) interface{} { + // we use short type name instead of full type name because this typ in other receiver packages could have different package name + typeName := GetTypeName(typ) + + return EmptyInstanceByTypeNameAndImplementedInterface[TInterface](typeName) +} + // InstancePointerByTypeName return an empty pointer instance of the type by its name // If the type is a pointer type, it will return a pointer instance of the type and // if the type is a struct type, it will return a pointer to the struct @@ -299,7 +387,7 @@ func getInstanceFromType(typ reflect.Type) interface{} { // return reflect.New(typ).Elem().Interface() } -func GetImplementInterfaceTypes[T any]() map[string][]reflect.Type { +func GetGenericImplementInterfaceTypesT[T any]() map[string][]reflect.Type { result := make(map[string][]reflect.Type) // Get the interface type @@ -324,3 +412,14 @@ func GetImplementInterfaceTypes[T any]() map[string][]reflect.Type { return result } + +func ImplementedInterfaceT[T any](obj interface{}) bool { + // Get the interface type + interfaceType := reflect.TypeOf((*T)(nil)).Elem() + + typ := GetReflectType(obj) + + implemented := typ.Implements(interfaceType) + + return implemented +} diff --git a/internal/pkg/reflection/typemapper/type_mapper_test.go b/internal/pkg/reflection/typemapper/type_mapper_test.go new file mode 100644 index 00000000..b2066fdb --- /dev/null +++ b/internal/pkg/reflection/typemapper/type_mapper_test.go @@ -0,0 +1,154 @@ +//go:build unit +// +build unit + +package typemapper + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_GetTypeNameByT(t *testing.T) { + pointerTypeName := GetGenericTypeNameByT[*Test]() + nonePointerTypeName := GetGenericTypeNameByT[Test]() + + require.Equal(t, pointerTypeName, "*Test") + require.Equal(t, nonePointerTypeName, "Test") +} + +func Test_GetNonePointerTypeNameByT(t *testing.T) { + pointerTypeName := GetGenericNonePointerTypeNameByT[*Test]() + nonePointerTypeName := GetGenericNonePointerTypeNameByT[Test]() + + require.Equal(t, pointerTypeName, "Test") + require.Equal(t, nonePointerTypeName, "Test") +} + +func Test_TypeByName(t *testing.T) { + s1 := TypeByName("*typemapper.Test") + s2 := TypeByName("typemapper.Test") + s3 := TypeByName("*Test") + s4 := TypeByName("Test") + + assert.NotNil(t, s1) + assert.NotNil(t, s2) + assert.NotNil(t, s3) + assert.NotNil(t, s4) +} + +func Test_GetTypeName(t *testing.T) { + t1 := Test{A: 10} + t2 := &Test{A: 10} + + typeName1 := GetTypeName(t1) + typeName2 := GetTypeName(t2) + + assert.Equal(t, "Test", typeName1) + assert.Equal(t, "*Test", typeName2) +} + +func Test_GetFullTypeName(t *testing.T) { + t1 := Test{A: 10} + t2 := &Test{A: 10} + + typeName1 := GetFullTypeName(t1) + typeName2 := GetFullTypeName(t2) + typeName3 := GetGenericFullTypeNameByT[*Test]() + typeName4 := GetGenericFullTypeNameByT[Test]() + + assert.Equal(t, "typemapper.Test", typeName1) + assert.Equal(t, "*typemapper.Test", typeName2) + assert.Equal(t, "*typemapper.Test", typeName3) + assert.Equal(t, "typemapper.Test", typeName4) +} + +func Test_InstanceByTypeName(t *testing.T) { + s1 := InstanceByTypeName("typemapper.Test").(Test) + s1.A = 100 + assert.NotNil(t, s1) + assert.NotZero(t, s1.A) + + s2 := InstanceByTypeName("*typemapper.Test").(*Test) + s2.A = 100 + assert.NotNil(t, s2) + assert.NotZero(t, s2.A) + + s3 := InstanceByTypeName("*Test").(*Test) + assert.NotNil(t, s3) + + s4 := InstanceByTypeName("Test").(Test) + assert.NotNil(t, s4) +} + +func Test_InstancePointerByTypeName(t *testing.T) { + s1 := InstancePointerByTypeName("*typemapper.Test").(*Test) + s2 := InstancePointerByTypeName("typemapper.Test").(*Test) + s3 := InstancePointerByTypeName("*Test").(*Test) + s4 := InstancePointerByTypeName("Test").(*Test) + + assert.NotNil(t, s1) + assert.NotNil(t, s2) + assert.NotNil(t, s3) + assert.NotNil(t, s4) +} + +func Test_GetTypeFromGeneric(t *testing.T) { + s1 := GetGenericTypeByT[Test]() + s2 := GetGenericTypeByT[*Test]() + s3 := GetGenericTypeByT[ITest]() + + assert.NotNil(t, s1) + assert.NotNil(t, s2) + assert.NotNil(t, s3) +} + +func Test_GenericInstanceByT(t *testing.T) { + s1 := GenericInstanceByT[*Test]() + s2 := GenericInstanceByT[Test]() + + assert.NotNil(t, s1) + assert.NotNil(t, s2) +} + +func Test_TypeByNameAndImplementedInterface(t *testing.T) { + s1 := TypeByNameAndImplementedInterface[ITest]("*typemapper.Test") + + assert.NotNil(t, s1) +} + +func Test_EmptyInstanceByTypeNameAndImplementedInterface(t *testing.T) { + s1 := EmptyInstanceByTypeNameAndImplementedInterface[ITest]("*typemapper.Test") + + assert.NotNil(t, s1) +} + +func Test_GetReflectType(t *testing.T) { + s1 := GetReflectType(Test{}) + s2 := GetReflectType(&Test{}) + s3 := GetReflectType((*ITest)(nil)) + + assert.NotNil(t, s1) + assert.NotNil(t, s2) + assert.NotNil(t, s3) +} + +func Test_GetPackageName(t *testing.T) { + pkName := GetPackageName(&Test{}) + pkName2 := GetPackageName(Test{}) + + assert.Equal(t, "typemapper", pkName) + assert.Equal(t, "typemapper", pkName2) +} + +type Test struct { + A int +} + +type ITest interface { + Method1() +} + +func (t *Test) Method1() { +} diff --git a/internal/pkg/reflection/type_mappper/unsafe_types.go b/internal/pkg/reflection/typemapper/unsafe_types.go similarity index 94% rename from internal/pkg/reflection/type_mappper/unsafe_types.go rename to internal/pkg/reflection/typemapper/unsafe_types.go index b90fbdcf..e4034894 100644 --- a/internal/pkg/reflection/type_mappper/unsafe_types.go +++ b/internal/pkg/reflection/typemapper/unsafe_types.go @@ -1,4 +1,4 @@ -package typeMapper +package typemapper import "unsafe" diff --git a/internal/pkg/reflection/type_registry/type_registry.go b/internal/pkg/reflection/typeregistry/type_registry.go similarity index 95% rename from internal/pkg/reflection/type_registry/type_registry.go rename to internal/pkg/reflection/typeregistry/type_registry.go index d981fa33..1cef8174 100644 --- a/internal/pkg/reflection/type_registry/type_registry.go +++ b/internal/pkg/reflection/typeregistry/type_registry.go @@ -1,4 +1,4 @@ -package type_registry +package typeregistry // Ref:https://stackoverflow.com/questions/23030884/is-there-a-way-to-create-an-instance-of-a-struct-from-a-string import "reflect" diff --git a/internal/pkg/test/containers/contracts/eventstoredb_container.go b/internal/pkg/test/containers/contracts/eventstoredb_container.go index aa6aa36c..39a52e4f 100644 --- a/internal/pkg/test/containers/contracts/eventstoredb_container.go +++ b/internal/pkg/test/containers/contracts/eventstoredb_container.go @@ -5,8 +5,6 @@ import ( "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/config" - - "github.com/EventStore/EventStore-Client-Go/esdb" ) type EventstoreDBContainerOptions struct { @@ -21,15 +19,11 @@ type EventstoreDBContainerOptions struct { } type EventstoreDBContainer interface { - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*EventstoreDBContainerOptions, ) (*config.EventStoreDbOptions, error) - Start(ctx context.Context, - t *testing.T, - options ...*EventstoreDBContainerOptions) (*esdb.Client, error) - Cleanup(ctx context.Context) error } diff --git a/internal/pkg/test/containers/contracts/gorm_container.go b/internal/pkg/test/containers/contracts/gorm_container.go index a96bdfc2..82ea93ea 100644 --- a/internal/pkg/test/containers/contracts/gorm_container.go +++ b/internal/pkg/test/containers/contracts/gorm_container.go @@ -4,9 +4,7 @@ import ( "context" "testing" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" - - "gorm.io/gorm" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" ) type PostgresContainerOptions struct { @@ -22,8 +20,7 @@ type PostgresContainerOptions struct { } type GormContainer interface { - Start(ctx context.Context, t *testing.T, options ...*PostgresContainerOptions) (*gorm.DB, error) - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*PostgresContainerOptions, diff --git a/internal/pkg/test/containers/contracts/mongo_container.go b/internal/pkg/test/containers/contracts/mongo_container.go index b7b9622e..67314820 100644 --- a/internal/pkg/test/containers/contracts/mongo_container.go +++ b/internal/pkg/test/containers/contracts/mongo_container.go @@ -5,8 +5,6 @@ import ( "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" - - "go.mongodb.org/mongo-driver/mongo" ) type MongoContainerOptions struct { @@ -22,12 +20,7 @@ type MongoContainerOptions struct { } type MongoContainer interface { - Start( - ctx context.Context, - t *testing.T, - options ...*MongoContainerOptions, - ) (*mongo.Client, error) - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*MongoContainerOptions, diff --git a/internal/pkg/test/containers/contracts/postgrespgx_container.go b/internal/pkg/test/containers/contracts/postgrespgx_container.go index 7d1bd432..3c11f535 100644 --- a/internal/pkg/test/containers/contracts/postgrespgx_container.go +++ b/internal/pkg/test/containers/contracts/postgrespgx_container.go @@ -4,12 +4,11 @@ import ( "context" "testing" - postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgres_pgx" + postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgrespgx" ) type PostgresPgxContainer interface { - Start(ctx context.Context, t *testing.T, options ...*PostgresContainerOptions) (*postgres.Pgx, error) - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*PostgresContainerOptions, diff --git a/internal/pkg/test/containers/contracts/rabbitmq_container.go b/internal/pkg/test/containers/contracts/rabbitmq_container.go index 5f012fa5..ca6c62ab 100644 --- a/internal/pkg/test/containers/contracts/rabbitmq_container.go +++ b/internal/pkg/test/containers/contracts/rabbitmq_container.go @@ -5,10 +5,7 @@ import ( "fmt" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" ) type RabbitMQContainerOptions struct { @@ -25,7 +22,13 @@ type RabbitMQContainerOptions struct { } func (h *RabbitMQContainerOptions) AmqpEndPoint() string { - return fmt.Sprintf("amqp://%s:%s@%s:%d", h.UserName, h.Password, h.Host, h.HostPort) + return fmt.Sprintf( + "amqp://%s:%s@%s:%d", + h.UserName, + h.Password, + h.Host, + h.HostPort, + ) } func (h *RabbitMQContainerOptions) HttpEndPoint() string { @@ -33,13 +36,7 @@ func (h *RabbitMQContainerOptions) HttpEndPoint() string { } type RabbitMQContainer interface { - Start(ctx context.Context, - t *testing.T, - serializer serializer.EventSerializer, - rabbitmqBuilderFunc configurations.RabbitMQConfigurationBuilderFuc, - options ...*RabbitMQContainerOptions) (bus.Bus, error) - - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*RabbitMQContainerOptions, diff --git a/internal/pkg/test/containers/contracts/redis_container.go b/internal/pkg/test/containers/contracts/redis_container.go index f44cbfe6..a6d0c0b3 100644 --- a/internal/pkg/test/containers/contracts/redis_container.go +++ b/internal/pkg/test/containers/contracts/redis_container.go @@ -5,8 +5,6 @@ import ( "testing" redis2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/redis" - - "github.com/redis/go-redis/v9" ) type RedisContainerOptions struct { @@ -21,12 +19,7 @@ type RedisContainerOptions struct { } type RedisContainer interface { - Start( - ctx context.Context, - t *testing.T, - options ...*RedisContainerOptions, - ) (redis.UniversalClient, error) - CreatingContainerOptions( + PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*RedisContainerOptions, diff --git a/internal/pkg/test/containers/dockertest/gorm/gorm_container.go b/internal/pkg/test/containers/dockertest/gorm/gorm_container.go index d1d8912e..69fa089a 100644 --- a/internal/pkg/test/containers/dockertest/gorm/gorm_container.go +++ b/internal/pkg/test/containers/dockertest/gorm/gorm_container.go @@ -6,13 +6,12 @@ import ( "strconv" "testing" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/phayes/freeport" - "gorm.io/gorm" ) type gormDockerTest struct { @@ -35,11 +34,13 @@ func NewGormDockerTest() contracts.GormContainer { } } -func (g *gormDockerTest) CreatingContainerOptions( +func (g *gormDockerTest) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.PostgresContainerOptions, ) (*gormPostgres.GormOptions, error) { + t.Helper() + // https://github.com/ory/dockertest/blob/v3/examples/PostgreSQL.md // https://github.com/bozd4g/fb.testcontainers pool, err := dockertest.NewPool("") @@ -57,7 +58,10 @@ func (g *gormDockerTest) CreatingContainerOptions( config.RestartPolicy = docker.RestartPolicy{Name: "no"} }) if err != nil { - log.Fatalf("Could not start resource (Postgresql Test Container): %s", err) + log.Fatalf( + "Could not start resource (Postgresql Test Container): %s", + err, + ) } resource.Expire( @@ -91,21 +95,6 @@ func (g *gormDockerTest) CreatingContainerOptions( return postgresoptions, nil } -func (g *gormDockerTest) Start( - ctx context.Context, - t *testing.T, - options ...*contracts.PostgresContainerOptions, -) (*gorm.DB, error) { - gormOptions, err := g.CreatingContainerOptions(ctx, t, options...) - if err != nil { - return nil, err - } - - db, err := gormPostgres.NewGorm(gormOptions) - - return db, nil -} - func (g *gormDockerTest) Cleanup(ctx context.Context) error { return g.resource.Close() } @@ -154,7 +143,10 @@ func (g *gormDockerTest) getRunOptions( ExposedPorts: []string{g.defaultOptions.Port}, PortBindings: map[docker.Port][]docker.PortBinding{ docker.Port(g.defaultOptions.Port): { - {HostIP: "0.0.0.0", HostPort: strconv.Itoa(g.defaultOptions.HostPort)}, + { + HostIP: "0.0.0.0", + HostPort: strconv.Itoa(g.defaultOptions.HostPort), + }, }, }, } diff --git a/internal/pkg/test/containers/dockertest/gorm/gorm_container_test.go b/internal/pkg/test/containers/dockertest/gorm/gorm_container_test.go index ebee5aba..b5c7e424 100644 --- a/internal/pkg/test/containers/dockertest/gorm/gorm_container_test.go +++ b/internal/pkg/test/containers/dockertest/gorm/gorm_container_test.go @@ -4,12 +4,32 @@ import ( "context" "testing" - "github.com/stretchr/testify/require" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + + "github.com/stretchr/testify/assert" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + "gorm.io/gorm" ) func Test_Gorm_Container(t *testing.T) { - gorm, err := NewGormDockerTest().Start(context.Background(), t) - require.NoError(t, err) + ctx := context.Background() + var gorm *gorm.DB + + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + gormPostgres.Module, + fx.Decorate(GormDockerTestConatnerOptionsDecorator(t, ctx)), + fx.Populate(&gorm), + ).RequireStart() - require.NotNil(t, gorm) + assert.NotNil(t, gorm) } diff --git a/internal/pkg/test/containers/dockertest/gorm/gorm_fx.go b/internal/pkg/test/containers/dockertest/gorm/gorm_fx.go new file mode 100644 index 00000000..30527787 --- /dev/null +++ b/internal/pkg/test/containers/dockertest/gorm/gorm_fx.go @@ -0,0 +1,14 @@ +package gorm + +import ( + "context" + "testing" + + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" +) + +var GormDockerTestConatnerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { + return func(c *gormPostgres.GormOptions) (*gormPostgres.GormOptions, error) { + return NewGormDockerTest().PopulateContainerOptions(ctx, t) + } +} diff --git a/internal/pkg/test/containers/dockertest/mongo/mongo_container.go b/internal/pkg/test/containers/dockertest/mongo/mongo_container.go index 0fa6683c..3c90ab8c 100644 --- a/internal/pkg/test/containers/dockertest/mongo/mongo_container.go +++ b/internal/pkg/test/containers/dockertest/mongo/mongo_container.go @@ -12,7 +12,6 @@ import ( "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" - "go.mongodb.org/mongo-driver/mongo" ) type mongoDockerTest struct { @@ -35,7 +34,7 @@ func NewMongoDockerTest() contracts.MongoContainer { } } -func (g *mongoDockerTest) CreatingContainerOptions( +func (g *mongoDockerTest) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.MongoContainerOptions, @@ -48,13 +47,16 @@ func (g *mongoDockerTest) CreatingContainerOptions( runOptions := g.getRunOptions(options...) // pull mongodb docker image for version 5.0 - resource, err := pool.RunWithOptions(runOptions, func(config *docker.HostConfig) { - // set AutoRemove to true so that stopped container goes away by itself - config.AutoRemove = true - config.RestartPolicy = docker.RestartPolicy{ - Name: "no", - } - }) + resource, err := pool.RunWithOptions( + runOptions, + func(config *docker.HostConfig) { + // set AutoRemove to true so that stopped container goes away by itself + config.AutoRemove = true + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } + }, + ) if err != nil { log.Fatalf("Could not start resource (Mongo Container): %s", err) } @@ -64,7 +66,9 @@ func (g *mongoDockerTest) CreatingContainerOptions( ) // Tell docker to hard kill the container in 120 seconds exponential backoff-retry, because the application_exceptions in the container might not be ready to accept connections yet g.resource = resource - port, _ := strconv.Atoi(resource.GetPort(fmt.Sprintf("%s/tcp", g.defaultOptions.Port))) + port, _ := strconv.Atoi( + resource.GetPort(fmt.Sprintf("%s/tcp", g.defaultOptions.Port)), + ) g.defaultOptions.HostPort = port t.Cleanup(func() { _ = resource.Close() }) @@ -91,24 +95,6 @@ func (g *mongoDockerTest) CreatingContainerOptions( return mongoOptions, nil } -func (g *mongoDockerTest) Start( - ctx context.Context, - t *testing.T, - options ...*contracts.MongoContainerOptions, -) (*mongo.Client, error) { - mongoOptions, err := g.CreatingContainerOptions(ctx, t, options...) - if err != nil { - return nil, err - } - - db, err := mongodb.NewMongoDB(mongoOptions) - if err != nil { - return nil, err - } - - return db, nil -} - func (g *mongoDockerTest) Cleanup(ctx context.Context) error { return g.resource.Close() } diff --git a/internal/pkg/test/containers/dockertest/mongo/mongo_container_test.go b/internal/pkg/test/containers/dockertest/mongo/mongo_container_test.go index 519305ef..c4d4282f 100644 --- a/internal/pkg/test/containers/dockertest/mongo/mongo_container_test.go +++ b/internal/pkg/test/containers/dockertest/mongo/mongo_container_test.go @@ -4,13 +4,32 @@ import ( "context" "testing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" + "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_Mongo_Container(t *testing.T) { - mongo, err := NewMongoDockerTest().Start(context.Background(), t) - require.NoError(t, err) + ctx := context.Background() + var mongoClient *mongo.Client + + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + mongodb.Module, + fx.Decorate(MongoDockerTestContainerOptionsDecorator(t, ctx)), + fx.Populate(&mongoClient), + ).RequireStart() - assert.NotNil(t, mongo) + assert.NotNil(t, mongoClient) } diff --git a/internal/pkg/test/containers/dockertest/mongo/mongo_fx.go b/internal/pkg/test/containers/dockertest/mongo/mongo_fx.go new file mode 100644 index 00000000..79763bc1 --- /dev/null +++ b/internal/pkg/test/containers/dockertest/mongo/mongo_fx.go @@ -0,0 +1,15 @@ +package mongo + +import ( + "context" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" +) + +var MongoDockerTestContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { + return func(c *mongodb.MongoDbOptions, logger logger.Logger) (*mongodb.MongoDbOptions, error) { + return NewMongoDockerTest().PopulateContainerOptions(ctx, t) + } +} diff --git a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container.go b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container.go index 423412df..25d3c36c 100644 --- a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container.go +++ b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container.go @@ -7,13 +7,8 @@ import ( "strconv" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" - bus2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "github.com/ory/dockertest/v3" @@ -29,11 +24,6 @@ type rabbitmqDockerTest struct { } func NewRabbitMQDockerTest(logger logger.Logger) contracts.RabbitMQContainer { - pool, err := dockertest.NewPool("") - if err != nil { - log.Fatalf("Could not connect to docker: %s", err) - } - return &rabbitmqDockerTest{ defaultOptions: &contracts.RabbitMQContainerOptions{ Ports: []string{"5672", "15672"}, @@ -46,25 +36,32 @@ func NewRabbitMQDockerTest(logger logger.Logger) contracts.RabbitMQContainer { Name: "rabbitmq-dockertest", }, logger: logger, - pool: pool, } } -func (g *rabbitmqDockerTest) CreatingContainerOptions( +func (g *rabbitmqDockerTest) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.RabbitMQContainerOptions, ) (*config.RabbitmqHostOptions, error) { + pool, err := dockertest.NewPool("") + if err != nil { + log.Fatalf("Could not connect to docker: %s", err) + } + runOptions := g.getRunOptions(options...) // pull mongodb docker image for version 5.0 - resource, err := g.pool.RunWithOptions(runOptions, func(config *docker.HostConfig) { - // set AutoRemove to true so that stopped container goes away by itself - config.AutoRemove = true - config.RestartPolicy = docker.RestartPolicy{ - Name: "no", - } - }) + resource, err := pool.RunWithOptions( + runOptions, + func(config *docker.HostConfig) { + // set AutoRemove to true so that stopped container goes away by itself + config.AutoRemove = true + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } + }, + ) if err != nil { log.Fatalf("Could not start resource (RabbitMQ Container): %s", err) } @@ -86,62 +83,29 @@ func (g *rabbitmqDockerTest) CreatingContainerOptions( t.Cleanup(func() { _ = resource.Close() }) - isConnectable := isConnectable(g.logger, g.defaultOptions) - if !isConnectable { - return g.CreatingContainerOptions(context.Background(), t, options...) - } - - opt := &config.RabbitmqHostOptions{ - UserName: g.defaultOptions.UserName, - Password: g.defaultOptions.Password, - HostName: g.defaultOptions.Host, - VirtualHost: g.defaultOptions.VirtualHost, - Port: g.defaultOptions.HostPort, - HttpPort: g.defaultOptions.HttpPort, - } - - return opt, nil -} - -func (g *rabbitmqDockerTest) Start( - ctx context.Context, - t *testing.T, - serializer serializer.EventSerializer, - rabbitmqBuilderFunc configurations.RabbitMQConfigurationBuilderFuc, - options ...*contracts.RabbitMQContainerOptions, -) (bus.Bus, error) { - rabbitmqHostOptions, err := g.CreatingContainerOptions(ctx, t, options...) - if err != nil { - return nil, err - } - - var mqBus bus.Bus - if err := g.pool.Retry(func() error { - config := &config.RabbitmqOptions{ - RabbitmqHostOptions: rabbitmqHostOptions, - } - conn, err := types.NewRabbitMQConnection(config) - if err != nil { - return err - } - - mqBus, err = bus2.NewRabbitmqBus( - config, - serializer, - g.logger, - conn, - rabbitmqBuilderFunc) - if err != nil { - return err + //isConnectable := isConnectable(g.logger, g.defaultOptions) + //if !isConnectable { + // return g.PopulateContainerOptions(context.Background(), t, options...) + //} + + var rabbitmqoptions *config.RabbitmqHostOptions + if err = pool.Retry(func() error { + rabbitmqoptions = &config.RabbitmqHostOptions{ + UserName: g.defaultOptions.UserName, + Password: g.defaultOptions.Password, + HostName: g.defaultOptions.Host, + VirtualHost: g.defaultOptions.VirtualHost, + Port: g.defaultOptions.HostPort, + HttpPort: g.defaultOptions.HttpPort, } return nil }); err != nil { - g.logger.Errorf("Could not connect to docker: %s", err) + log.Fatalf("Could not connect to docker: %s", err) return nil, err } - return mqBus, nil + return rabbitmqoptions, nil } func (g *rabbitmqDockerTest) Cleanup(ctx context.Context) error { @@ -187,12 +151,27 @@ func (g *rabbitmqDockerTest) getRunOptions( return runOptions } -func isConnectable(logger logger.Logger, options *contracts.RabbitMQContainerOptions) bool { +func isConnectable( + logger logger.Logger, + options *contracts.RabbitMQContainerOptions, +) bool { conn, err := amqp091.Dial( - fmt.Sprintf("amqp://%s:%s@%s:%d", options.UserName, options.Password, options.Host, options.HostPort), + fmt.Sprintf( + "amqp://%s:%s@%s:%d", + options.UserName, + options.Password, + options.Host, + options.HostPort, + ), ) if err != nil { - logError(logger, options.UserName, options.Password, options.Host, options.HostPort) + logError( + logger, + options.UserName, + options.Password, + options.Host, + options.HostPort, + ) return false } @@ -200,24 +179,48 @@ func isConnectable(logger logger.Logger, options *contracts.RabbitMQContainerOpt defer conn.Close() if err != nil || (conn != nil && conn.IsClosed()) { - logError(logger, options.UserName, options.Password, options.Host, options.HostPort) + logError( + logger, + options.UserName, + options.Password, + options.Host, + options.HostPort, + ) return false } logger.Infof( "Opened rabbitmq connection on host: %s", - fmt.Sprintf("amqp://%s:%s@%s:%d", options.UserName, options.Password, options.Host, options.HostPort), + fmt.Sprintf( + "amqp://%s:%s@%s:%d", + options.UserName, + options.Password, + options.Host, + options.HostPort, + ), ) return true } -func logError(logger logger.Logger, userName string, password string, host string, hostPort int) { +func logError( + logger logger.Logger, + userName string, + password string, + host string, + hostPort int, +) { // we should not use `t.Error` or `t.Errorf` for logging errors because it will `fail` our test at the end and, we just should use logs without error like log.Error (not log.Fatal) logger.Errorf( fmt.Sprintf( "Error in creating rabbitmq connection with %s", - fmt.Sprintf("amqp://%s:%s@%s:%d", userName, password, host, hostPort), + fmt.Sprintf( + "amqp://%s:%s@%s:%d", + userName, + password, + host, + hostPort, + ), ), ) } diff --git a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container_test.go b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container_test.go index 954ccf0d..f4b79837 100644 --- a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container_test.go +++ b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_container_test.go @@ -5,11 +5,17 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + rabbitmq2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" rabbitmqConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging/consumer" @@ -17,37 +23,50 @@ import ( uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/require" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_RabbitMQ_Container(t *testing.T) { ctx := context.Background() fakeConsumer := consumer.NewRabbitMQFakeTestConsumerHandler[*ProducerConsumerMessage]() - defaultLogger.SetupDefaultLogger() - eventSerializer := serializer.NewDefaultEventSerializer(json.NewDefaultSerializer()) - rabbitmq, err := NewRabbitMQDockerTest( - defaultLogger.Logger, - ).Start(ctx, t, eventSerializer, func(builder rabbitmqConfigurations.RabbitMQConfigurationBuilder) { - builder.AddConsumer(ProducerConsumerMessage{}, - func(consumerBuilder consumerConfigurations.RabbitMQConsumerConfigurationBuilder) { - consumerBuilder.WithHandlers( - func(handlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { - handlerBuilder.AddHandler(fakeConsumer) - }, - ) - }) - }) + var rabbitmqbus bus.Bus - require.NoError(t, err) - require.NotNil(t, rabbitmq) + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + rabbitmq2.ModuleFunc( + func(l logger.Logger) rabbitmqConfigurations.RabbitMQConfigurationBuilderFuc { + return func(builder configurations.RabbitMQConfigurationBuilder) { + builder.AddConsumer( + ProducerConsumerMessage{}, + func(consumerBuilder consumerConfigurations.RabbitMQConsumerConfigurationBuilder) { + consumerBuilder.WithHandlers( + func(handlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { + handlerBuilder.AddHandler(fakeConsumer) + }, + ) + }, + ) + } + }, + ), + fx.Decorate(RabbitmqDockerTestContainerOptionsDecorator(t, ctx)), + fx.Populate(&rabbitmqbus), + ).RequireStart() + + require.NotNil(t, rabbitmqbus) - err = rabbitmq.Start(ctx) + err := rabbitmqbus.Start(ctx) require.NoError(t, err) - // wait for consumers ready to consume before publishing messages (for preventing messages lost) - time.Sleep(time.Second * 1) + //// wait for consumers ready to consume before publishing messages (for preventing messages lost) + time.Sleep(time.Second * 2) - err = rabbitmq.PublishMessage( + err = rabbitmqbus.PublishMessage( context.Background(), &ProducerConsumerMessage{ Data: "ssssssssss", diff --git a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_fx.go b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_fx.go index 7cf7af6b..73311f36 100644 --- a/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_fx.go +++ b/internal/pkg/test/containers/dockertest/rabbitmq/rabbitmq_fx.go @@ -10,7 +10,7 @@ import ( var RabbitmqDockerTestContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *config.RabbitmqOptions, logger logger.Logger) (*config.RabbitmqOptions, error) { - rabbitmqHostOptions, err := NewRabbitMQDockerTest(logger).CreatingContainerOptions(ctx, t) + rabbitmqHostOptions, err := NewRabbitMQDockerTest(logger).PopulateContainerOptions(ctx, t) c.RabbitmqHostOptions = rabbitmqHostOptions return c, err diff --git a/internal/pkg/test/containers/gnomock/gorm/gorm_container.go b/internal/pkg/test/containers/gnomock/gorm/gorm_container.go index 91292a0a..c573b12d 100644 --- a/internal/pkg/test/containers/gnomock/gorm/gorm_container.go +++ b/internal/pkg/test/containers/gnomock/gorm/gorm_container.go @@ -5,7 +5,7 @@ import ( "log" "testing" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "emperror.dev/errors" @@ -34,7 +34,7 @@ func NewGnoMockGormContainer() contracts.GormContainer { } } -func (g *gnoMockGormContainer) CreatingContainerOptions( +func (g *gnoMockGormContainer) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.PostgresContainerOptions, @@ -70,7 +70,7 @@ func (g *gnoMockGormContainer) Start( t *testing.T, options ...*contracts.PostgresContainerOptions, ) (*gorm.DB, error) { - gormOptions, err := g.CreatingContainerOptions(ctx, t, options...) + gormOptions, err := g.PopulateContainerOptions(ctx, t, options...) if err != nil { return nil, err } diff --git a/internal/pkg/test/containers/testcontainer/eventstoredb/eveentstoredb_container_test.go b/internal/pkg/test/containers/testcontainer/eventstoredb/eveentstoredb_container_test.go index e85211d0..b4c7ebbb 100644 --- a/internal/pkg/test/containers/testcontainer/eventstoredb/eveentstoredb_container_test.go +++ b/internal/pkg/test/containers/testcontainer/eventstoredb/eveentstoredb_container_test.go @@ -4,17 +4,30 @@ import ( "context" "testing" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/EventStore/EventStore-Client-Go/esdb" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_Custom_EventStoreDB_Container(t *testing.T) { - defaultLogger.SetupDefaultLogger() + var esdbClient *esdb.Client + ctx := context.Background() - esdbInstance, err := NewEventstoreDBTestContainers(defaultLogger.Logger).Start(context.Background(), t) - require.NoError(t, err) - - assert.NotNil(t, esdbInstance) + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + eventstroredb.ModuleFunc(func() { + }), + fx.Decorate(EventstoreDBContainerOptionsDecorator(t, ctx)), + fx.Populate(&esdbClient), + ).RequireStart() } diff --git a/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_container.go b/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_container.go index 6c71df30..0b7682a2 100644 --- a/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_container.go +++ b/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_container.go @@ -40,7 +40,7 @@ func NewEventstoreDBTestContainers(l logger.Logger) contracts.EventstoreDBContai } } -func (g *eventstoredbTestContainers) CreatingContainerOptions( +func (g *eventstoredbTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.EventstoreDBContainerOptions, @@ -101,7 +101,7 @@ func (g *eventstoredbTestContainers) Start( t *testing.T, options ...*contracts.EventstoreDBContainerOptions, ) (*esdb.Client, error) { - eventstoredbOptions, err := g.CreatingContainerOptions(ctx, t, options...) + eventstoredbOptions, err := g.PopulateContainerOptions(ctx, t, options...) if err != nil { return nil, err } diff --git a/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_fx.go b/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_fx.go index 42a4ae09..7d08b66c 100644 --- a/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_fx.go +++ b/internal/pkg/test/containers/testcontainer/eventstoredb/eventstoredb_fx.go @@ -10,7 +10,7 @@ import ( var EventstoreDBContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *config.EventStoreDbOptions, logger logger.Logger) (*config.EventStoreDbOptions, error) { - newOption, err := NewEventstoreDBTestContainers(logger).CreatingContainerOptions(ctx, t) + newOption, err := NewEventstoreDBTestContainers(logger).PopulateContainerOptions(ctx, t) if err != nil { return nil, err } @@ -21,7 +21,7 @@ var EventstoreDBContainerOptionsDecorator = func(t *testing.T, ctx context.Conte } var ReplaceEventStoreContainerOptions = func(t *testing.T, options *config.EventStoreDbOptions, ctx context.Context, logger logger.Logger) error { - newOption, err := NewEventstoreDBTestContainers(logger).CreatingContainerOptions(ctx, t) + newOption, err := NewEventstoreDBTestContainers(logger).PopulateContainerOptions(ctx, t) if err != nil { return err } diff --git a/internal/pkg/test/containers/testcontainer/gorm/gorm_container.go b/internal/pkg/test/containers/testcontainer/gorm/gorm_container.go index 7b601369..b28bc5e0 100644 --- a/internal/pkg/test/containers/testcontainer/gorm/gorm_container.go +++ b/internal/pkg/test/containers/testcontainer/gorm/gorm_container.go @@ -6,8 +6,8 @@ import ( "testing" "time" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "emperror.dev/errors" @@ -43,7 +43,7 @@ func NewGormTestContainers(l logger.Logger) contracts.GormContainer { } } -func (g *gormTestContainers) CreatingContainerOptions( +func (g *gormTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.PostgresContainerOptions, @@ -85,7 +85,7 @@ func (g *gormTestContainers) CreatingContainerOptions( isConnectable := isConnectable(ctx, g.logger, g.defaultOptions) if !isConnectable { - return g.CreatingContainerOptions(context.Background(), t, options...) + return g.PopulateContainerOptions(context.Background(), t, options...) } g.container = dbContainer @@ -106,7 +106,7 @@ func (g *gormTestContainers) Start( t *testing.T, options ...*contracts.PostgresContainerOptions, ) (*gorm.DB, error) { - gormOptions, err := g.CreatingContainerOptions(ctx, t, options...) + gormOptions, err := g.PopulateContainerOptions(ctx, t, options...) if err != nil { return nil, err } diff --git a/internal/pkg/test/containers/testcontainer/gorm/gorm_container_test.go b/internal/pkg/test/containers/testcontainer/gorm/gorm_container_test.go index e394f8ff..f7c4f563 100644 --- a/internal/pkg/test/containers/testcontainer/gorm/gorm_container_test.go +++ b/internal/pkg/test/containers/testcontainer/gorm/gorm_container_test.go @@ -5,23 +5,37 @@ import ( "testing" "time" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/postgres" "github.com/testcontainers/testcontainers-go/wait" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + "gorm.io/gorm" ) func Test_Custom_Gorm_Container(t *testing.T) { ctx := context.Background() - defaultLogger.SetupDefaultLogger() - gorm, err := NewGormTestContainers(defaultLogger.Logger).Start(ctx, t) - require.NoError(t, err) + var gorm *gorm.DB + + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + gormPostgres.Module, + fx.Decorate(GormContainerOptionsDecorator(t, ctx)), + fx.Populate(&gorm), + ).RequireStart() assert.NotNil(t, gorm) } diff --git a/internal/pkg/test/containers/testcontainer/gorm/gorm_fx.go b/internal/pkg/test/containers/testcontainer/gorm/gorm_fx.go index 237f61fe..95a51ea3 100644 --- a/internal/pkg/test/containers/testcontainer/gorm/gorm_fx.go +++ b/internal/pkg/test/containers/testcontainer/gorm/gorm_fx.go @@ -4,12 +4,12 @@ import ( "context" "testing" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" ) var GormContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *gormPostgres.GormOptions, logger logger.Logger) (*gormPostgres.GormOptions, error) { - return NewGormTestContainers(logger).CreatingContainerOptions(ctx, t) + return NewGormTestContainers(logger).PopulateContainerOptions(ctx, t) } } diff --git a/internal/pkg/test/containers/testcontainer/mongo/mongo_container.go b/internal/pkg/test/containers/testcontainer/mongo/mongo_container.go index 7ed4d962..9dc18477 100644 --- a/internal/pkg/test/containers/testcontainer/mongo/mongo_container.go +++ b/internal/pkg/test/containers/testcontainer/mongo/mongo_container.go @@ -48,7 +48,7 @@ func NewMongoTestContainers(l logger.Logger) contracts.MongoContainer { } } -func (g *mongoTestContainers) CreatingContainerOptions( +func (g *mongoTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.MongoContainerOptions, @@ -76,7 +76,10 @@ func (g *mongoTestContainers) CreatingContainerOptions( }) // get a free random host hostPort - hostPort, err := dbContainer.MappedPort(ctx, nat.Port(g.defaultOptions.Port)) + hostPort, err := dbContainer.MappedPort( + ctx, + nat.Port(g.defaultOptions.Port), + ) if err != nil { return nil, err } @@ -89,7 +92,7 @@ func (g *mongoTestContainers) CreatingContainerOptions( isConnectable := isConnectable(ctx, g.logger, g.defaultOptions) if !isConnectable { - return g.CreatingContainerOptions(context.Background(), t, options...) + return g.PopulateContainerOptions(context.Background(), t, options...) } g.container = dbContainer @@ -106,24 +109,6 @@ func (g *mongoTestContainers) CreatingContainerOptions( return option, nil } -func (g *mongoTestContainers) Start( - ctx context.Context, - t *testing.T, - options ...*contracts.MongoContainerOptions, -) (*mongo.Client, error) { - mongoOptions, err := g.CreatingContainerOptions(ctx, t, options...) - if err != nil { - return nil, err - } - - db, err := mongodb.NewMongoDB(mongoOptions) - if err != nil { - return nil, err - } - - return db, nil -} - func (g *mongoTestContainers) Cleanup(ctx context.Context) error { if err := g.container.Terminate(ctx); err != nil { return errors.WrapIf(err, "failed to terminate container: %s") @@ -158,10 +143,15 @@ func (g *mongoTestContainers) getRunOptions( } containerReq := testcontainers.ContainerRequest{ - Image: fmt.Sprintf("%s:%s", g.defaultOptions.ImageName, g.defaultOptions.Tag), + Image: fmt.Sprintf( + "%s:%s", + g.defaultOptions.ImageName, + g.defaultOptions.Tag, + ), ExposedPorts: []string{g.defaultOptions.Port}, - WaitingFor: wait.ForListeningPort(nat.Port(g.defaultOptions.Port)).WithPollInterval(2 * time.Second), - Hostname: g.defaultOptions.Host, + WaitingFor: wait.ForListeningPort(nat.Port(g.defaultOptions.Port)). + WithPollInterval(2 * time.Second), + Hostname: g.defaultOptions.Host, HostConfigModifier: func(hostConfig *container.HostConfig) { hostConfig.AutoRemove = true }, @@ -174,7 +164,11 @@ func (g *mongoTestContainers) getRunOptions( return containerReq } -func isConnectable(ctx context.Context, logger logger.Logger, mongoOptions *contracts.MongoContainerOptions) bool { +func isConnectable( + ctx context.Context, + logger logger.Logger, + mongoOptions *contracts.MongoContainerOptions, +) bool { uriAddress := fmt.Sprintf( "mongodb://%s:%s@%s:%d", mongoOptions.UserName, @@ -187,7 +181,12 @@ func isConnectable(ctx context.Context, logger logger.Logger, mongoOptions *cont SetMaxConnIdleTime(maxConnIdleTime). SetMinPoolSize(minPoolSize). SetMaxPoolSize(maxPoolSize) - opt = opt.SetAuth(options.Credential{Username: mongoOptions.UserName, Password: mongoOptions.Password}) + opt = opt.SetAuth( + options.Credential{ + Username: mongoOptions.UserName, + Password: mongoOptions.Password, + }, + ) mongoClient, err := mongo.Connect(ctx, opt) @@ -206,7 +205,10 @@ func isConnectable(ctx context.Context, logger logger.Logger, mongoOptions *cont return false } logger.Infof( - "Opened mongodb connection on host: %s:%d", mongoOptions.Host, mongoOptions.HostPort) + "Opened mongodb connection on host: %s:%d", + mongoOptions.Host, + mongoOptions.HostPort, + ) return true } diff --git a/internal/pkg/test/containers/testcontainer/mongo/mongo_container_test.go b/internal/pkg/test/containers/testcontainer/mongo/mongo_container_test.go index 958c88a4..61141b13 100644 --- a/internal/pkg/test/containers/testcontainer/mongo/mongo_container_test.go +++ b/internal/pkg/test/containers/testcontainer/mongo/mongo_container_test.go @@ -4,17 +4,33 @@ import ( "context" "testing" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_Custom_Mongo_Container(t *testing.T) { - defaultLogger.SetupDefaultLogger() + ctx := context.Background() - mongo, err := NewMongoTestContainers(defaultLogger.Logger).Start(context.Background(), t) - require.NoError(t, err) + var mongoClient *mongo.Client - assert.NotNil(t, mongo) + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + mongodb.Module, + fx.Decorate(MongoContainerOptionsDecorator(t, ctx)), + fx.Populate(&mongoClient), + ).RequireStart() + + assert.NotNil(t, mongoClient) } diff --git a/internal/pkg/test/containers/testcontainer/mongo/mongo_fx.go b/internal/pkg/test/containers/testcontainer/mongo/mongo_fx.go index f9ed16a6..daa323b2 100644 --- a/internal/pkg/test/containers/testcontainer/mongo/mongo_fx.go +++ b/internal/pkg/test/containers/testcontainer/mongo/mongo_fx.go @@ -10,6 +10,6 @@ import ( var MongoContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *mongodb.MongoDbOptions, logger logger.Logger) (*mongodb.MongoDbOptions, error) { - return NewMongoTestContainers(logger).CreatingContainerOptions(ctx, t) + return NewMongoTestContainers(logger).PopulateContainerOptions(ctx, t) } } diff --git a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container.go b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container.go index 2fa22079..97c9848e 100644 --- a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container.go +++ b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container.go @@ -7,7 +7,7 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgres_pgx" + postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgrespgx" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "emperror.dev/errors" @@ -40,7 +40,7 @@ func NewPostgresPgxContainers(l logger.Logger) contracts.PostgresPgxContainer { } } -func (g *postgresPgxTestContainers) CreatingContainerOptions( +func (g *postgresPgxTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.PostgresContainerOptions, @@ -97,7 +97,7 @@ func (g *postgresPgxTestContainers) Start( t *testing.T, options ...*contracts.PostgresContainerOptions, ) (*postgres.Pgx, error) { - postgresPgxOptions, err := g.CreatingContainerOptions(ctx, t, options...) + postgresPgxOptions, err := g.PopulateContainerOptions(ctx, t, options...) if err != nil { return nil, err } diff --git a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container_test.go b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container_test.go index 7f6d7436..9e746273 100644 --- a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container_test.go +++ b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_container_test.go @@ -4,16 +4,16 @@ import ( "context" "testing" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func Test_Custom_PostgresPgx_Container(t *testing.T) { - defaultLogger.SetupDefaultLogger() - - gorm, err := NewPostgresPgxContainers(defaultLogger.Logger).Start(context.Background(), t) + gorm, err := NewPostgresPgxContainers( + defaultLogger.GetLogger(), + ).Start(context.Background(), t) require.NoError(t, err) assert.NotNil(t, gorm) diff --git a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_fx.go b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_fx.go index 9ad471e8..161288af 100644 --- a/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_fx.go +++ b/internal/pkg/test/containers/testcontainer/postgrespxg/postgrespgx_fx.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgres_pgx" + postgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgrespgx" ) var PostgresPgxContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *postgres.PostgresPgxOptions, logger logger.Logger) (*postgres.PostgresPgxOptions, error) { - return NewPostgresPgxContainers(logger).CreatingContainerOptions(ctx, t) + return NewPostgresPgxContainers(logger).PopulateContainerOptions(ctx, t) } } diff --git a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container.go b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container.go index 9c3af822..c5edf961 100644 --- a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container.go +++ b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container.go @@ -6,13 +6,8 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" - bus2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/contracts" "emperror.dev/errors" @@ -51,7 +46,7 @@ func NewRabbitMQTestContainers(l logger.Logger) contracts.RabbitMQContainer { } } -func (g *rabbitmqTestContainers) CreatingContainerOptions( +func (g *rabbitmqTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.RabbitMQContainerOptions, @@ -60,7 +55,6 @@ func (g *rabbitmqTestContainers) CreatingContainerOptions( // https://dev.to/remast/go-integration-tests-using-testcontainers-9o5 containerReq := g.getRunOptions(options...) - // TODO: Using Parallel Container dbContainer, err := testcontainers.GenericContainer( ctx, testcontainers.GenericContainerRequest{ @@ -109,7 +103,7 @@ func (g *rabbitmqTestContainers) CreatingContainerOptions( isConnectable := IsConnectable(g.logger, g.defaultOptions) if !isConnectable { - return g.CreatingContainerOptions(context.Background(), t, options...) + return g.PopulateContainerOptions(context.Background(), t, options...) } g.container = dbContainer @@ -126,45 +120,6 @@ func (g *rabbitmqTestContainers) CreatingContainerOptions( return option, nil } -func (g *rabbitmqTestContainers) Start( - ctx context.Context, - t *testing.T, - serializer serializer.EventSerializer, - rabbitmqBuilderFunc configurations.RabbitMQConfigurationBuilderFuc, - options ...*contracts.RabbitMQContainerOptions, -) (bus.Bus, error) { - rabbitHostOptions, err := g.CreatingContainerOptions(ctx, t, options...) - if err != nil { - return nil, err - } - - g.logger.Infof( - "rabbitmq connection is on host: %s", - rabbitHostOptions.AmqpEndPoint(), - ) - - rabbitmqConfig := &config.RabbitmqOptions{ - RabbitmqHostOptions: rabbitHostOptions, - } - conn, err := types.NewRabbitMQConnection(rabbitmqConfig) - if err != nil { - return nil, err - } - - mqBus, err := bus2.NewRabbitmqBus( - rabbitmqConfig, - serializer, - g.logger, - conn, - rabbitmqBuilderFunc, - ) - if err != nil { - return nil, err - } - - return mqBus, nil -} - func (g *rabbitmqTestContainers) Cleanup(ctx context.Context) error { if err := g.container.Terminate(ctx); err != nil { return errors.WrapIf(err, "failed to terminate container: %s") @@ -258,6 +213,7 @@ func IsConnectable( options.Password, ) _, err = rmqc.ListExchanges() + if err != nil { logger.Errorf( "Error in creating rabbitmq connection with http host: %s", diff --git a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container_test.go b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container_test.go index 81840d62..8d9b1970 100644 --- a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container_test.go +++ b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_container_test.go @@ -5,11 +5,17 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + messageConsumer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + rabbitmq2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" rabbitmqConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" consumerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging/consumer" @@ -17,37 +23,50 @@ import ( uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/require" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_Custom_RabbitMQ_Container(t *testing.T) { ctx := context.Background() fakeConsumer := consumer.NewRabbitMQFakeTestConsumerHandler[*ProducerConsumerMessage]() - defaultLogger.SetupDefaultLogger() - eventSerializer := serializer.NewDefaultEventSerializer(json.NewDefaultSerializer()) - rabbitmq, err := NewRabbitMQTestContainers( - defaultLogger.Logger, - ).Start(ctx, t, eventSerializer, func(builder rabbitmqConfigurations.RabbitMQConfigurationBuilder) { - builder.AddConsumer(ProducerConsumerMessage{}, - func(consumerBuilder consumerConfigurations.RabbitMQConsumerConfigurationBuilder) { - consumerBuilder.WithHandlers( - func(handlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { - handlerBuilder.AddHandler(fakeConsumer) - }, - ) - }) - }) + var rabbitmqbus bus.Bus - require.NoError(t, err) - require.NotNil(t, rabbitmq) + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + rabbitmq2.ModuleFunc( + func(l logger.Logger) rabbitmqConfigurations.RabbitMQConfigurationBuilderFuc { + return func(builder configurations.RabbitMQConfigurationBuilder) { + builder.AddConsumer( + ProducerConsumerMessage{}, + func(consumerBuilder consumerConfigurations.RabbitMQConsumerConfigurationBuilder) { + consumerBuilder.WithHandlers( + func(handlerBuilder messageConsumer.ConsumerHandlerConfigurationBuilder) { + handlerBuilder.AddHandler(fakeConsumer) + }, + ) + }, + ) + } + }, + ), + fx.Decorate(RabbitmqContainerOptionsDecorator(t, ctx)), + fx.Populate(&rabbitmqbus), + ).RequireStart() + + require.NotNil(t, rabbitmqbus) - err = rabbitmq.Start(ctx) + err := rabbitmqbus.Start(ctx) require.NoError(t, err) - // wait for consumers ready to consume before publishing messages (for preventing messages lost) + //// wait for consumers ready to consume before publishing messages (for preventing messages lost) time.Sleep(time.Second * 1) - err = rabbitmq.PublishMessage( + err = rabbitmqbus.PublishMessage( context.Background(), &ProducerConsumerMessage{ Data: "ssssssssss", diff --git a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_fx.go b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_fx.go index 3dcc0e5d..4dd0a4e2 100644 --- a/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_fx.go +++ b/internal/pkg/test/containers/testcontainer/rabbitmq/rabbitmq_fx.go @@ -10,7 +10,7 @@ import ( var RabbitmqContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *config.RabbitmqOptions, logger logger.Logger) (*config.RabbitmqOptions, error) { - rabbitmqHostOptions, err := NewRabbitMQTestContainers(logger).CreatingContainerOptions(ctx, t) + rabbitmqHostOptions, err := NewRabbitMQTestContainers(logger).PopulateContainerOptions(ctx, t) c.RabbitmqHostOptions = rabbitmqHostOptions return c, err diff --git a/internal/pkg/test/containers/testcontainer/redis/redis_container.go b/internal/pkg/test/containers/testcontainer/redis/redis_container.go index c2dcb052..f45526aa 100644 --- a/internal/pkg/test/containers/testcontainer/redis/redis_container.go +++ b/internal/pkg/test/containers/testcontainer/redis/redis_container.go @@ -38,7 +38,7 @@ func NewRedisTestContainers(l logger.Logger) contracts.RedisContainer { } } -func (g *redisTestContainers) CreatingContainerOptions( +func (g *redisTestContainers) PopulateContainerOptions( ctx context.Context, t *testing.T, options ...*contracts.RedisContainerOptions, @@ -66,7 +66,10 @@ func (g *redisTestContainers) CreatingContainerOptions( }) // get a free random host hostPort - hostPort, err := dbContainer.MappedPort(ctx, nat.Port(g.defaultOptions.Port)) + hostPort, err := dbContainer.MappedPort( + ctx, + nat.Port(g.defaultOptions.Port), + ) if err != nil { return nil, err } @@ -79,7 +82,7 @@ func (g *redisTestContainers) CreatingContainerOptions( isConnectable := isConnectable(ctx, g.logger, g.defaultOptions) if !isConnectable { - return g.CreatingContainerOptions(context.Background(), t, options...) + return g.PopulateContainerOptions(context.Background(), t, options...) } g.container = dbContainer @@ -93,21 +96,6 @@ func (g *redisTestContainers) CreatingContainerOptions( return reidsOptions, nil } -func (g *redisTestContainers) Start( - ctx context.Context, - t *testing.T, - options ...*contracts.RedisContainerOptions, -) (redis.UniversalClient, error) { - redisOptions, err := g.CreatingContainerOptions(ctx, t, options...) - - db := redis2.NewRedisClient(redisOptions) - if err != nil { - return nil, err - } - - return db, nil -} - func (g *redisTestContainers) Cleanup(ctx context.Context) error { if err := g.container.Terminate(ctx); err != nil { return errors.WrapIf(err, "failed to terminate container: %s") @@ -136,7 +124,11 @@ func (g *redisTestContainers) getRunOptions( } containerReq := testcontainers.ContainerRequest{ - Image: fmt.Sprintf("%s:%s", g.defaultOptions.ImageName, g.defaultOptions.Tag), + Image: fmt.Sprintf( + "%s:%s", + g.defaultOptions.ImageName, + g.defaultOptions.Tag, + ), ExposedPorts: []string{g.defaultOptions.Port}, WaitingFor: wait.ForListeningPort(nat.Port(g.defaultOptions.Port)). WithPollInterval(2 * time.Second), @@ -147,7 +139,11 @@ func (g *redisTestContainers) getRunOptions( return containerReq } -func isConnectable(ctx context.Context, logger logger.Logger, options *contracts.RedisContainerOptions) bool { +func isConnectable( + ctx context.Context, + logger logger.Logger, + options *contracts.RedisContainerOptions, +) bool { redisClient := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%d", options.Host, options.HostPort), }) @@ -158,13 +154,18 @@ func isConnectable(ctx context.Context, logger logger.Logger, options *contracts if err != nil { // we should not use `t.Error` or `t.Errorf` for logging errors because it will `fail` our test at the end and, we just should use logs without error like log.Error (not log.Fatal) logger.Errorf( - "Error in creating redis connection with %s:%d", options.Host, options.HostPort) + "Error in creating redis connection with %s:%d", + options.Host, + options.HostPort, + ) return false } logger.Infof( - "Opened redis connection on host: %s:%d", options.Host, options.HostPort, + "Opened redis connection on host: %s:%d", + options.Host, + options.HostPort, ) return true diff --git a/internal/pkg/test/containers/testcontainer/redis/redis_container_test.go b/internal/pkg/test/containers/testcontainer/redis/redis_container_test.go index 07d0ea78..3f5dd7af 100644 --- a/internal/pkg/test/containers/testcontainer/redis/redis_container_test.go +++ b/internal/pkg/test/containers/testcontainer/redis/redis_container_test.go @@ -4,17 +4,32 @@ import ( "context" "testing" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + redis2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/redis" + "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" ) func Test_Custom_Redis_Container(t *testing.T) { - defaultLogger.SetupDefaultLogger() + ctx := context.Background() + var redisClient redis.UniversalClient - redis, err := NewRedisTestContainers(defaultLogger.Logger).Start(context.Background(), t) - require.NoError(t, err) + fxtest.New(t, + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + core.Module, + redis2.Module, + fx.Decorate(RedisContainerOptionsDecorator(t, ctx)), + fx.Populate(&redisClient), + ).RequireStart() - assert.NotNil(t, redis) + assert.NotNil(t, redisClient) } diff --git a/internal/pkg/test/containers/testcontainer/redis/redis_fx.go b/internal/pkg/test/containers/testcontainer/redis/redis_fx.go index 418274a1..88cd70cf 100644 --- a/internal/pkg/test/containers/testcontainer/redis/redis_fx.go +++ b/internal/pkg/test/containers/testcontainer/redis/redis_fx.go @@ -10,6 +10,6 @@ import ( var RedisContainerOptionsDecorator = func(t *testing.T, ctx context.Context) interface{} { return func(c *redis.RedisOptions, logger logger.Logger) (*redis.RedisOptions, error) { - return NewRedisTestContainers(logger).CreatingContainerOptions(ctx, t) + return NewRedisTestContainers(logger).PopulateContainerOptions(ctx, t) } } diff --git a/internal/pkg/test/messaging/consumer/rabbitmq_fake_consumer.go b/internal/pkg/test/messaging/consumer/rabbitmq_fake_consumer.go index a32ee866..e0919e7e 100644 --- a/internal/pkg/test/messaging/consumer/rabbitmq_fake_consumer.go +++ b/internal/pkg/test/messaging/consumer/rabbitmq_fake_consumer.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" ) diff --git a/internal/pkg/test/messaging/utils.go b/internal/pkg/test/messaging/utils.go index 270956cd..14788cc7 100644 --- a/internal/pkg/test/messaging/utils.go +++ b/internal/pkg/test/messaging/utils.go @@ -3,10 +3,10 @@ package messaging import ( "context" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/utils" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/utils" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging/consumer" ) diff --git a/internal/pkg/utils/error_utils/errors.go b/internal/pkg/utils/errorutils/errors.go similarity index 87% rename from internal/pkg/utils/error_utils/errors.go rename to internal/pkg/utils/errorutils/errors.go index a704ed6e..18229d13 100644 --- a/internal/pkg/utils/error_utils/errors.go +++ b/internal/pkg/utils/errorutils/errors.go @@ -5,8 +5,8 @@ import ( "runtime/debug" "strings" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/contracts" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/contracts" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "emperror.dev/errors" ) @@ -24,7 +24,7 @@ func CheckErrMessages(err error, messages ...string) bool { return false } -// ErrorsWithStack returns a string contains grpc_errors messages in the stack with its stack trace levels for given error +// ErrorsWithStack returns a string contains errors messages in the stack with its stack trace levels for given error func ErrorsWithStack(err error) string { res := fmt.Sprintf("%+v\n", err) return res @@ -94,6 +94,7 @@ func RootStackTrace(err error) string { func HandlePanic() { if r := recover(); r != nil { - defaultLogger.Logger.Error("stacktrace from panic: \n" + string(debug.Stack())) + defaultLogger.GetLogger(). + Error("stacktrace from panic: \n" + string(debug.Stack())) } } diff --git a/internal/pkg/utils/error_utils/errors_test.go b/internal/pkg/utils/errorutils/errors_test.go similarity index 100% rename from internal/pkg/utils/error_utils/errors_test.go rename to internal/pkg/utils/errorutils/errors_test.go diff --git a/internal/pkg/validation/pipeline/validation_pipeline.go b/internal/pkg/validation/pipeline/validation_pipeline.go new file mode 100644 index 00000000..a8ab86c5 --- /dev/null +++ b/internal/pkg/validation/pipeline/validation_pipeline.go @@ -0,0 +1,34 @@ +package pipeline + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/validation" + + "github.com/mehdihadeli/go-mediatr" +) + +type mediatorValidationPipeline struct { + logger logger.Logger +} + +func NewMediatorValidationPipeline(l logger.Logger) mediatr.PipelineBehavior { + return &mediatorValidationPipeline{logger: l} +} + +func (m mediatorValidationPipeline) Handle( + ctx context.Context, + request interface{}, + next mediatr.RequestHandlerFunc, +) (interface{}, error) { + v, ok := request.(validation.Validator) + if ok { + err := v.Validate() + if err != nil { + return nil, err + } + } + + return next(ctx) +} diff --git a/internal/pkg/validation/validation.go b/internal/pkg/validation/validation.go new file mode 100644 index 00000000..bcf5f758 --- /dev/null +++ b/internal/pkg/validation/validation.go @@ -0,0 +1,5 @@ +package validation + +type Validator interface { + Validate() error +} diff --git a/internal/services/catalog_read_service/.env b/internal/services/catalog_read_service/.env deleted file mode 100644 index c307987a..00000000 --- a/internal/services/catalog_read_service/.env +++ /dev/null @@ -1 +0,0 @@ -PROJECT_NAME=catalog_read_service diff --git a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/commands/delete_product_handler.go b/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/commands/delete_product_handler.go deleted file mode 100644 index ae581fd2..00000000 --- a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/commands/delete_product_handler.go +++ /dev/null @@ -1,103 +0,0 @@ -package commands - -import ( - "context" - "fmt" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" - - "github.com/mehdihadeli/go-mediatr" - attribute2 "go.opentelemetry.io/otel/attribute" -) - -type DeleteProductCommand struct { - log logger.Logger - mongoRepository data.ProductRepository - redisRepository data.ProductCacheRepository - tracer tracing.AppTracer -} - -func NewDeleteProductHandler( - log logger.Logger, - repository data.ProductRepository, - redisRepository data.ProductCacheRepository, - tracer tracing.AppTracer, -) *DeleteProductCommand { - return &DeleteProductCommand{ - log: log, - mongoRepository: repository, - redisRepository: redisRepository, - tracer: tracer, - } -} - -func (c *DeleteProductCommand) Handle( - ctx context.Context, - command *DeleteProduct, -) (*mediatr.Unit, error) { - ctx, span := c.tracer.Start(ctx, "DeleteProductCommand.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductId.String())) - span.SetAttributes(attribute.Object("Command", command)) - product, err := c.mongoRepository.GetProductByProductId(ctx, command.ProductId.String()) - - defer span.End() - - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - fmt.Sprintf( - "[DeleteProductHandler_Handle.GetProductById] error in fetching product with productId %s in the mongo repository", - command.ProductId, - ), - ), - ) - } - if product == nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewNotFoundErrorWrap( - err, - fmt.Sprintf( - "[DeleteProductHandler_Handle.GetProductById] product with productId %s not found", - command.ProductId, - ), - ), - ) - } - - if err := c.mongoRepository.DeleteProductByID(ctx, product.Id); err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[DeleteProductHandler_Handle.DeleteProductByID] error in deleting product in the mongo repository", - ), - ) - } - - c.log.Infof("(product deleted) id: {%s}", product.Id) - - err = c.redisRepository.DeleteProduct(ctx, product.Id) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[DeleteProductHandler_Handle.DeleteProduct] error in deleting product in the redis repository", - ), - ) - } - - c.log.Infow( - fmt.Sprintf("[DeleteProductCommand.Handle] product with id: {%s} deleted", product.Id), - logger.Fields{"ProductId": command.ProductId, "Id": product.Id}, - ) - - return &mediatr.Unit{}, nil -} diff --git a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go b/internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go deleted file mode 100644 index 6bf878de..00000000 --- a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go +++ /dev/null @@ -1,21 +0,0 @@ -package infrastructure - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" -) - -type InfrastructureConfigurator struct { - contracts.Application -} - -func NewInfrastructureConfigurator(app contracts.Application) *InfrastructureConfigurator { - return &InfrastructureConfigurator{ - Application: app, - } -} - -func (ic *InfrastructureConfigurator) ConfigInfrastructures() { - ic.ResolveFunc(func() error { - return nil - }) -} diff --git a/internal/services/catalog_write_service/.env b/internal/services/catalog_write_service/.env deleted file mode 100644 index aa77c30f..00000000 --- a/internal/services/catalog_write_service/.env +++ /dev/null @@ -1 +0,0 @@ -PROJECT_NAME=catalog_write_service diff --git a/internal/services/catalog_write_service/internal/products/configurations/mediatr/mediator_configurations.go b/internal/services/catalog_write_service/internal/products/configurations/mediatr/mediator_configurations.go deleted file mode 100644 index e1df43a5..00000000 --- a/internal/services/catalog_write_service/internal/products/configurations/mediatr/mediator_configurations.go +++ /dev/null @@ -1,73 +0,0 @@ -package mediatr - -import ( - logger2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - createProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - createProductV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - deleteProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/commands" - getProductByIdDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/dtos" - getProductByIdQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - getProductsDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/dtos" - getProductsQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/queries" - searchProductsDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/dtos" - searchProductsQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/queries" - updateProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" - - "github.com/mehdihadeli/go-mediatr" -) - -func ConfigProductsMediator( - logger logger2.Logger, - uow data.CatalogUnitOfWork, - productRepository data.ProductRepository, - producer producer.Producer, - tracer tracing.AppTracer, -) error { - // https://stackoverflow.com/questions/72034479/how-to-implement-generic-interfaces - err := mediatr.RegisterRequestHandler[*createProductCommandV1.CreateProduct, *createProductV1.CreateProductResponseDto]( - createProductCommandV1.NewCreateProductHandler(logger, uow, producer, tracer), - ) - if err != nil { - return err - } - - err = mediatr.RegisterRequestHandler[*getProductsQueryV1.GetProducts, *getProductsDtosV1.GetProductsResponseDto]( - getProductsQueryV1.NewGetProductsHandler(logger, productRepository, tracer), - ) - if err != nil { - return err - } - - err = mediatr.RegisterRequestHandler[*searchProductsQueryV1.SearchProducts, *searchProductsDtosV1.SearchProductsResponseDto]( - searchProductsQueryV1.NewSearchProductsHandler(logger, productRepository, tracer), - ) - if err != nil { - return err - } - - err = mediatr.RegisterRequestHandler[*updateProductCommandV1.UpdateProduct, *mediatr.Unit]( - updateProductCommandV1.NewUpdateProductHandler(logger, uow, producer, tracer), - ) - if err != nil { - return err - } - - err = mediatr.RegisterRequestHandler[*deleteProductCommandV1.DeleteProduct, *mediatr.Unit]( - deleteProductCommandV1.NewDeleteProductHandler(logger, uow, producer, tracer), - ) - if err != nil { - return err - } - - err = mediatr.RegisterRequestHandler[*getProductByIdQueryV1.GetProductById, *getProductByIdDtosV1.GetProductByIdResponseDto]( - getProductByIdQueryV1.NewGetProductByIdHandler(logger, productRepository, tracer), - ) - if err != nil { - return err - } - - return nil -} diff --git a/internal/services/catalog_write_service/internal/products/configurations/products_module_configurator.go b/internal/services/catalog_write_service/internal/products/configurations/products_module_configurator.go deleted file mode 100644 index a13481f1..00000000 --- a/internal/services/catalog_write_service/internal/products/configurations/products_module_configurator.go +++ /dev/null @@ -1,69 +0,0 @@ -package configurations - -import ( - contracts2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - grpcServer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mappings" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mediatr" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc" - productsservice "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" - - googleGrpc "google.golang.org/grpc" -) - -type ProductsModuleConfigurator struct { - contracts2.Application -} - -func NewProductsModuleConfigurator( - fxapp contracts2.Application, -) *ProductsModuleConfigurator { - return &ProductsModuleConfigurator{ - Application: fxapp, - } -} - -func (c *ProductsModuleConfigurator) ConfigureProductsModule() { - c.ResolveFunc( - func(logger logger.Logger, uow data.CatalogUnitOfWork, productRepository data.ProductRepository, producer producer.Producer, tracer tracing.AppTracer) error { - // Config Products Mediators - err := mediatr.ConfigProductsMediator(logger, uow, productRepository, producer, tracer) - if err != nil { - return err - } - - // cfg Products Mappings - err = mappings.ConfigureProductsMappings() - if err != nil { - return err - } - - return nil - }, - ) -} - -func (c *ProductsModuleConfigurator) MapProductsEndpoints() { - // Config Products Http Endpoints - c.ResolveFunc(func(endpointParams params.ProductsEndpointsParams) { - for _, endpoint := range endpointParams.Endpoints { - endpoint.MapEndpoint() - } - }) - - // Config Products Grpc Endpoints - c.ResolveFunc( - func(catalogsGrpcServer grpcServer.GrpcServer, grpcService *grpc.ProductGrpcServiceServer) error { - catalogsGrpcServer.GrpcServiceBuilder().RegisterRoutes(func(server *googleGrpc.Server) { - productsservice.RegisterProductsServiceServer(server, grpcService) - }) - - return nil - }, - ) -} diff --git a/internal/services/catalog_write_service/internal/products/contracts/data/catalog_unit_of_work.go b/internal/services/catalog_write_service/internal/products/contracts/data/catalog_unit_of_work.go deleted file mode 100644 index 44d4f22a..00000000 --- a/internal/services/catalog_write_service/internal/products/contracts/data/catalog_unit_of_work.go +++ /dev/null @@ -1,10 +0,0 @@ -package data - -import "context" - -type CatalogUnitOfWorkActionFunc func(catalogContext CatalogContext) error - -type CatalogUnitOfWork interface { - // Do execute the given CatalogUnitOfWorkActionFunc atomically (inside a DB transaction). - Do(ctx context.Context, action CatalogUnitOfWorkActionFunc) error -} diff --git a/internal/services/catalog_write_service/internal/products/contracts/data/catalogs_context.go b/internal/services/catalog_write_service/internal/products/contracts/data/catalogs_context.go deleted file mode 100644 index adf3fc9c..00000000 --- a/internal/services/catalog_write_service/internal/products/contracts/data/catalogs_context.go +++ /dev/null @@ -1,8 +0,0 @@ -package data - -// CatalogContext provides access to datastores that can be -// used inside a Unit-of-Work. All data changes done through -// them will be executed atomically (inside a DB transaction). -type CatalogContext interface { - Products() ProductRepository -} diff --git a/internal/services/catalog_write_service/internal/products/contracts/params/product_endpoints_param.go b/internal/services/catalog_write_service/internal/products/contracts/params/product_endpoints_param.go deleted file mode 100644 index ee29e693..00000000 --- a/internal/services/catalog_write_service/internal/products/contracts/params/product_endpoints_param.go +++ /dev/null @@ -1,13 +0,0 @@ -package params - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - - "go.uber.org/fx" -) - -type ProductsEndpointsParams struct { - fx.In - - Endpoints []route.Endpoint `group:"product-routes"` -} diff --git a/internal/services/catalog_write_service/internal/products/data/uow/catalog_context.go b/internal/services/catalog_write_service/internal/products/data/uow/catalog_context.go deleted file mode 100644 index cfc0a8c8..00000000 --- a/internal/services/catalog_write_service/internal/products/data/uow/catalog_context.go +++ /dev/null @@ -1,13 +0,0 @@ -package uow - -import ( - data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" -) - -type catalogContext struct { - productRepository data2.ProductRepository -} - -func (c *catalogContext) Products() data2.ProductRepository { - return c.productRepository -} diff --git a/internal/services/catalog_write_service/internal/products/data/uow/catalogs_unit_of_work.go b/internal/services/catalog_write_service/internal/products/data/uow/catalogs_unit_of_work.go deleted file mode 100644 index 76d8bd2b..00000000 --- a/internal/services/catalog_write_service/internal/products/data/uow/catalogs_unit_of_work.go +++ /dev/null @@ -1,60 +0,0 @@ -package uow - -// https://blog.devgenius.io/go-golang-unit-of-work-and-generics-5e9fb00ec996 -// https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application -// https://dev.to/techschoolguru/a-clean-way-to-implement-database-transaction-in-golang-2ba - -import ( - "context" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/repositories" - - "gorm.io/gorm" -) - -type catalogUnitOfWork[TContext data2.CatalogContext] struct { - logger logger.Logger - db *gorm.DB - tracer tracing.AppTracer -} - -func NewCatalogsUnitOfWork( - logger logger.Logger, - db *gorm.DB, - tracer tracing.AppTracer, -) data2.CatalogUnitOfWork { - return &catalogUnitOfWork[data2.CatalogContext]{logger: logger, db: db, tracer: tracer} -} - -func (c *catalogUnitOfWork[TContext]) Do( - ctx context.Context, - action data2.CatalogUnitOfWorkActionFunc, -) error { - // https://gorm.io/docs/transactions.html#Transaction - return c.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - catalog := &catalogContext{ - productRepository: repositories.NewPostgresProductRepository(c.logger, tx, c.tracer), - } - - defer func() { - r := recover() - if r != nil { - tx.WithContext(ctx).Rollback() - err, _ := r.(error) - if err != nil { - c.logger.Errorf( - "panic tn the transaction, rolling back transaction with panic err: %+v", - err, - ) - } else { - c.logger.Errorf("panic tn the transaction, rolling back transaction with panic message: %+v", r) - } - } - }() - - return action(catalog) - }) -} diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product.go b/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product.go deleted file mode 100644 index 624cfe1c..00000000 --- a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product.go +++ /dev/null @@ -1,44 +0,0 @@ -package createProductCommand - -import ( - "time" - - validation "github.com/go-ozzo/ozzo-validation" - uuid "github.com/satori/go.uuid" -) - -// https://echo.labstack.com/guide/request/ -// https://github.com/go-playground/validator - -type CreateProduct struct { - ProductID uuid.UUID - Name string - Description string - Price float64 - CreatedAt time.Time -} - -func NewCreateProduct(name string, description string, price float64) (*CreateProduct, error) { - command := &CreateProduct{ - ProductID: uuid.NewV4(), - Name: name, - Description: description, - Price: price, - CreatedAt: time.Now(), - } - err := command.Validate() - if err != nil { - return nil, err - } - return command, nil -} - -func (c *CreateProduct) Validate() error { - return validation.ValidateStruct(c, - validation.Field(&c.ProductID, validation.Required), - validation.Field(&c.Name, validation.Required, validation.Length(0, 255)), - validation.Field(&c.Description, validation.Required, validation.Length(0, 5000)), - validation.Field(&c.Price, validation.Required, validation.Min(0.0).Exclusive()), - validation.Field(&c.CreatedAt, validation.Required), - ) -} diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product_handler.go b/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product_handler.go deleted file mode 100644 index 217d1425..00000000 --- a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/commands/create_product_handler.go +++ /dev/null @@ -1,123 +0,0 @@ -package createProductCommand - -import ( - "context" - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/events/integration_events" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - - attribute2 "go.opentelemetry.io/otel/attribute" -) - -type CreateProductHandler struct { - log logger.Logger - uow data.CatalogUnitOfWork - rabbitmqProducer producer.Producer - tracer tracing.AppTracer -} - -func NewCreateProductHandler( - log logger.Logger, - uow data.CatalogUnitOfWork, - rabbitmqProducer producer.Producer, - tracer tracing.AppTracer, -) *CreateProductHandler { - return &CreateProductHandler{ - log: log, - uow: uow, - rabbitmqProducer: rabbitmqProducer, - tracer: tracer, - } -} - -func (c *CreateProductHandler) Handle( - ctx context.Context, - command *CreateProduct, -) (*dtos.CreateProductResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "CreateProductHandler.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductID.String())) - span.SetAttributes(attribute.Object("Command", command)) - defer span.End() - - product := &models.Product{ - ProductId: command.ProductID, - Name: command.Name, - Description: command.Description, - Price: command.Price, - CreatedAt: command.CreatedAt, - } - - var createProductResult *dtos.CreateProductResponseDto - - err := c.uow.Do(ctx, func(catalogContext data.CatalogContext) error { - createdProduct, err := catalogContext.Products().CreateProduct(ctx, product) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrapWithCode( - err, - http.StatusConflict, - "[CreateProductHandler.CreateProduct] product already exists", - ), - ) - } - productDto, err := mapper.Map[*dtoV1.ProductDto](createdProduct) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[CreateProductHandler.Map] error in the mapping ProductDto", - ), - ) - } - - productCreated := integrationEvents.NewProductCreatedV1(productDto) - - err = c.rabbitmqProducer.PublishMessage(ctx, productCreated, nil) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[CreateProductHandler.PublishMessage] error in publishing ProductCreated integration_events event", - ), - ) - } - - c.log.Infow( - fmt.Sprintf( - "[CreateProductHandler.Handle] ProductCreated message with messageId `%s` published to the rabbitmq broker", - productCreated.MessageId, - ), - logger.Fields{"MessageId": productCreated.MessageId}, - ) - - createProductResult = &dtos.CreateProductResponseDto{ProductID: product.ProductId} - - span.SetAttributes(attribute.Object("CreateProductResultDto", createProductResult)) - - c.log.Infow( - fmt.Sprintf( - "[CreateProductHandler.Handle] product with id '%s' created", - command.ProductID, - ), - logger.Fields{"ProductId": command.ProductID, "MessageId": productCreated.MessageId}, - ) - - return nil - }) - - return createProductResult, err -} diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go b/internal/services/catalog_write_service/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go deleted file mode 100644 index 02572e6a..00000000 --- a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go +++ /dev/null @@ -1,8 +0,0 @@ -package dtos - -import uuid "github.com/satori/go.uuid" - -// https://echo.labstack.com/guide/response/ -type CreateProductResponseDto struct { - ProductID uuid.UUID `json:"productId"` -} diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/endpoints/create_product_endpoint.go b/internal/services/catalog_write_service/internal/products/features/creating_product/v1/endpoints/create_product_endpoint.go deleted file mode 100644 index a745d35b..00000000 --- a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/endpoints/create_product_endpoint.go +++ /dev/null @@ -1,95 +0,0 @@ -package endpoints - -import ( - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - createProductCommand "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - - "emperror.dev/errors" - "github.com/labstack/echo/v4" - "github.com/mehdihadeli/go-mediatr" -) - -type createProductEndpoint struct { - params.ProductRouteParams -} - -func NewCreteProductEndpoint(params params.ProductRouteParams) route.Endpoint { - return &createProductEndpoint{ProductRouteParams: params} -} - -func (ep *createProductEndpoint) MapEndpoint() { - ep.ProductsGroup.POST("", ep.handler()) -} - -// CreateProduct -// @Tags Products -// @Summary Create product -// @Description Create new product item -// @Accept json -// @Produce json -// @Param CreateProductRequestDto body dtos.CreateProductRequestDto true "Product data" -// @Success 201 {object} dtos.CreateProductResponseDto -// @Router /api/v1/products [post] -func (ep *createProductEndpoint) handler() echo.HandlerFunc { - return func(c echo.Context) error { - ctx := c.Request().Context() - - ep.CatalogsMetrics.CreateProductHttpRequests.Add(ctx, 1) - - request := &dtos.CreateProductRequestDto{} - if err := c.Bind(request); err != nil { - badRequestErr := customErrors.NewBadRequestErrorWrap( - err, - "[createProductEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[createProductEndpoint_handler.Bind] err: %v", badRequestErr), - ) - } - - command, err := createProductCommand.NewCreateProduct( - request.Name, - request.Description, - request.Price, - ) - if err != nil { - validationErr := customErrors.NewValidationErrorWrap( - err, - "[createProductEndpoint_handler.StructCtx] command validation failed", - ) - ep.Logger.Errorf( - fmt.Sprintf("[createProductEndpoint_handler.StructCtx] err: {%v}", validationErr), - ) - return validationErr - } - - result, err := mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( - ctx, - command, - ) - if err != nil { - err = errors.WithMessage( - err, - "[createProductEndpoint_handler.Send] error in sending CreateProduct", - ) - ep.Logger.Errorw( - fmt.Sprintf( - "[createProductEndpoint_handler.Send] id: {%s}, err: {%v}", - command.ProductID, - err, - ), - logger.Fields{"ProductId": command.ProductID}, - ) - return err - } - - return c.JSON(http.StatusCreated, result) - } -} diff --git a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product.go b/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product.go deleted file mode 100644 index 1b125a26..00000000 --- a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product.go +++ /dev/null @@ -1,29 +0,0 @@ -package commands - -import ( - validation "github.com/go-ozzo/ozzo-validation" - "github.com/go-ozzo/ozzo-validation/is" - uuid "github.com/satori/go.uuid" -) - -type DeleteProduct struct { - ProductID uuid.UUID -} - -func NewDeleteProduct(productID uuid.UUID) (*DeleteProduct, error) { - command := &DeleteProduct{ProductID: productID} - err := command.Validate() - if err != nil { - return nil, err - } - - return command, nil -} - -func (p *DeleteProduct) Validate() error { - return validation.ValidateStruct( - p, - validation.Field(&p.ProductID, validation.Required), - validation.Field(&p.ProductID, is.UUIDv4), - ) -} diff --git a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product_handler.go b/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product_handler.go deleted file mode 100644 index d1aa93a8..00000000 --- a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/commands/delete_product_handler.go +++ /dev/null @@ -1,94 +0,0 @@ -package commands - -import ( - "context" - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/events/integration_events" - - "github.com/mehdihadeli/go-mediatr" - attribute2 "go.opentelemetry.io/otel/attribute" -) - -type DeleteProductHandler struct { - log logger.Logger - uow data.CatalogUnitOfWork - rabbitmqProducer producer.Producer - tracer tracing.AppTracer -} - -func NewDeleteProductHandler( - log logger.Logger, - uow data.CatalogUnitOfWork, - rabbitmqProducer producer.Producer, - tracer tracing.AppTracer, -) *DeleteProductHandler { - return &DeleteProductHandler{ - log: log, - uow: uow, - rabbitmqProducer: rabbitmqProducer, - tracer: tracer, - } -} - -func (c *DeleteProductHandler) Handle( - ctx context.Context, - command *DeleteProduct, -) (*mediatr.Unit, error) { - ctx, span := c.tracer.Start(ctx, "deleteProductHandler.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductID.String())) - span.SetAttributes(attribute.Object("Command", command)) - defer span.End() - - err := c.uow.Do(ctx, func(catalogContext data.CatalogContext) error { - if err := catalogContext.Products().DeleteProductByID(ctx, command.ProductID); err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrapWithCode( - err, - http.StatusNotFound, - "[DeleteProductHandler_Handle.DeleteProductByID] product not found", - ), - ) - } - - productDeleted := integrationEvents.NewProductDeletedV1(command.ProductID.String()) - err := c.rabbitmqProducer.PublishMessage(ctx, productDeleted, nil) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[DeleteProductHandler_Handle.PublishMessage] error in publishing 'ProductDeleted' message", - ), - ) - } - - c.log.Infow( - fmt.Sprintf( - "[DeleteProductHandler.Handle] ProductDeleted message with messageId '%s' published to the rabbitmq broker", - productDeleted.MessageId, - ), - logger.Fields{"MessageId": productDeleted.MessageId}, - ) - - c.log.Infow( - fmt.Sprintf( - "[DeleteProductHandler.Handle] product with id '%s' deleted", - command.ProductID, - ), - logger.Fields{"ProductId": command.ProductID}, - ) - - return nil - }) - - return &mediatr.Unit{}, err -} diff --git a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/endpoints/delete_product_endpoint.go b/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/endpoints/delete_product_endpoint.go deleted file mode 100644 index d30cf015..00000000 --- a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/endpoints/delete_product_endpoint.go +++ /dev/null @@ -1,91 +0,0 @@ -package endpoints - -import ( - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/dtos" - - "emperror.dev/errors" - "github.com/labstack/echo/v4" - "github.com/mehdihadeli/go-mediatr" -) - -type deleteProductEndpoint struct { - params.ProductRouteParams -} - -func NewDeleteProductEndpoint( - params params.ProductRouteParams, -) route.Endpoint { - return &deleteProductEndpoint{ProductRouteParams: params} -} - -func (ep *deleteProductEndpoint) MapEndpoint() { - ep.ProductsGroup.DELETE("/:id", ep.handler()) -} - -// DeleteProduct -// @Tags Products -// @Summary Delete product -// @Description Delete existing product -// @Accept json -// @Produce json -// @Success 204 -// @Param id path string true "Product ID" -// @Router /api/v1/products/{id} [delete] -func (ep *deleteProductEndpoint) handler() echo.HandlerFunc { - return func(c echo.Context) error { - ctx := c.Request().Context() - ep.CatalogsMetrics.DeleteProductHttpRequests.Add(ctx, 1) - - request := &dtos.DeleteProductRequestDto{} - if err := c.Bind(request); err != nil { - badRequestErr := customErrors.NewBadRequestErrorWrap( - err, - "[deleteProductEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[deleteProductEndpoint_handler.Bind] err: %v", badRequestErr), - ) - return badRequestErr - } - - command, err := commands.NewDeleteProduct(request.ProductID) - if err != nil { - validationErr := customErrors.NewValidationErrorWrap( - err, - "[deleteProductEndpoint_handler.StructCtx] command validation failed", - ) - ep.Logger.Errorf( - fmt.Sprintf("[deleteProductEndpoint_handler.StructCtx] err: {%v}", validationErr), - ) - return validationErr - } - - _, err = mediatr.Send[*commands.DeleteProduct, *mediatr.Unit](ctx, command) - - if err != nil { - err = errors.WithMessage( - err, - "[deleteProductEndpoint_handler.Send] error in sending DeleteProduct", - ) - ep.Logger.Errorw( - fmt.Sprintf( - "[deleteProductEndpoint_handler.Send] id: {%s}, err: {%v}", - command.ProductID, - err, - ), - logger.Fields{"ProductId": command.ProductID}, - ) - return err - } - - return c.NoContent(http.StatusNoContent) - } -} diff --git a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/endpoints/get_product_by_id_endpoint.go b/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/endpoints/get_product_by_id_endpoint.go deleted file mode 100644 index 7a98ac58..00000000 --- a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/endpoints/get_product_by_id_endpoint.go +++ /dev/null @@ -1,91 +0,0 @@ -package endpoints - -import ( - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/dtos" - getProductByIdQuery "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - - "emperror.dev/errors" - "github.com/labstack/echo/v4" - "github.com/mehdihadeli/go-mediatr" -) - -type getProductByIdEndpoint struct { - params.ProductRouteParams -} - -func NewGetProductByIdEndpoint( - params params.ProductRouteParams, -) route.Endpoint { - return &getProductByIdEndpoint{ProductRouteParams: params} -} - -func (ep *getProductByIdEndpoint) MapEndpoint() { - ep.ProductsGroup.GET("/:id", ep.handler()) -} - -// GetProductByID -// @Tags Products -// @Summary Get product by id -// @Description Get product by id -// @Accept json -// @Produce json -// @Param id path string true "Product ID" -// @Success 200 {object} dtos.GetProductByIdResponseDto -// @Router /api/v1/products/{id} [get] -func (ep *getProductByIdEndpoint) handler() echo.HandlerFunc { - return func(c echo.Context) error { - ctx := c.Request().Context() - ep.CatalogsMetrics.GetProductByIdHttpRequests.Add(ctx, 1) - - request := &dtos.GetProductByIdRequestDto{} - if err := c.Bind(request); err != nil { - badRequestErr := customErrors.NewBadRequestErrorWrap( - err, - "[getProductByIdEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[getProductByIdEndpoint_handler.Bind] err: %v", badRequestErr), - ) - return badRequestErr - } - - query, err := getProductByIdQuery.NewGetProductById(request.ProductId) - if err != nil { - validationErr := customErrors.NewValidationErrorWrap( - err, - "[getProductByIdEndpoint_handler.StructCtx] query validation failed", - ) - ep.Logger.Errorf("[getProductByIdEndpoint_handler.StructCtx] err: {%v}", validationErr) - return validationErr - } - - queryResult, err := mediatr.Send[*getProductByIdQuery.GetProductById, *dtos.GetProductByIdResponseDto]( - ctx, - query, - ) - if err != nil { - err = errors.WithMessage( - err, - "[getProductByIdEndpoint_handler.Send] error in sending GetProductById", - ) - ep.Logger.Errorw( - fmt.Sprintf( - "[getProductByIdEndpoint_handler.Send] id: {%s}, err: {%v}", - query.ProductID, - err, - ), - logger.Fields{"ProductId": query.ProductID}, - ) - return err - } - - return c.JSON(http.StatusOK, queryResult) - } -} diff --git a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id.go b/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id.go deleted file mode 100644 index 0dee457c..00000000 --- a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id.go +++ /dev/null @@ -1,28 +0,0 @@ -package getProductByIdQuery - -import ( - validation "github.com/go-ozzo/ozzo-validation" - "github.com/go-ozzo/ozzo-validation/is" - uuid "github.com/satori/go.uuid" -) - -// https://echo.labstack.com/guide/request/ -// https://github.com/go-playground/validator - -type GetProductById struct { - ProductID uuid.UUID -} - -func NewGetProductById(productId uuid.UUID) (*GetProductById, error) { - query := &GetProductById{ProductID: productId} - err := query.Validate() - if err != nil { - return nil, err - } - - return query, nil -} - -func (p *GetProductById) Validate() error { - return validation.ValidateStruct(p, validation.Field(&p.ProductID, validation.Required, is.UUIDv4)) -} diff --git a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler.go b/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler.go deleted file mode 100644 index a61ab286..00000000 --- a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler.go +++ /dev/null @@ -1,78 +0,0 @@ -package getProductByIdQuery - -import ( - "context" - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/dtos" - - attribute2 "go.opentelemetry.io/otel/attribute" -) - -type GetProductByIdHandler struct { - log logger.Logger - pgRepo data.ProductRepository - tracer tracing.AppTracer -} - -func NewGetProductByIdHandler( - log logger.Logger, - pgRepo data.ProductRepository, - tracer tracing.AppTracer, -) *GetProductByIdHandler { - return &GetProductByIdHandler{log: log, pgRepo: pgRepo, tracer: tracer} -} - -func (q *GetProductByIdHandler) Handle( - ctx context.Context, - query *GetProductById, -) (*dtos.GetProductByIdResponseDto, error) { - ctx, span := q.tracer.Start(ctx, "GetProductByIdHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - span.SetAttributes(attribute2.String("ProductId", query.ProductID.String())) - defer span.End() - - product, err := q.pgRepo.GetProductById(ctx, query.ProductID) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrapWithCode( - err, - http.StatusNotFound, - fmt.Sprintf( - "[GetProductByIdHandler_Handle.GetProductById] error in getting product with id %s in the repository", - query.ProductID.String(), - ), - ), - ) - } - - productDto, err := mapper.Map[*dtoV1.ProductDto](product) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductByIdHandler_Handle.Map] error in the mapping product", - ), - ) - } - - q.log.Infow( - fmt.Sprintf( - "[GetProductByIdHandler.Handle] product with id: {%s} fetched", - query.ProductID, - ), - logger.Fields{"ProductId": query.ProductID.String()}, - ) - - return &dtos.GetProductByIdResponseDto{Product: productDto}, nil -} diff --git a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products_handler.go b/internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products_handler.go deleted file mode 100644 index 26b453dd..00000000 --- a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products_handler.go +++ /dev/null @@ -1,63 +0,0 @@ -package queries - -import ( - "context" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/dtos" -) - -type GetProductsHandler struct { - log logger.Logger - pgRepo data.ProductRepository - tracer tracing.AppTracer -} - -func NewGetProductsHandler( - log logger.Logger, - pgRepo data.ProductRepository, - tracer tracing.AppTracer, -) *GetProductsHandler { - return &GetProductsHandler{log: log, pgRepo: pgRepo, tracer: tracer} -} - -func (c *GetProductsHandler) Handle( - ctx context.Context, - query *GetProducts, -) (*dtos.GetProductsResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "GetProductsHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - defer span.End() - - products, err := c.pgRepo.GetAllProducts(ctx, query.ListQuery) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductsHandler_Handle.GetAllProducts] error in getting products in the repository", - ), - ) - } - - listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto](products) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductsHandler_Handle.ListResultToListResultDto] error in the mapping ListResultToListResultDto", - ), - ) - } - - c.log.Info("[GetProductsHandler.Handle] products fetched") - - return &dtos.GetProductsResponseDto{Products: listResultDto}, nil -} diff --git a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products.go b/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products.go deleted file mode 100644 index 677db550..00000000 --- a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products.go +++ /dev/null @@ -1,30 +0,0 @@ -package queries - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - - validation "github.com/go-ozzo/ozzo-validation" -) - -type SearchProducts struct { - SearchText string - *utils.ListQuery -} - -func NewSearchProducts(searchText string, query *utils.ListQuery) (*SearchProducts, error) { - command := &SearchProducts{ - SearchText: searchText, - ListQuery: query, - } - - err := command.Validate() - if err != nil { - return nil, err - } - - return command, nil -} - -func (p *SearchProducts) Validate() error { - return validation.ValidateStruct(p, validation.Field(&p.SearchText, validation.Required)) -} diff --git a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products_handler.go b/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products_handler.go deleted file mode 100644 index f81e8c06..00000000 --- a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/queries/search_products_handler.go +++ /dev/null @@ -1,63 +0,0 @@ -package queries - -import ( - "context" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/dtos" -) - -type SearchProductsHandler struct { - log logger.Logger - pgRepo data.ProductRepository - tracer tracing.AppTracer -} - -func NewSearchProductsHandler( - log logger.Logger, - pgRepo data.ProductRepository, - tracer tracing.AppTracer, -) *SearchProductsHandler { - return &SearchProductsHandler{log: log, pgRepo: pgRepo, tracer: tracer} -} - -func (c *SearchProductsHandler) Handle( - ctx context.Context, - query *SearchProducts, -) (*dtos.SearchProductsResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "SearchProductsHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - defer span.End() - - products, err := c.pgRepo.SearchProducts(ctx, query.SearchText, query.ListQuery) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[SearchProductsHandler_Handle.SearchProducts] error in searching products in the repository", - ), - ) - } - - listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto](products) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[SearchProductsHandler_Handle.ListResultToListResultDto] error in the mapping ListResultToListResultDto", - ), - ) - } - - c.log.Info("[SearchProductsHandler.Handle] products fetched") - - return &dtos.SearchProductsResponseDto{Products: listResultDto}, nil -} diff --git a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product.go b/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product.go deleted file mode 100644 index 6900a236..00000000 --- a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product.go +++ /dev/null @@ -1,40 +0,0 @@ -package commands - -import ( - "time" - - validation "github.com/go-ozzo/ozzo-validation" - uuid "github.com/satori/go.uuid" -) - -type UpdateProduct struct { - ProductID uuid.UUID - Name string - Description string - Price float64 - UpdatedAt time.Time -} - -func NewUpdateProduct(productID uuid.UUID, name string, description string, price float64) (*UpdateProduct, error) { - command := &UpdateProduct{ - ProductID: productID, - Name: name, - Description: description, - Price: price, - UpdatedAt: time.Now(), - } - err := command.Validate() - if err != nil { - return nil, err - } - - return command, nil -} - -func (p *UpdateProduct) Validate() error { - return validation.ValidateStruct(p, validation.Field(&p.ProductID, validation.Required), - validation.Field(&p.Name, validation.Required, validation.Length(0, 255)), - validation.Field(&p.Description, validation.Required, validation.Length(0, 5000)), - validation.Field(&p.Price, validation.Required, validation.Min(0.0)), - validation.Field(&p.UpdatedAt, validation.Required)) -} diff --git a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product_handler.go b/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product_handler.go deleted file mode 100644 index d6c6f9a1..00000000 --- a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/commands/update_product_handler.go +++ /dev/null @@ -1,128 +0,0 @@ -package commands - -import ( - "context" - "fmt" - "net/http" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/events/integration_events" - - "github.com/mehdihadeli/go-mediatr" - attribute2 "go.opentelemetry.io/otel/attribute" -) - -type UpdateProductHandler struct { - log logger.Logger - uow data.CatalogUnitOfWork - rabbitmqProducer producer.Producer - tracer tracing.AppTracer -} - -func NewUpdateProductHandler( - log logger.Logger, - uow data.CatalogUnitOfWork, - rabbitmqProducer producer.Producer, - tracer tracing.AppTracer, -) *UpdateProductHandler { - return &UpdateProductHandler{ - log: log, - uow: uow, - rabbitmqProducer: rabbitmqProducer, - tracer: tracer, - } -} - -func (c *UpdateProductHandler) Handle( - ctx context.Context, - command *UpdateProduct, -) (*mediatr.Unit, error) { - ctx, span := c.tracer.Start(ctx, "UpdateProductHandler.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductID.String())) - span.SetAttributes(attribute.Object("Command", command)) - defer span.End() - - err := c.uow.Do(ctx, func(catalogContext data.CatalogContext) error { - product, err := catalogContext.Products().GetProductById(ctx, command.ProductID) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrapWithCode( - err, - http.StatusNotFound, - fmt.Sprintf( - "[UpdateProductHandler_Handle.GetProductById] product with id %s not found", - command.ProductID, - ), - ), - ) - } - - product.Name = command.Name - product.Price = command.Price - product.Description = command.Description - product.UpdatedAt = command.UpdatedAt - - updatedProduct, err := catalogContext.Products().UpdateProduct(ctx, product) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[UpdateProductHandler_Handle.UpdateProduct] error in updating product in the repository", - ), - ) - } - - productDto, err := mapper.Map[*dto.ProductDto](updatedProduct) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[UpdateProductHandler_Handle.Map] error in the mapping ProductDto", - ), - ) - } - - productUpdated := integration_events.NewProductUpdatedV1(productDto) - - err = c.rabbitmqProducer.PublishMessage(ctx, productUpdated, nil) - if err != nil { - return tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[UpdateProductHandler_Handle.PublishMessage] error in publishing 'ProductUpdated' message", - ), - ) - } - - c.log.Infow( - fmt.Sprintf( - "[UpdateProductHandler.Handle] product with id '%s' updated", - command.ProductID, - ), - logger.Fields{"ProductId": command.ProductID}, - ) - - c.log.Infow( - fmt.Sprintf( - "[DeleteProductHandler.Handle] ProductUpdated message with messageId `%s` published to the rabbitmq broker", - productUpdated.MessageId, - ), - logger.Fields{"MessageId": productUpdated.MessageId}, - ) - - return nil - }) - - return &mediatr.Unit{}, err -} diff --git a/internal/services/catalog_write_service/internal/products/mocks/testData/products.go b/internal/services/catalog_write_service/internal/products/mocks/testData/products.go deleted file mode 100644 index 63fb8476..00000000 --- a/internal/services/catalog_write_service/internal/products/mocks/testData/products.go +++ /dev/null @@ -1,27 +0,0 @@ -package testData - -import ( - "time" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - - "github.com/brianvoe/gofakeit/v6" - uuid "github.com/satori/go.uuid" -) - -var Products = []*models.Product{ - { - ProductId: uuid.NewV4(), - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.AdjectiveDescriptive(), - Price: gofakeit.Price(100, 1000), - }, - { - ProductId: uuid.NewV4(), - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.AdjectiveDescriptive(), - Price: gofakeit.Price(100, 1000), - }, -} diff --git a/internal/services/catalog_write_service/internal/products/models/product.go b/internal/services/catalog_write_service/internal/products/models/product.go deleted file mode 100644 index 9611ed70..00000000 --- a/internal/services/catalog_write_service/internal/products/models/product.go +++ /dev/null @@ -1,23 +0,0 @@ -package models - -import ( - "time" - - "github.com/goccy/go-json" - uuid "github.com/satori/go.uuid" -) - -// Product model -type Product struct { - ProductId uuid.UUID `json:"productId" gorm:"primaryKey"` - Name string `json:"name"` - Description string `json:"description"` - Price float64 `json:"price"` - CreatedAt time.Time `json:"createdAt"` // https://gorm.io/docs/models.html#gorm-Model - UpdatedAt time.Time `json:"updatedAt"` // https://gorm.io/docs/models.html#gorm-Model -} - -func (p *Product) String() string { - j, _ := json.Marshal(p) - return string(j) -} diff --git a/internal/services/catalog_write_service/internal/products/products_fx.go b/internal/services/catalog_write_service/internal/products/products_fx.go deleted file mode 100644 index dfe0d070..00000000 --- a/internal/services/catalog_write_service/internal/products/products_fx.go +++ /dev/null @@ -1,44 +0,0 @@ -package products - -import ( - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/repositories" - createProductV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/endpoints" - deleteProductV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/endpoints" - getProductByIdV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/endpoints" - getProductsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/endpoints" - searchProductsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/endpoints" - updateProductsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/endpoints" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc" - - "github.com/labstack/echo/v4" - "go.uber.org/fx" -) - -var Module = fx.Module( - "productsfx", - - // Other provides - fx.Provide(repositories.NewPostgresProductRepository), - fx.Provide(grpc.NewProductGrpcService), - - fx.Provide(fx.Annotate(func(catalogsServer customEcho.EchoHttpServer) *echo.Group { - var g *echo.Group - catalogsServer.RouteBuilder().RegisterGroupFunc("/api/v1", func(v1 *echo.Group) { - group := v1.Group("/products") - g = group - }) - - return g - }, fx.ResultTags(`name:"product-echo-group"`))), - - fx.Provide( - route.AsRoute(createProductV1.NewCreteProductEndpoint, "product-routes"), - route.AsRoute(updateProductsV1.NewUpdateProductEndpoint, "product-routes"), - route.AsRoute(getProductsV1.NewGetProductsEndpoint, "product-routes"), - route.AsRoute(searchProductsV1.NewSearchProductsEndpoint, "product-routes"), - route.AsRoute(getProductByIdV1.NewGetProductByIdEndpoint, "product-routes"), - route.AsRoute(deleteProductV1.NewDeleteProductEndpoint, "product-routes"), - ), -) diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_migration.go b/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_migration.go deleted file mode 100644 index c79f8ba9..00000000 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_migration.go +++ /dev/null @@ -1,18 +0,0 @@ -package catalogs - -import ( - "gorm.io/gorm" -) - -func (ic *CatalogsServiceConfigurator) migrateCatalogs(gorm *gorm.DB) error { - // - for complex migration and ability to back-track to specific migration revision it is better we use `goose`, but if we want to use built-in gorm migration we can also sync gorm with `atlas` integration migration versioning for getting migration history from grom changes - // - here I used goose for migration, with using cmd/migration file - // https://atlasgo.io/guides/orms/gorm - - //err := gorm.AutoMigrate(&models.Product{}) - //if err != nil { - // return err - //} - - return nil -} diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go b/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go deleted file mode 100644 index b3c5777b..00000000 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go +++ /dev/null @@ -1,21 +0,0 @@ -package infrastructure - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" -) - -type InfrastructureConfigurator struct { - contracts.Application -} - -func NewInfrastructureConfigurator(fxapp contracts.Application) *InfrastructureConfigurator { - return &InfrastructureConfigurator{ - Application: fxapp, - } -} - -func (ic *InfrastructureConfigurator) ConfigInfrastructures() { - ic.ResolveFunc(func() error { - return nil - }) -} diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_test_fx.go b/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_test_fx.go deleted file mode 100644 index 428b7c6d..00000000 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_test_fx.go +++ /dev/null @@ -1,10 +0,0 @@ -package infrastructure - -//var TestModule = func(t *testing.T, ctx context.Context) fx.Option { -// return fx.Module( -// "infrastructuretestfx", -// // Modules -// gorm.ModuleFunc(t, ctx), -// rabbitmq.ModuleFunc(t, ctx), -// ) -//} diff --git a/internal/services/catalog_write_service/internal/shared/test_fixtures/unit_test/unit_test_fixture.go b/internal/services/catalog_write_service/internal/shared/test_fixtures/unit_test/unit_test_fixture.go deleted file mode 100644 index b55976d6..00000000 --- a/internal/services/catalog_write_service/internal/shared/test_fixtures/unit_test/unit_test_fixture.go +++ /dev/null @@ -1,117 +0,0 @@ -package unit_test - -import ( - "fmt" - "testing" - - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - mocks3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/mocks" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/mocks/testData" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/mocks" - - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "go.opentelemetry.io/otel/trace" -) - -type UnitTestSharedFixture struct { - Cfg *config.AppOptions - Log logger.Logger - suite.Suite - Items []*models.Product - Uow *mocks.CatalogUnitOfWork - ProductRepository *mocks.ProductRepository - Bus *mocks3.Bus - Tracer trace.Tracer -} - -func NewUnitTestSharedFixture(t *testing.T) *UnitTestSharedFixture { - // we could use EmptyLogger if we don't want to log anything - defaultLogger.SetupDefaultLogger() - log := defaultLogger.Logger - cfg := &config.AppOptions{} - - err := configMapper() - require.NoError(t, err) - - // empty tracer, just for testing - nopetracer := trace.NewNoopTracerProvider() - testTracer := nopetracer.Tracer("test_tracer") - - unit := &UnitTestSharedFixture{ - Cfg: cfg, - Log: log, - Items: testData.Products, - Tracer: testTracer, - } - - return unit -} - -func configMapper() error { - err := mapper.CreateMap[*models.Product, *dto.ProductDto]() - if err != nil { - return err - } - - err = mapper.CreateMap[*dto.ProductDto, *models.Product]() - if err != nil { - return err - } - - return nil -} - -// //////////////Shared Hooks//////////////// -func (c *UnitTestSharedFixture) SetupTest() { - // create new mocks - productRepository := &mocks.ProductRepository{} - bus := &mocks3.Bus{} - uow := &mocks.CatalogUnitOfWork{} - catalogContext := &mocks.CatalogContext{} - - //// or just clear the mocks - //c.Bus.ExpectedCalls = nil - //c.Bus.Calls = nil - //c.Uow.ExpectedCalls = nil - //c.Uow.Calls = nil - //c.ProductRepository.ExpectedCalls = nil - //c.ProductRepository.Calls = nil - - uow.On("Products").Return(productRepository) - catalogContext.On("Products").Return(productRepository) - - var mockUOW *mock.Call - mockUOW = uow.On("Do", mock.Anything, mock.Anything). - Run(func(args mock.Arguments) { - fn, ok := args.Get(1).(data.CatalogUnitOfWorkActionFunc) - if !ok { - panic("argument mismatch") - } - fmt.Println(fn) - - mockUOW.Return(fn(catalogContext)) - }) - - mockUOW.Times(1) - bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything).Return(nil) - - c.Uow = uow - c.ProductRepository = productRepository - c.Bus = bus -} - -func (c *UnitTestSharedFixture) CleanupMocks() { - c.SetupTest() -} - -func (c *UnitTestSharedFixture) TearDownSuite() { - mapper.ClearMappings() -} diff --git a/internal/services/catalog_write_service/mocks/CatalogUnitOfWork.go b/internal/services/catalog_write_service/mocks/CatalogUnitOfWork.go deleted file mode 100644 index 886dfa9f..00000000 --- a/internal/services/catalog_write_service/mocks/CatalogUnitOfWork.go +++ /dev/null @@ -1,80 +0,0 @@ -// Code generated by mockery v2.30.16. DO NOT EDIT. - -package mocks - -import ( - context "context" - - data "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - mock "github.com/stretchr/testify/mock" -) - -// CatalogUnitOfWork is an autogenerated mock type for the CatalogUnitOfWork type -type CatalogUnitOfWork struct { - mock.Mock -} - -type CatalogUnitOfWork_Expecter struct { - mock *mock.Mock -} - -func (_m *CatalogUnitOfWork) EXPECT() *CatalogUnitOfWork_Expecter { - return &CatalogUnitOfWork_Expecter{mock: &_m.Mock} -} - -// Do provides a mock function with given fields: ctx, action -func (_m *CatalogUnitOfWork) Do(ctx context.Context, action data.CatalogUnitOfWorkActionFunc) error { - ret := _m.Called(ctx, action) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, data.CatalogUnitOfWorkActionFunc) error); ok { - r0 = rf(ctx, action) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CatalogUnitOfWork_Do_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Do' -type CatalogUnitOfWork_Do_Call struct { - *mock.Call -} - -// Do is a helper method to define mock.On call -// - ctx context.Context -// - action data.CatalogUnitOfWorkActionFunc -func (_e *CatalogUnitOfWork_Expecter) Do(ctx interface{}, action interface{}) *CatalogUnitOfWork_Do_Call { - return &CatalogUnitOfWork_Do_Call{Call: _e.mock.On("Do", ctx, action)} -} - -func (_c *CatalogUnitOfWork_Do_Call) Run(run func(ctx context.Context, action data.CatalogUnitOfWorkActionFunc)) *CatalogUnitOfWork_Do_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(data.CatalogUnitOfWorkActionFunc)) - }) - return _c -} - -func (_c *CatalogUnitOfWork_Do_Call) Return(_a0 error) *CatalogUnitOfWork_Do_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *CatalogUnitOfWork_Do_Call) RunAndReturn(run func(context.Context, data.CatalogUnitOfWorkActionFunc) error) *CatalogUnitOfWork_Do_Call { - _c.Call.Return(run) - return _c -} - -// NewCatalogUnitOfWork creates a new instance of CatalogUnitOfWork. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewCatalogUnitOfWork(t interface { - mock.TestingT - Cleanup(func()) -}) *CatalogUnitOfWork { - mock := &CatalogUnitOfWork{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/services/catalog_write_service/mocks/CatalogUnitOfWorkActionFunc.go b/internal/services/catalog_write_service/mocks/CatalogUnitOfWorkActionFunc.go deleted file mode 100644 index dea44acd..00000000 --- a/internal/services/catalog_write_service/mocks/CatalogUnitOfWorkActionFunc.go +++ /dev/null @@ -1,77 +0,0 @@ -// Code generated by mockery v2.30.16. DO NOT EDIT. - -package mocks - -import ( - data "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - mock "github.com/stretchr/testify/mock" -) - -// CatalogUnitOfWorkActionFunc is an autogenerated mock type for the CatalogUnitOfWorkActionFunc type -type CatalogUnitOfWorkActionFunc struct { - mock.Mock -} - -type CatalogUnitOfWorkActionFunc_Expecter struct { - mock *mock.Mock -} - -func (_m *CatalogUnitOfWorkActionFunc) EXPECT() *CatalogUnitOfWorkActionFunc_Expecter { - return &CatalogUnitOfWorkActionFunc_Expecter{mock: &_m.Mock} -} - -// Execute provides a mock function with given fields: catalogContext -func (_m *CatalogUnitOfWorkActionFunc) Execute(catalogContext data.CatalogContext) error { - ret := _m.Called(catalogContext) - - var r0 error - if rf, ok := ret.Get(0).(func(data.CatalogContext) error); ok { - r0 = rf(catalogContext) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CatalogUnitOfWorkActionFunc_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' -type CatalogUnitOfWorkActionFunc_Execute_Call struct { - *mock.Call -} - -// Execute is a helper method to define mock.On call -// - catalogContext data.CatalogContext -func (_e *CatalogUnitOfWorkActionFunc_Expecter) Execute(catalogContext interface{}) *CatalogUnitOfWorkActionFunc_Execute_Call { - return &CatalogUnitOfWorkActionFunc_Execute_Call{Call: _e.mock.On("Execute", catalogContext)} -} - -func (_c *CatalogUnitOfWorkActionFunc_Execute_Call) Run(run func(catalogContext data.CatalogContext)) *CatalogUnitOfWorkActionFunc_Execute_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(data.CatalogContext)) - }) - return _c -} - -func (_c *CatalogUnitOfWorkActionFunc_Execute_Call) Return(_a0 error) *CatalogUnitOfWorkActionFunc_Execute_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *CatalogUnitOfWorkActionFunc_Execute_Call) RunAndReturn(run func(data.CatalogContext) error) *CatalogUnitOfWorkActionFunc_Execute_Call { - _c.Call.Return(run) - return _c -} - -// NewCatalogUnitOfWorkActionFunc creates a new instance of CatalogUnitOfWorkActionFunc. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewCatalogUnitOfWorkActionFunc(t interface { - mock.TestingT - Cleanup(func()) -}) *CatalogUnitOfWorkActionFunc { - mock := &CatalogUnitOfWorkActionFunc{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/services/catalog_write_service/test/integration/products/features/creating_product/v1/create_product_test.go b/internal/services/catalog_write_service/test/integration/products/features/creating_product/v1/create_product_test.go deleted file mode 100644 index f9ef809d..00000000 --- a/internal/services/catalog_write_service/test/integration/products/features/creating_product/v1/create_product_test.go +++ /dev/null @@ -1,197 +0,0 @@ -//go:build integration -// +build integration - -package v1 - -import ( - "context" - "net/http" - "testing" - "time" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" - createProductCommand "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/events/integration_events" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" - - "github.com/brianvoe/gofakeit/v6" - "github.com/mehdihadeli/go-mediatr" - uuid "github.com/satori/go.uuid" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var integrationFixture *integration.IntegrationTestSharedFixture - -func TestCreateProduct(t *testing.T) { - RegisterFailHandler(Fail) - integrationFixture = integration.NewIntegrationTestSharedFixture(t) - RunSpecs(t, "Create Product Integration Tests") -} - -// https://specflow.org/learn/gherkin/#learn-gherkin -// scenario -var _ = Describe("Creating Product Feature", func() { - var ( - ctx context.Context - err error - command *createProductCommand.CreateProduct - result *dtos.CreateProductResponseDto - createdProduct *models.Product - id uuid.UUID - shouldPublish hypothesis.Hypothesis[*integrationEvents.ProductCreatedV1] - ) - - _ = BeforeEach(func() { - By("Seeding the required data") - integrationFixture.InitializeTest() - - id = integrationFixture.Items[0].ProductId - }) - - _ = AfterEach(func() { - By("Cleanup test data") - integrationFixture.DisposeTest() - }) - - _ = BeforeSuite(func() { - ctx = context.Background() - - // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually - err = integrationFixture.Bus.Start(context.Background()) - Expect(err).ShouldNot(HaveOccurred()) - - // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) - time.Sleep(1 * time.Second) - }) - - _ = AfterSuite(func() { - integrationFixture.Log.Info("TearDownSuite started") - err := integrationFixture.Bus.Stop() - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(1 * time.Second) - }) - - // "Scenario" step for testing creating a new product - Describe("Creating a new product and saving it to the database when product doesn't exists", func() { - Context("Given new product doesn't exists in the system", func() { - BeforeEach(func() { - command, err = createProductCommand.NewCreateProduct( - gofakeit.Name(), - gofakeit.AdjectiveDescriptive(), - gofakeit.Price(150, 6000), - ) - Expect(err).ToNot(HaveOccurred()) - Expect(command).ToNot(BeNil()) - }) - - When("the CreateProduct command is executed for non-existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( - ctx, - command, - ) - }) - - It("Should create the product successfully", func() { - Expect(err).NotTo(HaveOccurred()) - Expect(result).NotTo(BeNil()) - }) - - It("Should have a non-empty product ID matching the command ID", func() { - Expect(result.ProductID).To(Equal(command.ProductID)) - }) - - It("Should be able to retrieve the product from the database", func() { - createdProduct, err = integrationFixture.ProductRepository.GetProductById( - ctx, - result.ProductID, - ) - Expect(err).NotTo(HaveOccurred()) - - Expect(result).NotTo(BeNil()) - Expect(command.ProductID).To(Equal(result.ProductID)) - Expect(createdProduct).NotTo(BeNil()) - }) - }) - }) - }) - - // "Scenario" step for testing creating a product with duplicate data - Describe("Creating a new product with duplicate data and already exists product", func() { - Context("Given product already exists in the system", func() { - BeforeEach(func() { - command = &createProductCommand.CreateProduct{ - Name: gofakeit.Name(), - Description: gofakeit.AdjectiveDescriptive(), - Price: gofakeit.Price(150, 6000), - ProductID: id, - } - }) - - When("the CreateProduct command is executed for existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( - ctx, - command, - ) - }) - - It("Should return an error indicating duplicate record", func() { - Expect(err).To(HaveOccurred()) - Expect(customErrors.IsApplicationError(err, http.StatusConflict)).To(BeTrue()) - }) - - It("Should not return a result", func() { - Expect(result).To(BeNil()) - }) - }) - }) - }) - - // "Scenario" step for testing creating a product with duplicate data - Describe("Publishing ProductCreated event to the broker when product saved successfully", func() { - Context("Given new product doesn't exists in the system", func() { - BeforeEach(func() { - shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductCreatedV1]( - ctx, - integrationFixture.Bus, - nil, - ) - command, err = createProductCommand.NewCreateProduct( - gofakeit.Name(), - gofakeit.AdjectiveDescriptive(), - gofakeit.Price(150, 6000), - ) - Expect(err).ToNot(HaveOccurred()) - }) - - When("CreateProduct command is executed for non-existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( - ctx, - command, - ) - }) - - It("Should return no error", func() { - Expect(err).ToNot(HaveOccurred()) - }) - - It("Should return not nil result", func() { - Expect(result).ToNot(BeNil()) - }) - - It("Should publish ProductCreated event to the broker", func() { - // ensuring message published to the rabbitmq broker - shouldPublish.Validate(ctx, "there is no published message", time.Second*30) - }) - }) - }) - }) -}) diff --git a/internal/services/catalog_write_service/test/integration/products/features/deleting_product/v1/delete_product_test.go b/internal/services/catalog_write_service/test/integration/products/features/deleting_product/v1/delete_product_test.go deleted file mode 100644 index db6ba166..00000000 --- a/internal/services/catalog_write_service/test/integration/products/features/deleting_product/v1/delete_product_test.go +++ /dev/null @@ -1,176 +0,0 @@ -//go:build integration -// +build integration - -package v1 - -import ( - "context" - "net/http" - "testing" - "time" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/commands" - integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/events/integration_events" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" - - "github.com/mehdihadeli/go-mediatr" - uuid "github.com/satori/go.uuid" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var integrationFixture *integration.IntegrationTestSharedFixture - -func TestDeleteProduct(t *testing.T) { - RegisterFailHandler(Fail) - integrationFixture = integration.NewIntegrationTestSharedFixture(t) - RunSpecs(t, "Delete Product Integration Tests") -} - -// https://specflow.org/learn/gherkin/#learn-gherkin -// scenario -var _ = Describe("Delete Product Feature", func() { - var ( - ctx context.Context - err error - command *commands.DeleteProduct - result *mediatr.Unit - id uuid.UUID - notExistsId uuid.UUID - shouldPublish hypothesis.Hypothesis[*integrationEvents.ProductDeletedV1] - ) - - _ = BeforeEach(func() { - By("Seeding the required data") - integrationFixture.InitializeTest() - - id = integrationFixture.Items[0].ProductId - }) - - _ = AfterEach(func() { - By("Cleanup test data") - integrationFixture.DisposeTest() - }) - - _ = BeforeSuite(func() { - ctx = context.Background() - - // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually - err = integrationFixture.Bus.Start(context.Background()) - Expect(err).ShouldNot(HaveOccurred()) - - // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) - time.Sleep(1 * time.Second) - }) - - _ = AfterSuite(func() { - integrationFixture.Log.Info("TearDownSuite started") - err := integrationFixture.Bus.Stop() - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(1 * time.Second) - }) - - // "Scenario" step for testing deleting an existing product - Describe("Deleting an existing product from the database", func() { - Context("Given product already exists in the system", func() { - BeforeEach(func() { - shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductDeletedV1]( - ctx, - integrationFixture.Bus, - nil, - ) - command, err = commands.NewDeleteProduct(id) - Expect(err).ShouldNot(HaveOccurred()) - }) - - When("the DeleteProduct command is executed for existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.DeleteProduct, *mediatr.Unit]( - ctx, - command, - ) - }) - - It("Should not return an error", func() { - Expect(err).NotTo(HaveOccurred()) - }) - - It("Should delete the product from the database", func() { - deletedProduct, err := integrationFixture.ProductRepository.GetProductById(ctx, id) - Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("can't find the product with id"))) - Expect(deletedProduct).To(BeNil()) - }) - }) - }) - }) - - // "Scenario" step for testing deleting a non-existing product - Describe("Deleting a non-existing product from the database", func() { - Context("Given product does not exists in the system", func() { - BeforeEach(func() { - notExistsId = uuid.NewV4() - command, err = commands.NewDeleteProduct(notExistsId) - Expect(err).ShouldNot(HaveOccurred()) - }) - - When("the DeleteProduct command is executed for non-existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.DeleteProduct, *mediatr.Unit]( - ctx, - command, - ) - }) - - It("Should return an error", func() { - Expect(err).To(HaveOccurred()) - }) - - It("Should return a NotFound error", func() { - Expect(err).To(MatchError(ContainSubstring("product not found"))) - }) - - It("Should return a custom NotFound error", func() { - Expect(customErrors.IsApplicationError(err, http.StatusNotFound)).To(BeTrue()) - Expect(customErrors.IsNotFoundError(err)).To(BeTrue()) - }) - - It("Should not return a result", func() { - Expect(result).To(BeNil()) - }) - }) - }) - }) - - Describe("Publishing ProductDeleted event when product deleted successfully", func() { - Context("Given product already exists in the system", func() { - BeforeEach(func() { - shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductDeletedV1]( - ctx, - integrationFixture.Bus, - nil, - ) - command, err = commands.NewDeleteProduct(id) - Expect(err).ShouldNot(HaveOccurred()) - }) - - When("the DeleteProduct command is executed for existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.DeleteProduct, *mediatr.Unit]( - ctx, - command, - ) - }) - - It("Should publish ProductDeleted event to the broker", func() { - // ensuring message published to the rabbitmq broker - shouldPublish.Validate(ctx, "there is no published message", time.Second*30) - }) - }) - }) - }) -}) diff --git a/internal/services/catalog_write_service/test/integration/products/features/getting_product_by_id/v1/get_product_by_id_test.go b/internal/services/catalog_write_service/test/integration/products/features/getting_product_by_id/v1/get_product_by_id_test.go deleted file mode 100644 index 15836714..00000000 --- a/internal/services/catalog_write_service/test/integration/products/features/getting_product_by_id/v1/get_product_by_id_test.go +++ /dev/null @@ -1,143 +0,0 @@ -//go:build integration -// +build integration - -package v1 - -import ( - "context" - "net/http" - "testing" - "time" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/dtos" - getProductByIdQuery "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" - - "github.com/mehdihadeli/go-mediatr" - uuid "github.com/satori/go.uuid" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var integrationFixture *integration.IntegrationTestSharedFixture - -func TestGetProductById(t *testing.T) { - RegisterFailHandler(Fail) - integrationFixture = integration.NewIntegrationTestSharedFixture(t) - RunSpecs(t, "Get Product By ID Integration Tests") -} - -var _ = Describe("Get Product by ID Feature", func() { - var ( - ctx context.Context - id uuid.UUID - query *getProductByIdQuery.GetProductById - result *dtos.GetProductByIdResponseDto - err error - ) - - _ = BeforeEach(func() { - By("Seeding the required data") - integrationFixture.InitializeTest() - - id = integrationFixture.Items[0].ProductId - }) - - _ = AfterEach(func() { - By("Cleanup test data") - integrationFixture.DisposeTest() - }) - - _ = BeforeSuite(func() { - ctx = context.Background() - - // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually - err = integrationFixture.Bus.Start(context.Background()) - Expect(err).ShouldNot(HaveOccurred()) - - // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) - time.Sleep(1 * time.Second) - }) - - _ = AfterSuite(func() { - integrationFixture.Log.Info("TearDownSuite started") - err := integrationFixture.Bus.Stop() - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(1 * time.Second) - }) - - // "Scenario" step for testing returning an existing product with correct properties - Describe("Returning an existing product with valid Id from the database with correct properties", func() { - Context("Given products exists in the database", func() { - BeforeEach(func() { - query, err = getProductByIdQuery.NewGetProductById(id) - }) - - // "When" step - When("the GteProductById query is executed for existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*getProductByIdQuery.GetProductById, *dtos.GetProductByIdResponseDto]( - ctx, - query, - ) - }) - - // "Then" step - It("Should not return an error", func() { - Expect(err).To(BeNil()) - }) - - It("Should return a non-nil result", func() { - Expect(result).NotTo(BeNil()) - }) - - It("Should return a product with the correct ID", func() { - Expect(result.Product).NotTo(BeNil()) - Expect(result.Product.ProductId).To(Equal(id)) - }) - }) - }) - }) - - // "Scenario" step for testing returning a NotFound error when record does not exist - Describe("Returning a NotFound error when product with specific id does not exist", func() { - Context("Given products does not exists in the database", func() { - BeforeEach(func() { - // Generate a random UUID that does not exist in the database - id = uuid.NewV4() - query, err = getProductByIdQuery.NewGetProductById(id) - Expect(err).To(BeNil()) - }) - - // "When" step - When("the GteProductById query is executed for non-existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*getProductByIdQuery.GetProductById, *dtos.GetProductByIdResponseDto]( - ctx, - query, - ) - }) - - // "Then" step - It("Should return an error", func() { - Expect(err).To(HaveOccurred()) - }) - - It("Should return a NotFound error", func() { - Expect(err).To(MatchError(ContainSubstring("error in getting product with id"))) - }) - - It("Should return a custom NotFound error", func() { - Expect(customErrors.IsNotFoundError(err)).To(BeTrue()) - Expect(customErrors.IsApplicationError(err, http.StatusNotFound)).To(BeTrue()) - }) - - It("Should not return a result", func() { - Expect(result).To(BeNil()) - }) - }) - }) - }) -}) diff --git a/internal/services/catalog_write_service/test/integration/products/features/updating_product/v1/update_product_test.go b/internal/services/catalog_write_service/test/integration/products/features/updating_product/v1/update_product_test.go deleted file mode 100644 index 5ef22d36..00000000 --- a/internal/services/catalog_write_service/test/integration/products/features/updating_product/v1/update_product_test.go +++ /dev/null @@ -1,195 +0,0 @@ -//go:build integration -// +build integration - -package v1 - -import ( - "context" - "fmt" - "net/http" - "testing" - "time" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/events/integration_events" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" - - "github.com/mehdihadeli/go-mediatr" - uuid "github.com/satori/go.uuid" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -var integrationFixture *integration.IntegrationTestSharedFixture - -func TestUpdateProduct(t *testing.T) { - RegisterFailHandler(Fail) - integrationFixture = integration.NewIntegrationTestSharedFixture(t) - RunSpecs(t, "Updated Products Integration Tests") -} - -var _ = Describe("Update Product Feature", func() { - // Define variables to hold command and result data - var ( - ctx context.Context - existingProduct *models.Product - command *commands.UpdateProduct - result *mediatr.Unit - err error - id uuid.UUID - shouldPublish hypothesis.Hypothesis[*integration_events.ProductUpdatedV1] - ) - - _ = BeforeEach(func() { - By("Seeding the required data") - integrationFixture.InitializeTest() - - existingProduct = integrationFixture.Items[0] - }) - - _ = AfterEach(func() { - By("Cleanup test data") - integrationFixture.DisposeTest() - }) - - _ = BeforeSuite(func() { - ctx = context.Background() - - // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually - err = integrationFixture.Bus.Start(context.Background()) - Expect(err).ShouldNot(HaveOccurred()) - - // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) - time.Sleep(1 * time.Second) - }) - - _ = AfterSuite(func() { - integrationFixture.Log.Info("TearDownSuite started") - err := integrationFixture.Bus.Stop() - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(1 * time.Second) - }) - - // "Scenario" step for testing updating an existing product - Describe("Updating an existing product in the database", func() { - Context("Given product exists in the database", func() { - BeforeEach(func() { - command, err = commands.NewUpdateProduct( - existingProduct.ProductId, - "Updated Product Name", - existingProduct.Description, - existingProduct.Price, - ) - Expect(err).NotTo(HaveOccurred()) - }) - - // "When" step - When("the UpdateProduct command is executed", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.UpdateProduct, *mediatr.Unit](ctx, command) - }) - - // "Then" step - It("Should not return an error", func() { - Expect(err).NotTo(HaveOccurred()) - Expect(result).NotTo(BeNil()) - }) - - It("Should return a non-nil result", func() { - Expect(result).NotTo(BeNil()) - }) - - It("Should update the existing product in the database", func() { - updatedProduct, err := integrationFixture.ProductRepository.GetProductById( - ctx, - existingProduct.ProductId, - ) - Expect(err).To(BeNil()) - Expect(updatedProduct).NotTo(BeNil()) - Expect(updatedProduct.ProductId).To(Equal(existingProduct.ProductId)) - Expect(updatedProduct.Price).To(Equal(existingProduct.Price)) - Expect(updatedProduct.Name).NotTo(Equal(existingProduct.Name)) - }) - }) - }) - }) - - // "Scenario" step for testing updating a non-existing product - Describe("Updating a non-existing product in the database", func() { - Context("Given product not exists in the database", func() { - BeforeEach(func() { - // Generate a random ID that does not exist in the database - id = uuid.NewV4() - command, err = commands.NewUpdateProduct( - id, - "Updated Product Name", - "Updated Product Description", - 100, - ) - Expect(err).NotTo(HaveOccurred()) - }) - - // "When" step - When("the UpdateProduct command executed for non-existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.UpdateProduct, *mediatr.Unit](ctx, command) - }) - - // "Then" step - It("Should return an error", func() { - Expect(err).To(HaveOccurred()) - }) - It("Should not return a result", func() { - Expect(result).To(BeNil()) - }) - - It("Should return a NotFound error", func() { - Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("product with id %s not found", id)))) - }) - - It("Should return a custom NotFound error", func() { - Expect(customErrors.IsNotFoundError(err)).To(BeTrue()) - Expect(customErrors.IsApplicationError(err, http.StatusNotFound)).To(BeTrue()) - }) - }) - }) - }) - - // "Scenario" step for testing updating an existing product - Describe("Publishing ProductUpdated when product updated successfully", func() { - Context("Given product exists in the database", func() { - BeforeEach(func() { - command, err = commands.NewUpdateProduct( - existingProduct.ProductId, - "Updated Product Name", - existingProduct.Description, - existingProduct.Price, - ) - Expect(err).NotTo(HaveOccurred()) - - shouldPublish = messaging.ShouldProduced[*integration_events.ProductUpdatedV1]( - ctx, - integrationFixture.Bus, - nil, - ) - }) - - // "When" step - When("the UpdateProduct command is executed for existing product", func() { - BeforeEach(func() { - result, err = mediatr.Send[*commands.UpdateProduct, *mediatr.Unit](ctx, command) - }) - - It("Should publish ProductUpdated event to the broker", func() { - // ensuring message published to the rabbitmq broker - shouldPublish.Validate(ctx, "there is no published message", time.Second*30) - }) - }) - }) - }) -}) diff --git a/internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_handler_unit_test.go b/internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_handler_unit_test.go deleted file mode 100644 index 8662a280..00000000 --- a/internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_handler_unit_test.go +++ /dev/null @@ -1,180 +0,0 @@ -//go:build unit -// +build unit - -package commands - -import ( - "context" - "errors" - "net/http" - "testing" - "time" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - createProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/mocks/testData" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" - - "github.com/brianvoe/gofakeit/v6" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type createProductHandlerUnitTests struct { - *unit_test.UnitTestSharedFixture -} - -func TestCreateProductHandlerUnit(t *testing.T) { - suite.Run( - t, - &createProductHandlerUnitTests{ - UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t), - }, - ) -} - -func (c *createProductHandlerUnitTests) Test_Handle_Should_Create_New_Product_With_Valid_Data() { - ctx := context.Background() - id := uuid.NewV4() - - createProductHandler := createProductCommandV1.NewCreateProductHandler( - c.Log, - c.Uow, - c.Bus, - c.Tracer, - ) - - createProduct := &createProductCommandV1.CreateProduct{ - ProductID: id, - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.EmojiDescription(), - Price: gofakeit.Price(100, 1000), - } - - product := testData.Products[0] - - c.ProductRepository.On("CreateProduct", mock.Anything, mock.Anything). - Once(). - Return(product, nil) - - dto, err := createProductHandler.Handle(ctx, createProduct) - c.Require().NoError(err) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "CreateProduct", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) - c.Equal(dto.ProductID, id) -} - -func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Duplicate_Item() { - ctx := context.Background() - id := uuid.NewV4() - - createProduct := &createProductCommandV1.CreateProduct{ - ProductID: id, - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.EmojiDescription(), - Price: gofakeit.Price(100, 1000), - } - - c.ProductRepository.On("CreateProduct", mock.Anything, mock.Anything). - Once(). - Return(nil, errors.New("error duplicate product")) - - createProductHandler := createProductCommandV1.NewCreateProductHandler( - c.Log, - c.Uow, - c.Bus, - c.Tracer, - ) - - dto, err := createProductHandler.Handle(ctx, createProduct) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "CreateProduct", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) - c.True(customErrors.IsApplicationError(err, http.StatusConflict)) - c.ErrorContains(err, "product already exists") - c.Nil(dto) -} - -func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { - ctx := context.Background() - id := uuid.NewV4() - - createProduct := &createProductCommandV1.CreateProduct{ - ProductID: id, - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.EmojiDescription(), - Price: gofakeit.Price(100, 1000), - } - - product := testData.Products[0] - c.ProductRepository.On("CreateProduct", mock.Anything, mock.Anything). - Once(). - Return(product, nil) - - // override called mock - // https://github.com/stretchr/testify/issues/558 - c.Bus.Mock.ExpectedCalls = nil - c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). - Once(). - Return(errors.New("error in the publish message")) - - createProductHandler := createProductCommandV1.NewCreateProductHandler( - c.Log, - c.Uow, - c.Bus, - c.Tracer, - ) - - dto, err := createProductHandler.Handle(ctx, createProduct) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "CreateProduct", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) - c.ErrorContains(err, "error in the publish message") - c.ErrorContains(err, "error in publishing ProductCreated integration_events event") - c.Nil(dto) -} - -func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Mapping() { - ctx := context.Background() - id := uuid.NewV4() - - createProduct := &createProductCommandV1.CreateProduct{ - ProductID: id, - Name: gofakeit.Name(), - CreatedAt: time.Now(), - Description: gofakeit.EmojiDescription(), - Price: gofakeit.Price(100, 1000), - } - - product := testData.Products[0] - c.ProductRepository.On("CreateProduct", mock.Anything, mock.Anything). - Once(). - Return(product, nil) - - mapper.ClearMappings() - - createProductHandler := createProductCommandV1.NewCreateProductHandler( - c.Log, - c.Uow, - c.Bus, - c.Tracer, - ) - - dto, err := createProductHandler.Handle(ctx, createProduct) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "CreateProduct", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) - c.ErrorContains(err, "error in the mapping ProductDto") - c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - c.Nil(dto) -} diff --git a/internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_handler_unit_test.go b/internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_handler_unit_test.go deleted file mode 100644 index d6f49bac..00000000 --- a/internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_handler_unit_test.go +++ /dev/null @@ -1,110 +0,0 @@ -//go:build unit -// +build unit - -package commands - -import ( - "context" - "net/http" - "testing" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" - - "emperror.dev/errors" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type deleteProductHandlerUnitTests struct { - *unit_test.UnitTestSharedFixture -} - -func TestDeleteProductHandlerUnit(t *testing.T) { - suite.Run( - t, - &deleteProductHandlerUnitTests{ - UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t), - }, - ) -} - -func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Delete_Product_With_Valid_Id() { - ctx := context.Background() - id := c.Items[0].ProductId - - deleteProduct := &commands.DeleteProduct{ - ProductID: id, - } - - c.ProductRepository.On("DeleteProductByID", mock.Anything, id). - Once(). - Return(nil) - - deleteProductHandler := commands.NewDeleteProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - - _, err := deleteProductHandler.Handle(ctx, deleteProduct) - c.Require().NoError(err) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "DeleteProductByID", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) -} - -func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Return_NotFound_Error_When_Id_Is_Invalid() { - ctx := context.Background() - id := uuid.NewV4() - - deleteProduct := &commands.DeleteProduct{ - ProductID: id, - } - - c.ProductRepository.On("DeleteProductByID", mock.Anything, id). - Once(). - Return(customErrors.NewNotFoundError("error finding product")) - - deleteProductHandler := commands.NewDeleteProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - - res, err := deleteProductHandler.Handle(ctx, deleteProduct) - c.Require().Error(err) - c.True(customErrors.IsNotFoundError(err)) - c.True(customErrors.IsApplicationError(err, http.StatusNotFound)) - c.NotNil(res) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "DeleteProductByID", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) -} - -func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { - ctx := context.Background() - id := c.Items[0].ProductId - - deleteProduct := &commands.DeleteProduct{ - ProductID: id, - } - - c.ProductRepository.On("DeleteProductByID", mock.Anything, id). - Once(). - Return(nil) - - // override called mock - // https://github.com/stretchr/testify/issues/558 - c.Bus.Mock.ExpectedCalls = nil - c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). - Once(). - Return(errors.New("error in the publish message")) - - deleteProductHandler := commands.NewDeleteProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - dto, err := deleteProductHandler.Handle(ctx, deleteProduct) - - c.NotNil(dto) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "DeleteProductByID", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) - c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - c.ErrorContains(err, "error in publishing 'ProductDeleted' message") -} diff --git a/internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler_unit_test.go b/internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler_unit_test.go deleted file mode 100644 index 97b6c89d..00000000 --- a/internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_id_handler_unit_test.go +++ /dev/null @@ -1,209 +0,0 @@ -//go:build unit -// +build unit - -package queries - -import ( - "context" - "fmt" - "net/http" - "testing" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - getProductByIdQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" - - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type getProductByIdHandlerTest struct { - *unit_test.UnitTestSharedFixture -} - -func TestGetProductByIdHandlerUnit(t *testing.T) { - suite.Run( - t, - &getProductByIdHandlerTest{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, - ) -} - -func (c *getProductByIdHandlerTest) Test_Get_Product_By_Id() { - product := c.Items[0] - id := uuid.NewV4() - testCases := []struct { - Name string - id uuid.UUID - HandlerError error - ProductRepositoryNumberOfCall int - ExpectedName string - ExpectedId uuid.UUID - RepositoryReturnProduct *models.Product - RepositoryReturnError error - fn func() - }{ - { - Name: "Handle_Should_Get_Product_Successfully", - id: product.ProductId, - HandlerError: nil, - ProductRepositoryNumberOfCall: 1, - ExpectedId: product.ProductId, - ExpectedName: product.Name, - RepositoryReturnProduct: product, - RepositoryReturnError: nil, - }, - { - Name: "Handle_Should_Return_NotFound_Error_For_NotFound_Item", - id: id, - HandlerError: customErrors.NewApplicationErrorWithCode( - fmt.Sprintf("error in getting product with id %s in the repository", id.String()), - http.StatusNotFound, - ), - ProductRepositoryNumberOfCall: 1, - ExpectedId: *new(uuid.UUID), - ExpectedName: "", - RepositoryReturnProduct: nil, - RepositoryReturnError: customErrors.NewNotFoundError("product not found"), - }, - { - Name: "Handle_Should_Return_Error_For_Error_In_Mapping", - id: product.ProductId, - HandlerError: customErrors.NewApplicationErrorWithCode( - "error in the mapping product", - http.StatusInternalServerError, - ), - ProductRepositoryNumberOfCall: 1, - ExpectedId: *new(uuid.UUID), - ExpectedName: "", - RepositoryReturnProduct: product, - RepositoryReturnError: nil, - fn: func() { - mapper.ClearMappings() - }, - }, - } - - ctx := context.Background() - for _, testCase := range testCases { - c.Run(testCase.Name, func() { - // arrange - // create new mocks or clear mocks before executing - c.CleanupMocks() - - getProductByIdHandler := getProductByIdQueryV1.NewGetProductByIdHandler( - c.Log, - c.ProductRepository, - c.Tracer, - ) - - c.ProductRepository.On("GetProductById", mock.Anything, testCase.id). - Once(). - Return(testCase.RepositoryReturnProduct, testCase.RepositoryReturnError) - - if testCase.fn != nil { - testCase.fn() - } - - query, err := getProductByIdQueryV1.NewGetProductById(testCase.id) - c.Require().NoError(err) - - // act - dto, err := getProductByIdHandler.Handle(ctx, query) - - // assert - c.ProductRepository.AssertNumberOfCalls( - c.T(), - "GetProductById", - testCase.ProductRepositoryNumberOfCall, - ) - if testCase.HandlerError == nil { - // success path with a valid dto - c.Require().NoError(err) - c.NotNil(dto.Product) - c.Equal(testCase.ExpectedId, dto.Product.ProductId) - c.Equal(testCase.ExpectedName, dto.Product.Name) - } else { - // handler error path - c.Nil(dto) - c.ErrorContains(err, testCase.HandlerError.Error()) - if customErrors.IsApplicationError(testCase.HandlerError, http.StatusNotFound) { - // not found error - c.True(customErrors.IsNotFoundError(err)) - c.True(customErrors.IsApplicationError(err, http.StatusNotFound)) - c.ErrorContains(err, testCase.HandlerError.Error()) - } else { - // mapping error - c.ErrorContains(err, testCase.HandlerError.Error()) - c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - } - } - }) - } - - //c.Register("Handle_Should_Get_Product_Successfully", func() { - // // create new mocks or clear mocks before executing - // c.UnitTestMockFixture = unit_test.NewUnitTestMockFixture(c.T()) - // c.getProductByIdHandler = NewGetProductByIdHandler(c.Log, c.Cfg, c.ProductRepository) - // - // c.ProductRepository.On("GetProductById", mock.Anything, product.ProductId). - // Once(). - // Return(product, nil) - // - // query := NewGetProductById(product.ProductId) - // - // dto, err := c.getProductByIdHandler.Handle(c.Ctx, query) - // c.Require().NoError(err) - // - // c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - // c.Equal(product.ProductId, dto.Product.ProductId) - // c.Equal(product.Name, dto.Product.Name) - //}) - // - //c.Register("Handle_Should_Return_NotFound_Error_For_NotFound_Item", func() { - // // create new mocks or clear mocks before executing - // c.UnitTestMockFixture = unit_test.NewUnitTestMockFixture(c.T()) - // c.getProductByIdHandler = NewGetProductByIdHandler(c.Log, c.Cfg, c.ProductRepository) - // - // c.ProductRepository.On("GetProductById", mock.Anything, id). - // Once(). - // Return(nil, customErrors.NewNotFoundError("product not found")) - // - // query := NewGetProductById(id) - // - // dto, err := c.getProductByIdHandler.Handle(c.Ctx, query) - // - // c.Require().Error(err) - // c.True(customErrors.IsApplicationError(err, http.StatusNotFound)) - // c.True(customErrors.IsNotFoundError(err)) - // c.ErrorContains(err, fmt.Sprintf("error in getting product with id %s in the repository", id.String())) - // c.Nil(dto) - // - // c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - //}) - // - //c.Register("Handle_Should_Return_Error_For_Error_In_Mapping", func() { - // // create new mocks or clear mocks before executing - // c.UnitTestMockFixture = unit_test.NewUnitTestMockFixture(c.T()) - // c.getProductByIdHandler = NewGetProductByIdHandler(c.Log, c.Cfg, c.ProductRepository) - // - // product := testData.Products[0] - // c.ProductRepository.On("GetProductById", mock.Anything, product.ProductId). - // Once(). - // Return(product, nil) - // - // mapper.ClearMappings() - // - // query := NewGetProductById(product.ProductId) - // - // dto, err := c.getProductByIdHandler.Handle(c.Ctx, query) - // - // c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - // c.Nil(dto) - // c.Require().Error(err) - // c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - // c.ErrorContains(err, "error in the mapping product") - //}) -} diff --git a/internal/services/catalog_write_service/test/unit/products/features/getting_products/v1/queries/get_products_handler_unit_test.go b/internal/services/catalog_write_service/test/unit/products/features/getting_products/v1/queries/get_products_handler_unit_test.go deleted file mode 100644 index ab76b0af..00000000 --- a/internal/services/catalog_write_service/test/unit/products/features/getting_products/v1/queries/get_products_handler_unit_test.go +++ /dev/null @@ -1,96 +0,0 @@ -//go:build unit -// +build unit - -package queries - -import ( - "context" - "net/http" - "testing" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/mocks/testData" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" - - "emperror.dev/errors" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type getProductsHandlerUnitTests struct { - *unit_test.UnitTestSharedFixture -} - -func TestGetProductsUnit(t *testing.T) { - suite.Run( - t, - &getProductsHandlerUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, - ) -} - -func (c *getProductsHandlerUnitTests) Test_Handle_Should_Return_Products_Successfully() { - ctx := context.Background() - - query, err := queries.NewGetProducts(utils.NewListQuery(10, 1)) - c.Require().NoError(err) - - items := utils.NewListResult[*models.Product]( - testData.Products, - 10, - 1, - int64(len(testData.Products)), - ) - c.ProductRepository.On("GetAllProducts", mock.Anything, mock.Anything). - Once(). - Return(items, nil) - - getProductsHandler := queries.NewGetProductsHandler(c.Log, c.ProductRepository, c.Tracer) - - res, err := getProductsHandler.Handle(ctx, query) - c.Require().NoError(err) - c.NotNil(res) - c.NotEmpty(res.Products) - c.Equal(len(testData.Products), len(res.Products.Items)) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetAllProducts", 1) -} - -func (c *getProductsHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Fetching_Data_From_Repository() { - ctx := context.Background() - - query, err := queries.NewGetProducts(utils.NewListQuery(10, 1)) - c.Require().NoError(err) - - c.ProductRepository.On("GetAllProducts", mock.Anything, mock.Anything). - Once(). - Return(nil, errors.New("error in fetching products from repository")) - - getProductsHandler := queries.NewGetProductsHandler(c.Log, c.ProductRepository, c.Tracer) - - res, err := getProductsHandler.Handle(ctx, query) - c.Require().Error(err) - c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - c.Nil(res) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetAllProducts", 1) -} - -func (c *getProductsHandlerUnitTests) Test_Handle_Should_Return_Error_For_Mapping_List_Result() { - ctx := context.Background() - - query, err := queries.NewGetProducts(utils.NewListQuery(10, 1)) - c.Require().NoError(err) - - c.ProductRepository.On("GetAllProducts", mock.Anything, mock.Anything). - Once(). - Return(nil, nil) - - getProductsHandler := queries.NewGetProductsHandler(c.Log, c.ProductRepository, c.Tracer) - - res, err := getProductsHandler.Handle(ctx, query) - c.Require().Error(err) - c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) - c.Nil(res) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetAllProducts", 1) -} diff --git a/internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_handler_unit_test.go b/internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_handler_unit_test.go deleted file mode 100644 index e232d388..00000000 --- a/internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_handler_unit_test.go +++ /dev/null @@ -1,150 +0,0 @@ -//go:build unit -// +build unit - -package commands - -import ( - "context" - "net/http" - "testing" - - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" - - "emperror.dev/errors" - "github.com/brianvoe/gofakeit/v6" - uuid "github.com/satori/go.uuid" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" -) - -type updateProductHandlerUnitTests struct { - *unit_test.UnitTestSharedFixture -} - -func TestUpdateProductHandlerUnit(t *testing.T) { - suite.Run( - t, - &updateProductHandlerUnitTests{ - UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t), - }, - ) -} - -func (c *updateProductHandlerUnitTests) Test_Handle_Should_Update_Product_With_Valid_Data() { - ctx := context.Background() - existing := c.Items[0] - - updateProductCommand, err := commands.NewUpdateProduct( - existing.ProductId, - gofakeit.Name(), - gofakeit.EmojiDescription(), - existing.Price, - ) - c.Require().NoError(err) - - updated := &models.Product{ - ProductId: existing.ProductId, - Name: updateProductCommand.Name, - Description: updateProductCommand.Description, - UpdatedAt: updateProductCommand.UpdatedAt, - CreatedAt: existing.CreatedAt, - Price: existing.Price, - } - - c.ProductRepository.On("GetProductById", mock.Anything, existing.ProductId). - Once(). - Return(existing, nil) - - c.ProductRepository.On("UpdateProduct", mock.Anything, mock.Anything). - Once(). - Return(updated, nil) - - updateProductHandler := commands.NewUpdateProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - - _, err = updateProductHandler.Handle(ctx, updateProductCommand) - c.Require().NoError(err) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "UpdateProduct", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) -} - -func (c *updateProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_NotFound_Item() { - ctx := context.Background() - id := uuid.NewV4() - - command, err := commands.NewUpdateProduct( - id, - gofakeit.Name(), - gofakeit.EmojiDescription(), - gofakeit.Price(150, 6000), - ) - c.Require().NoError(err) - - c.ProductRepository.On("GetProductById", mock.Anything, mock.Anything). - Once(). - Return(nil, errors.New("error notfound product")) - - updateProductHandler := commands.NewUpdateProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - dto, err := updateProductHandler.Handle(ctx, command) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "UpdateProduct", 0) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) - c.True(customErrors.IsApplicationError(err, http.StatusNotFound)) - c.ErrorContains(err, "error notfound product") - c.NotNil(dto) -} - -func (c *updateProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { - ctx := context.Background() - existing := c.Items[0] - - updateProductCommand, err := commands.NewUpdateProduct( - existing.ProductId, - gofakeit.Name(), - gofakeit.EmojiDescription(), - existing.Price, - ) - c.Require().NoError(err) - - updated := &models.Product{ - ProductId: existing.ProductId, - Name: updateProductCommand.Name, - Description: updateProductCommand.Description, - UpdatedAt: updateProductCommand.UpdatedAt, - CreatedAt: existing.CreatedAt, - Price: existing.Price, - } - - c.ProductRepository.On("GetProductById", mock.Anything, existing.ProductId). - Once(). - Return(existing, nil) - - c.ProductRepository.On("UpdateProduct", mock.Anything, mock.Anything). - Once(). - Return(updated, nil) - - // override called mock - // https://github.com/stretchr/testify/issues/558 - c.Bus.Mock.ExpectedCalls = nil - c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). - Once(). - Return(errors.New("error in the publish message")) - - updateProductHandler := commands.NewUpdateProductHandler(c.Log, c.Uow, c.Bus, c.Tracer) - dto, err := updateProductHandler.Handle(ctx, updateProductCommand) - - c.Uow.AssertNumberOfCalls(c.T(), "Do", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "UpdateProduct", 1) - c.ProductRepository.AssertNumberOfCalls(c.T(), "GetProductById", 1) - c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) - c.ErrorContains(err, "error in the publish message") - c.ErrorContains(err, "error in publishing 'ProductUpdated' message") - c.NotNil(dto) -} diff --git a/internal/services/catalog_read_service/.air.toml b/internal/services/catalogreadservice/.air.toml similarity index 100% rename from internal/services/catalog_read_service/.air.toml rename to internal/services/catalogreadservice/.air.toml diff --git a/internal/services/catalog_read_service/.dockerignore b/internal/services/catalogreadservice/.dockerignore similarity index 100% rename from internal/services/catalog_read_service/.dockerignore rename to internal/services/catalogreadservice/.dockerignore diff --git a/internal/services/catalogreadservice/.env b/internal/services/catalogreadservice/.env new file mode 100644 index 00000000..e19f4d52 --- /dev/null +++ b/internal/services/catalogreadservice/.env @@ -0,0 +1 @@ +PROJECT_NAME=catalogreadservice diff --git a/internal/services/catalog_read_service/.gitignore b/internal/services/catalogreadservice/.gitignore similarity index 100% rename from internal/services/catalog_read_service/.gitignore rename to internal/services/catalogreadservice/.gitignore diff --git a/internal/services/catalog_read_service/.golangci.yml b/internal/services/catalogreadservice/.golangci.yml similarity index 100% rename from internal/services/catalog_read_service/.golangci.yml rename to internal/services/catalogreadservice/.golangci.yml diff --git a/internal/services/catalog_read_service/client.http b/internal/services/catalogreadservice/Dockerfile similarity index 100% rename from internal/services/catalog_read_service/client.http rename to internal/services/catalogreadservice/Dockerfile diff --git a/internal/services/catalog_read_service/Makefile b/internal/services/catalogreadservice/Makefile similarity index 100% rename from internal/services/catalog_read_service/Makefile rename to internal/services/catalogreadservice/Makefile diff --git a/internal/services/catalog_read_service/arch-go.yml b/internal/services/catalogreadservice/arch-go.yml similarity index 100% rename from internal/services/catalog_read_service/arch-go.yml rename to internal/services/catalogreadservice/arch-go.yml diff --git a/internal/services/catalog_write_service/client.http b/internal/services/catalogreadservice/client.http similarity index 100% rename from internal/services/catalog_write_service/client.http rename to internal/services/catalogreadservice/client.http diff --git a/internal/services/catalog_read_service/cmd/app/main.go b/internal/services/catalogreadservice/cmd/app/main.go similarity index 100% rename from internal/services/catalog_read_service/cmd/app/main.go rename to internal/services/catalogreadservice/cmd/app/main.go diff --git a/internal/services/catalog_read_service/cmd/migration/.gitkeep b/internal/services/catalogreadservice/cmd/migration/.gitkeep similarity index 100% rename from internal/services/catalog_read_service/cmd/migration/.gitkeep rename to internal/services/catalogreadservice/cmd/migration/.gitkeep diff --git a/internal/services/catalog_read_service/config/config.development.json b/internal/services/catalogreadservice/config/config.development.json similarity index 60% rename from internal/services/catalog_read_service/config/config.development.json rename to internal/services/catalogreadservice/config/config.development.json index df1a7ecc..6b988ae3 100644 --- a/internal/services/catalog_read_service/config/config.development.json +++ b/internal/services/catalogreadservice/config/config.development.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "catalogs_read_service", + "serviceName": "catalogreadservice", "deliveryType": "http" }, "grpcOptions": { - "name": "catalogs_read_service", + "name": "catalogreadservice", "port": ":6004", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "catalogs_read_service", + "name": "catalogreadservice", "port": ":7001", "development": true, "timeout": 30, @@ -55,24 +55,47 @@ "database": "catalogs_service", "useAuth": true }, - "OpenTelemetryOptions": { + "tracingOptions": { "enable": true, "serviceName": "catalogs-read-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice", + "instrumentationName": "io.opentelemetry.traces.catalogs-read-service", "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4320", + "enabled": true + }, + "tempoExporterOptions": { + "otlpEndpoint": "localhost:4322", + "enabled": true }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" }, - "otelMetricsOptions": { - "port": ":1003", - "metricsRoutePath": "/metrics", - "name": "catalogs-read-service" - } + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": { + "uptrace-dsn": "https://@uptrace.dev/" + } + }, + { + "name": "elastic-apm", + "enabled": false, + "otlpEndpoint": "host.docker.internal:4317", + "otlpHeaders": { + "Authorization": "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + } + } + ] + }, + "metricsOptions": { + "metricsRoutePath": "/metrics", + "serviceName": "catalogs-read-service", + "instrumentationName": "io.opentelemetry.metrics.catalogs-write-service" }, "elasticOptions": { "url": "http://localhost:9200", @@ -85,8 +108,5 @@ }, "elasticIndexes": { "products": "products" - }, - "eventStoreDbOptions": { - "connectionString": "esdb://localhost:2113?tls=false" } } diff --git a/internal/services/catalog_read_service/config/config.go b/internal/services/catalogreadservice/config/config.go similarity index 81% rename from internal/services/catalog_read_service/config/config.go rename to internal/services/catalogreadservice/config/config.go index e265914d..0ee2cccf 100644 --- a/internal/services/catalog_read_service/config/config.go +++ b/internal/services/catalogreadservice/config/config.go @@ -4,14 +4,14 @@ import ( "strings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" ) type Config struct { AppOptions AppOptions `mapstructure:"appOptions" env:"AppOptions"` } -func NewConfig(env environemnt.Environment) (*Config, error) { +func NewConfig(env environment.Environment) (*Config, error) { cfg, err := config.BindConfig[*Config](env) if err != nil { return nil, err @@ -22,7 +22,7 @@ func NewConfig(env environemnt.Environment) (*Config, error) { type AppOptions struct { DeliveryType string `mapstructure:"deliveryType" env:"DeliveryType"` - ServiceName string `mapstructure:"serviceName" env:"ServiceName"` + ServiceName string `mapstructure:"serviceName" env:"serviceName"` } func (cfg *AppOptions) GetMicroserviceNameUpper() string { diff --git a/internal/services/catalog_read_service/config/config.test.json b/internal/services/catalogreadservice/config/config.test.json similarity index 62% rename from internal/services/catalog_read_service/config/config.test.json rename to internal/services/catalogreadservice/config/config.test.json index 0cadbdb1..5038d505 100644 --- a/internal/services/catalog_read_service/config/config.test.json +++ b/internal/services/catalogreadservice/config/config.test.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "catalogs_read_service", + "serviceName": "catalogreadservice", "deliveryType": "http" }, "grpcOptions": { - "name": "catalogs_read_service", + "name": "catalogsreadservice", "port": ":3300", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "catalogs_read_service", + "name": "catalogreadservice", "port": ":6000", "development": true, "timeout": 30, @@ -53,24 +53,43 @@ "database": "catalogs_service", "useAuth": true }, - "OpenTelemetryOptions": { + "tracingOptions": { "enable": true, "serviceName": "catalogs-read-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice", + "instrumentationName": "io.opentelemetry.traces.catalogs-read-service", "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4320", + "enabled": true }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" }, - "otelMetricsOptions": { - "port": ":2002", - "metricsRoutePath": "/metrics", - "name": "catalogs-read-service" - } + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": { + "uptrace-dsn": "https://@uptrace.dev/" + } + }, + { + "name": "elastic-apm", + "enabled": false, + "otlpEndpoint": "host.docker.internal:4317", + "otlpHeaders": { + "Authorization": "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + } + } + ] + }, + "metricsOptions": { + "metricsRoutePath": "/metrics", + "serviceName": "catalogs-read-service", + "instrumentationName": "io.opentelemetry.metrics.catalogs-write-service" }, "elasticOptions": { "url": "http://localhost:9200", @@ -83,8 +102,5 @@ }, "elasticIndexes": { "products": "products" - }, - "eventStoreDbOptions": { - "connectionString": "esdb://localhost:2113?tls=false" } } diff --git a/internal/services/catalog_read_service/config/confix_fx.go b/internal/services/catalogreadservice/config/confix_fx.go similarity index 100% rename from internal/services/catalog_read_service/config/confix_fx.go rename to internal/services/catalogreadservice/config/confix_fx.go diff --git a/internal/services/catalog_read_service/docs/docs.go b/internal/services/catalogreadservice/docs/docs.go similarity index 100% rename from internal/services/catalog_read_service/docs/docs.go rename to internal/services/catalogreadservice/docs/docs.go diff --git a/internal/services/catalog_read_service/docs/swagger.json b/internal/services/catalogreadservice/docs/swagger.json similarity index 100% rename from internal/services/catalog_read_service/docs/swagger.json rename to internal/services/catalogreadservice/docs/swagger.json diff --git a/internal/services/catalog_read_service/docs/swagger.yaml b/internal/services/catalogreadservice/docs/swagger.yaml similarity index 100% rename from internal/services/catalog_read_service/docs/swagger.yaml rename to internal/services/catalogreadservice/docs/swagger.yaml diff --git a/internal/services/catalog_read_service/go.mod b/internal/services/catalogreadservice/go.mod similarity index 70% rename from internal/services/catalog_read_service/go.mod rename to internal/services/catalogreadservice/go.mod index e3343eb9..31438437 100644 --- a/internal/services/catalog_read_service/go.mod +++ b/internal/services/catalogreadservice/go.mod @@ -7,16 +7,16 @@ replace github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg => ../../ require ( emperror.dev/errors v0.8.1 - github.com/brianvoe/gofakeit/v6 v6.23.2 + github.com/brianvoe/gofakeit/v6 v6.25.0 github.com/gavv/httpexpect/v2 v2.3.1 github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-playground/validator v9.31.0+incompatible github.com/labstack/echo/v4 v4.11.1 github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg v0.0.0-20230831075934-be8df319f588 - github.com/mehdihadeli/go-mediatr v1.1.10 + github.com/mehdihadeli/go-mediatr v1.3.0 github.com/michaelklishin/rabbit-hole v1.5.0 github.com/pterm/pterm v0.12.69 - github.com/redis/go-redis/v9 v9.0.5 + github.com/redis/go-redis/v9 v9.2.1 github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v1.8.1 github.com/spf13/cobra v1.7.0 @@ -24,10 +24,11 @@ require ( github.com/swaggo/echo-swagger v1.4.1 github.com/swaggo/swag v1.16.2 go.mongodb.org/mongo-driver v1.12.1 - go.opentelemetry.io/otel v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 - go.opentelemetry.io/otel/trace v1.17.0 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/metric v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/fx v1.20.0 + gorm.io/gorm v1.25.5 ) require ( @@ -36,10 +37,10 @@ require ( atomicgo.dev/schedule v0.1.0 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/EventStore/EventStore-Client-Go v1.0.2 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -51,21 +52,25 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/console v1.0.3 // indirect - github.com/containerd/containerd v1.7.5 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/doug-martin/goqu/v9 v9.18.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fatih/structs v1.0.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/glebarez/sqlite v1.10.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.20.9 // indirect @@ -74,11 +79,9 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-reflect v1.2.0 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-migrate/migrate/v4 v4.16.2 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect @@ -87,6 +90,7 @@ require ( github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/imkira/go-interpol v1.0.0 // indirect @@ -107,12 +111,13 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/kamva/mgm/v3 v3.5.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -129,41 +134,50 @@ require ( github.com/nolleh/caption_json_formatter v0.2.2 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.9 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rabbitmq/amqp091-go v1.8.1 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/samber/lo v1.38.1 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smarty/assertions v1.15.0 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect - github.com/streadway/amqp v1.1.0 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect - github.com/testcontainers/testcontainers-go v0.23.0 // indirect + github.com/testcontainers/testcontainers-go v0.25.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - github.com/uptrace/bun v1.1.14 // indirect - github.com/uptrace/bun/driver/pgdriver v1.1.14 // indirect + github.com/ulule/limiter/v3 v3.11.2 // indirect + github.com/uptrace/bun v1.1.16 // indirect + github.com/uptrace/bun/driver/pgdriver v1.1.16 // indirect + github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.27.0 // indirect + github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -176,18 +190,25 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yudai/gojsondiff v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 // indirect - go.opentelemetry.io/contrib/propagators/ot v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.40.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.17.0 // indirect - go.opentelemetry.io/otel/sdk v1.17.0 // indirect - go.opentelemetry.io/otel/sdk/metric v0.40.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/host v0.45.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.19.0 // indirect + go.opentelemetry.io/otel/sdk v1.19.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.25.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect @@ -198,14 +219,19 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect - google.golang.org/grpc v1.57.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/grpc v1.58.2 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/postgres v1.5.2 // indirect - gorm.io/gorm v1.25.4 // indirect + gorm.io/plugin/opentelemetry v0.1.4 // indirect mellium.im/sasl v0.3.1 // indirect + modernc.org/libc v1.24.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.6.0 // indirect + modernc.org/sqlite v1.25.0 // indirect moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e // indirect ) diff --git a/internal/services/catalog_read_service/go.sum b/internal/services/catalogreadservice/go.sum similarity index 87% rename from internal/services/catalog_read_service/go.sum rename to internal/services/catalogreadservice/go.sum index 861a54b6..58dd0c77 100644 --- a/internal/services/catalog_read_service/go.sum +++ b/internal/services/catalogreadservice/go.sum @@ -6,7 +6,6 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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= @@ -33,8 +32,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -56,15 +55,12 @@ emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0= emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/EventStore/EventStore-Client-Go v1.0.2 h1:onM2TIInLhWUJwUQ/5a/8blNrrbhwrtm7Tpmg13ohiw= -github.com/EventStore/EventStore-Client-Go v1.0.2/go.mod h1:NOqSOtNxqGizr1Qnf7joGGLK6OkeoLV/QEI893A43H0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= @@ -80,13 +76,10 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= @@ -106,25 +99,24 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8= -github.com/brianvoe/gofakeit/v6 v6.23.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/brianvoe/gofakeit/v6 v6.25.0 h1:ZpFjktOpLZUeF8q223o0rUuXtA+m5qW5srjvVi+JkXk= +github.com/brianvoe/gofakeit/v6 v6.25.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 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/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -133,62 +125,51 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.5 h1:i9T9XpAWMe11BHMN7pu1BZqOGjXaKTPyz2v+KYOZgkY= -github.com/containerd/containerd v1.7.5/go.mod h1:ieJNCSzASw2shSGYLHx8NAE7WsZ/gEigo5fQ78W5Zvw= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 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/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= -github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fasthttp/websocket v1.4.3-rc.6 h1:omHqsl8j+KXpmzRjF8bmzOSYJ8GnS0E3efi1wYT+niY= github.com/fasthttp/websocket v1.4.3-rc.6/go.mod h1:43W9OM2T8FeXpCWMsBd9Cb7nE2CACNqNvCqQCoty/Lc= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -199,6 +180,10 @@ github.com/gavv/httpexpect/v2 v2.3.1 h1:sGLlKMn8AuHS9ztK9Sb7AJ7OxIL8v2PcLdyxfKt1 github.com/gavv/httpexpect/v2 v2.3.1/go.mod h1:yOE8m/aqFYQDNrgprMeXgq4YynfN9h1NgcE1+1suV64= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= +github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -209,6 +194,9 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -235,12 +223,12 @@ github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+Pugk github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-reflect v1.2.0 h1:O0T8rZCuNmGXewnATuKYnkL0xm6o8UNOJZd/gOkb9ms= github.com/goccy/go-reflect v1.2.0/go.mod h1:n0oYZn8VcV2CkWTxi8B9QjkCoq6GTtCEdfmR66YhFtE= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -252,6 +240,8 @@ github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8 github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 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/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -262,8 +252,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -297,6 +285,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -315,9 +304,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= @@ -329,14 +318,14 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40= -github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/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/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -352,11 +341,8 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imkira/go-interpol v1.0.0 h1:HrmLyvOLJyjR0YofMw8QGdCIuYOs4TJUBDNU5sJC09E= github.com/imkira/go-interpol v1.0.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -430,8 +416,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -459,17 +445,18 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -495,22 +482,22 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= -github.com/mehdihadeli/go-mediatr v1.1.10 h1:NAzg4065c90lgYeb+Vzbd2WKH0tUFpxzL0mpx6hkU/A= -github.com/mehdihadeli/go-mediatr v1.1.10/go.mod h1:lwgZl7qVL/RKomObBblhG3uEte/r4nJDV95Vd+nGrMw= +github.com/mehdihadeli/go-mediatr v1.3.0 h1:hrb5Scp/nsiR3Y62mjZ0Tc5UX/dRJl4nDFkINBEIESA= +github.com/mehdihadeli/go-mediatr v1.3.0/go.mod h1:lsG+hyH+pEOhmZiZl0KPO72BcZiEReF03CBk4GVJB0k= github.com/michaelklishin/rabbit-hole v1.5.0 h1:Bex27BiFDsijCM9D0ezSHqyy0kehpYHuNKaPqq/a4RM= github.com/michaelklishin/rabbit-hole v1.5.0/go.mod h1:vvI1uOitYZi0O5HEGXhaWC1XT80Gy+HvFheJ+5Krlhk= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -518,7 +505,6 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8 github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nolleh/caption_json_formatter v0.2.2 h1:EKsOr/fCllNQF2ZoajfbSDlV73BNV1bDu1aTTSRrlN0= github.com/nolleh/caption_json_formatter v0.2.2/go.mod h1:5FYofZA8NAej/eFxa12FvyQKosU1LfyKizZPlY0JojU= @@ -530,47 +516,42 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= -github.com/ory/dockertest/v3 v3.6.3/go.mod h1:EFLcVUOl8qCwp9NyDAcCDtq/QviLtYswW/VbWzUnTNE= -github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= -github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= @@ -586,8 +567,11 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -597,7 +581,6 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= @@ -605,36 +588,35 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 h1:N3Af8f13ooDKcIhsmFT7Z05CStZWu4C7Md0uDEy4q6o= github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873/go.mod h1:dmPawKuiAeG/aFYVs2i+Dyosoo7FNcm+Pi8iK6ZUrX8= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= +github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= @@ -668,32 +650,40 @@ github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= +github.com/testcontainers/testcontainers-go v0.25.0 h1:erH6cQjsaJrH+rJDU9qIf89KFdhK0Bft0aEZHlYC3Vs= +github.com/testcontainers/testcontainers-go v0.25.0/go.mod h1:4sC9SiJyzD1XFi59q8umTQYWxnkweEc5OjVtTUlJzqQ= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM= -github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8= -github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA= -github.com/uptrace/bun/driver/pgdriver v1.1.14/go.mod h1:D4FjWV9arDYct6sjMJhFoyU71SpllZRHXFRRP2Kd0Kw= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= +github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= +github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A= +github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8= +github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7QiNOCPLqPFJjK4= +github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 h1:m5eNyOhch/7tyK6aN6eRRpNoD1vM8PNh64dA05X22Js= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3/go.mod h1:APPUXm9BbpH7NFkfpbw04raZSitzl19/3NOCu0rbI4E= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 h1:LyGS9cIZV0YVhE81zwfMhIE2l2flcj3wn5IoK4VkbWA= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3/go.mod h1:RvCYhPchLhvQ9l9C9goblbgO7BaKt597kBMf5mgKyo0= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 h1:2na5W81H38Z4qXCQCuzlcdSMiTWgPJ6XeZIArq6VIJE= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3/go.mod h1:9IVEh9mPv3NwFf99dVLX15FqVgdpZJ8RMDo/Cr0vK74= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.27.0 h1:gDefRDL9aqSiwXV6aRW8aSBPs82y4KizSzHrBLf4NDI= github.com/valyala/fasthttp v1.27.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608= +github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= @@ -728,8 +718,9 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= @@ -740,32 +731,42 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 h1:pcWR7mkuO5XMK3f0KeGpr70OTR9/ikPk0D1hHEd5dp4= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0/go.mod h1:azTPpa9PvRDSNU/lQbe2CRDQoTcau5moOjS4EzhpyAY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0 h1:hhSlPVi9AQwOmbMmptPNLfRZOLgENdRM2kb7z9LFe1A= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0/go.mod h1:qtt+pEu23D7UVP+j33G4i7LopmVu8/6/IwGu3hEm100= -go.opentelemetry.io/contrib/propagators/ot v1.18.0 h1:VmzxO7BjUU6oo0ChcKuGdKaSR0vchPxwahHZl64zVUM= -go.opentelemetry.io/contrib/propagators/ot v1.18.0/go.mod h1:5VwcOJ7OjS0uPxaxuwKHwJtkt+EAC+cgjXleXMe51z4= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ERB6R034zmsHjBRLyxrAR8= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0 h1:oi5+xMN3pflqWSd4EX6FiO+Cn3KbFBBzeQmD5LMIf0c= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0/go.mod h1:pNir+S6/f0HFGfbXhobXLTFu60KtAzw8aGSUpt9A6VU= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= -go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 h1:bldpPC7XAv7f7LKTwNfRkNdzRhjtXaWybZFFa16dAb8= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0/go.mod h1:xhkNpJG3D+kmuaciNTco7cdK27Fb77J9Iqcq5CMe4Y8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0 h1:1uzNKJDqZ6y6F5J6aKWgJjRREpKiGhBvKHlWon/bqB4= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0/go.mod h1:vlqPvzDsmB4+jlERxBRXsdLCD6Q0LoBzxHqNXp3qvG4= +go.opentelemetry.io/contrib/propagators/ot v1.20.0 h1:duH7mgL6VGQH7e7QEAVOFkCQXWpCb4PjTtrhdrYrJRQ= +go.opentelemetry.io/contrib/propagators/ot v1.20.0/go.mod h1:gijQzxOq0JLj9lyZhTvqjDddGV/zaNagpPIn+2r8CEI= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0 h1:EGY0h5mGliP9o/nIkVuLI0vRiQqmsYOcbwCuotksO1o= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0/go.mod h1:JQgTGJP11yi3o4GHzIWYodhPisxANdqxF1eHwDSnJrI= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -791,9 +792,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -850,7 +850,6 @@ 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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -869,7 +868,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -890,7 +888,6 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -906,8 +903,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -918,7 +915,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= @@ -935,15 +931,14 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w 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= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -961,12 +956,11 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -975,8 +969,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -991,8 +983,11 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1035,7 +1030,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1080,7 +1074,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= @@ -1147,7 +1140,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1156,8 +1148,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1174,8 +1170,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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= @@ -1188,10 +1184,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1200,7 +1194,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= @@ -1211,7 +1204,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1223,9 +1215,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= -gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= -gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/opentelemetry v0.1.4 h1:7p0ocWELjSSRI7NCKPW2mVe6h43YPini99sNJcbsTuc= +gorm.io/plugin/opentelemetry v0.1.4/go.mod h1:tndJHOdvPT0pyGhOb8E2209eXJCUxhC5UpKw7bGVWeI= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1237,6 +1232,14 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= +modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= +modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA= +modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e h1:C7q+e9M5nggAvWfVg9Nl66kebKeuJlP3FD58V4RR5wo= moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e/go.mod h1:nejbQVfXh96n9dSF6cH3Jsk/QI1Z2oEL7sSI2ifXFNA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/services/catalog_read_service/internal/products/configurations/mappings/mapping_profile.go b/internal/services/catalogreadservice/internal/products/configurations/mappings/mapping_profile.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/configurations/mappings/mapping_profile.go rename to internal/services/catalogreadservice/internal/products/configurations/mappings/mapping_profile.go diff --git a/internal/services/catalog_read_service/internal/products/configurations/mediator/mediator_configurations.go b/internal/services/catalogreadservice/internal/products/configurations/mediator/mediator_configurations.go similarity index 91% rename from internal/services/catalog_read_service/internal/products/configurations/mediator/mediator_configurations.go rename to internal/services/catalogreadservice/internal/products/configurations/mediator/mediator_configurations.go index 7f929af7..da5b63ed 100644 --- a/internal/services/catalog_read_service/internal/products/configurations/mediator/mediator_configurations.go +++ b/internal/services/catalogreadservice/internal/products/configurations/mediator/mediator_configurations.go @@ -4,7 +4,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" - createProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/commands" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1" createProductDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos" deleteProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands" getProductByIdDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos" @@ -25,8 +25,8 @@ func ConfigProductsMediator( cacheProductRepository data.ProductCacheRepository, tracer tracing.AppTracer, ) error { - err := mediatr.RegisterRequestHandler[*createProductCommandV1.CreateProduct, *createProductDtosV1.CreateProductResponseDto]( - createProductCommandV1.NewCreateProductHandler( + err := mediatr.RegisterRequestHandler[*v1.CreateProduct, *createProductDtosV1.CreateProductResponseDto]( + v1.NewCreateProductHandler( logger, mongoProductRepository, cacheProductRepository, diff --git a/internal/services/catalog_read_service/internal/products/configurations/products_module_configurator.go b/internal/services/catalogreadservice/internal/products/configurations/products_module_configurator.go similarity index 87% rename from internal/services/catalog_read_service/internal/products/configurations/products_module_configurator.go rename to internal/services/catalogreadservice/internal/products/configurations/products_module_configurator.go index ef39b6f3..1398c3bd 100644 --- a/internal/services/catalog_read_service/internal/products/configurations/products_module_configurator.go +++ b/internal/services/catalogreadservice/internal/products/configurations/products_module_configurator.go @@ -1,10 +1,10 @@ package configurations import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" logger2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/configurations/mappings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/configurations/mediator" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" @@ -25,13 +25,18 @@ func NewProductsModuleConfigurator( func (c *ProductsModuleConfigurator) ConfigureProductsModule() { c.ResolveFunc( func(logger logger2.Logger, mongoRepository data.ProductRepository, cacheRepository data.ProductCacheRepository, tracer tracing.AppTracer) error { - // Config Products Mediators - err := mediator.ConfigProductsMediator(logger, mongoRepository, cacheRepository, tracer) + // config Products Mediators + err := mediator.ConfigProductsMediator( + logger, + mongoRepository, + cacheRepository, + tracer, + ) if err != nil { return err } - // Config Products Mappings + // config Products Mappings err = mappings.ConfigureProductsMappings() if err != nil { return err @@ -42,7 +47,7 @@ func (c *ProductsModuleConfigurator) ConfigureProductsModule() { } func (c *ProductsModuleConfigurator) MapProductsEndpoints() { - // Config Products Http Endpoints + // config Products Http Endpoints c.ResolveFuncWithParamTag(func(endpoints []route.Endpoint) { for _, endpoint := range endpoints { endpoint.MapEndpoint() diff --git a/internal/services/catalog_read_service/internal/products/configurations/rabbitmq/rabbitmq_configuration.go b/internal/services/catalogreadservice/internal/products/configurations/rabbitmq/rabbitmq_configuration.go similarity index 96% rename from internal/services/catalog_read_service/internal/products/configurations/rabbitmq/rabbitmq_configuration.go rename to internal/services/catalogreadservice/internal/products/configurations/rabbitmq/rabbitmq_configuration.go index d86e8907..b63b44db 100644 --- a/internal/services/catalog_read_service/internal/products/configurations/rabbitmq/rabbitmq_configuration.go +++ b/internal/services/catalogreadservice/internal/products/configurations/rabbitmq/rabbitmq_configuration.go @@ -1,12 +1,12 @@ package rabbitmq import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" rabbitmqConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/consumer/configurations" - createProductExternalEventV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integration_events/external_events" + createProductExternalEventV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents" deleteProductExternalEventV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events" updateProductExternalEventsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events" diff --git a/internal/services/catalog_read_service/internal/products/consts/consts.go b/internal/services/catalogreadservice/internal/products/consts/consts.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/consts/consts.go rename to internal/services/catalogreadservice/internal/products/consts/consts.go diff --git a/internal/services/catalog_read_service/internal/products/contracts/data/product_cache_repository.go b/internal/services/catalogreadservice/internal/products/contracts/data/product_cache_repository.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/contracts/data/product_cache_repository.go rename to internal/services/catalogreadservice/internal/products/contracts/data/product_cache_repository.go diff --git a/internal/services/catalog_read_service/internal/products/contracts/data/product_repository.go b/internal/services/catalogreadservice/internal/products/contracts/data/product_repository.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/contracts/data/product_repository.go rename to internal/services/catalogreadservice/internal/products/contracts/data/product_repository.go diff --git a/internal/services/catalog_read_service/internal/products/contracts/params/product_route_params.go b/internal/services/catalogreadservice/internal/products/contracts/params/product_route_params.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/contracts/params/product_route_params.go rename to internal/services/catalogreadservice/internal/products/contracts/params/product_route_params.go diff --git a/internal/services/catalog_read_service/internal/products/data/repositories/mongo_product_repository.go b/internal/services/catalogreadservice/internal/products/data/repositories/mongo_product_repository.go similarity index 76% rename from internal/services/catalog_read_service/internal/products/data/repositories/mongo_product_repository.go rename to internal/services/catalogreadservice/internal/products/data/repositories/mongo_product_repository.go index 25673369..ea5c9dff 100644 --- a/internal/services/catalog_read_service/internal/products/data/repositories/mongo_product_repository.go +++ b/internal/services/catalogreadservice/internal/products/data/repositories/mongo_product_repository.go @@ -17,6 +17,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb/repository" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + utils2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" @@ -48,7 +49,11 @@ func NewMongoProductRepository( mongoOptions.Database, productCollection, ) - return &mongoProductRepository{log: log, mongoGenericRepository: mongoRepo, tracer: tracer} + return &mongoProductRepository{ + log: log, + mongoGenericRepository: mongoRepo, + tracer: tracer, + } } func (p *mongoProductRepository) GetAllProducts( @@ -61,17 +66,17 @@ func (p *mongoProductRepository) GetAllProducts( // https://www.mongodb.com/docs/drivers/go/current/fundamentals/crud/read-operations/query-document/ result, err := p.mongoGenericRepository.GetAll(ctx, listQuery) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[mongoProductRepository_GetAllProducts.Paginate] error in the paginate", + "error in the paginate", ), ) } p.log.Infow( - "[mongoProductRepository.GetAllProducts] products loaded", + "products loaded", logger.Fields{"ProductsResult": result}, ) @@ -91,18 +96,18 @@ func (p *mongoProductRepository) SearchProducts( result, err := p.mongoGenericRepository.Search(ctx, searchText, listQuery) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[mongoProductRepository_SearchProducts.Paginate] error in the paginate", + "error in the paginate", ), ) } p.log.Infow( fmt.Sprintf( - "[mongoProductRepository.SearchProducts] products loaded for search term '%s'", + "products loaded for search term '%s'", searchText, ), logger.Fields{"ProductsResult": result}, @@ -128,12 +133,12 @@ func (p *mongoProductRepository) GetProductById( product, err := p.mongoGenericRepository.GetById(ctx, id) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[mongoProductRepository_GetProductById.FindOne] can't find the product with id %s into the database.", + "can't find the product with id %s into the database.", uuid, ), ), @@ -143,7 +148,7 @@ func (p *mongoProductRepository) GetProductById( span.SetAttributes(attribute.Object("Product", product)) p.log.Infow( - fmt.Sprintf("[mongoProductRepository.GetProductById] product with id %s laoded", uuid), + fmt.Sprintf("product with id %s laoded", uuid), logger.Fields{"Product": product, "Id": uuid}, ) @@ -155,7 +160,10 @@ func (p *mongoProductRepository) GetProductByProductId( uuid string, ) (*models.Product, error) { productId := uuid - ctx, span := p.tracer.Start(ctx, "mongoProductRepository.GetProductByProductId") + ctx, span := p.tracer.Start( + ctx, + "mongoProductRepository.GetProductByProductId", + ) span.SetAttributes(attribute2.String("ProductId", productId)) defer span.End() @@ -164,12 +172,12 @@ func (p *mongoProductRepository) GetProductByProductId( map[string]interface{}{"productId": uuid}, ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[mongoProductRepository_GetProductById.FindOne] can't find the product with productId %s into the database.", + "can't find the product with productId %s into the database.", uuid, ), ), @@ -180,7 +188,7 @@ func (p *mongoProductRepository) GetProductByProductId( p.log.Infow( fmt.Sprintf( - "[mongoProductRepository.GetProductById] product with productId %s laoded", + "product with productId %s laoded", productId, ), logger.Fields{"Product": product, "ProductId": uuid}, @@ -198,11 +206,11 @@ func (p *mongoProductRepository) CreateProduct( err := p.mongoGenericRepository.Add(ctx, product) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[mongoProductRepository_CreateProduct.InsertOne] error in the inserting product into the database.", + "error in the inserting product into the database.", ), ) } @@ -211,7 +219,7 @@ func (p *mongoProductRepository) CreateProduct( p.log.Infow( fmt.Sprintf( - "[mongoProductRepository.CreateProduct] product with id '%s' created", + "product with id '%s' created", product.ProductId, ), logger.Fields{"Product": product, "Id": product.ProductId}, @@ -230,12 +238,12 @@ func (p *mongoProductRepository) UpdateProduct( err := p.mongoGenericRepository.Update(ctx, updateProduct) // https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/ if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[mongoProductRepository_UpdateProduct.FindOneAndUpdate] error in updating product with id %s into the database.", + "error in updating product with id %s into the database.", updateProduct.ProductId, ), ), @@ -245,7 +253,7 @@ func (p *mongoProductRepository) UpdateProduct( span.SetAttributes(attribute.Object("Product", updateProduct)) p.log.Infow( fmt.Sprintf( - "[mongoProductRepository.UpdateProduct] product with id '%s' updated", + "product with id '%s' updated", updateProduct.ProductId, ), logger.Fields{"Product": updateProduct, "Id": updateProduct.ProductId}, @@ -254,7 +262,10 @@ func (p *mongoProductRepository) UpdateProduct( return updateProduct, nil } -func (p *mongoProductRepository) DeleteProductByID(ctx context.Context, uuid string) error { +func (p *mongoProductRepository) DeleteProductByID( + ctx context.Context, + uuid string, +) error { ctx, span := p.tracer.Start(ctx, "mongoProductRepository.DeleteProductByID") span.SetAttributes(attribute2.String("Id", uuid)) defer span.End() @@ -266,14 +277,17 @@ func (p *mongoProductRepository) DeleteProductByID(ctx context.Context, uuid str err = p.mongoGenericRepository.Delete(ctx, id) if err != nil { - return tracing.TraceErrFromSpan(span, errors.WrapIf(err, fmt.Sprintf( - "[mongoProductRepository_DeleteProductByID.FindOneAndDelete] error in deleting product with id %s from the database.", - uuid, - ))) + return utils2.TraceErrStatusFromSpan( + span, + errors.WrapIf(err, fmt.Sprintf( + "error in deleting product with id %s from the database.", + uuid, + )), + ) } p.log.Infow( - fmt.Sprintf("[mongoProductRepository.DeleteProductByID] product with id %s deleted", uuid), + fmt.Sprintf("product with id %s deleted", uuid), logger.Fields{"Product": uuid}, ) diff --git a/internal/services/catalog_read_service/internal/products/data/repositories/redis_product_repository.go b/internal/services/catalogreadservice/internal/products/data/repositories/redis_product_repository.go similarity index 70% rename from internal/services/catalog_read_service/internal/products/data/repositories/redis_product_repository.go rename to internal/services/catalogreadservice/internal/products/data/repositories/redis_product_repository.go index c7634164..3387dd3b 100644 --- a/internal/services/catalog_read_service/internal/products/data/repositories/redis_product_repository.go +++ b/internal/services/catalogreadservice/internal/products/data/repositories/redis_product_repository.go @@ -8,6 +8,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" @@ -31,7 +32,11 @@ func NewRedisProductRepository( redisClient redis.UniversalClient, tracer tracing.AppTracer, ) data.ProductCacheRepository { - return &redisProductRepository{log: log, redisClient: redisClient, tracer: tracer} + return &redisProductRepository{ + log: log, + redisClient: redisClient, + tracer: tracer, + } } func (r *redisProductRepository) PutProduct( @@ -40,28 +45,30 @@ func (r *redisProductRepository) PutProduct( product *models.Product, ) error { ctx, span := r.tracer.Start(ctx, "redisRepository.PutProduct") - span.SetAttributes(attribute2.String("PrefixKey", r.getRedisProductPrefixKey())) + span.SetAttributes( + attribute2.String("PrefixKey", r.getRedisProductPrefixKey()), + ) span.SetAttributes(attribute2.String("Key", key)) defer span.End() productBytes, err := json.Marshal(product) if err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[redisProductRepository_PutProduct.Marshal] error marshalling product", + "error marshalling product", ), ) } if err := r.redisClient.HSetNX(ctx, r.getRedisProductPrefixKey(), key, productBytes).Err(); err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[redisProductRepository_PutProduct.HSetNX] error in updating product with key %s", + "error in updating product with key %s", key, ), ), @@ -72,7 +79,7 @@ func (r *redisProductRepository) PutProduct( r.log.Infow( fmt.Sprintf( - "[redisProductRepository.PutProduct] product with key '%s', prefix '%s' updated successfully", + "product with key '%s', prefix '%s' updated successfully", key, r.getRedisProductPrefixKey(), ), @@ -92,22 +99,25 @@ func (r *redisProductRepository) GetProductById( key string, ) (*models.Product, error) { ctx, span := r.tracer.Start(ctx, "redisRepository.GetProductById") - span.SetAttributes(attribute2.String("PrefixKey", r.getRedisProductPrefixKey())) + span.SetAttributes( + attribute2.String("PrefixKey", r.getRedisProductPrefixKey()), + ) span.SetAttributes(attribute2.String("Key", key)) defer span.End() - productBytes, err := r.redisClient.HGet(ctx, r.getRedisProductPrefixKey(), key).Bytes() + productBytes, err := r.redisClient.HGet(ctx, r.getRedisProductPrefixKey(), key). + Bytes() if err != nil { if errors.Is(err, redis.Nil) { return nil, nil } - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[redisProductRepository_GetProduct.HGet] error in getting product with Key %s from database", + "error in getting product with Key %s from database", key, ), ), @@ -116,14 +126,14 @@ func (r *redisProductRepository) GetProductById( var product models.Product if err := json.Unmarshal(productBytes, &product); err != nil { - return nil, tracing.TraceErrFromSpan(span, err) + return nil, utils.TraceErrStatusFromSpan(span, err) } span.SetAttributes(attribute.Object("Product", product)) r.log.Infow( fmt.Sprintf( - "[redisProductRepository.GetProductById] product with with key '%s', prefix '%s' laoded", + "product with with key '%s', prefix '%s' laoded", key, r.getRedisProductPrefixKey(), ), @@ -138,19 +148,24 @@ func (r *redisProductRepository) GetProductById( return &product, nil } -func (r *redisProductRepository) DeleteProduct(ctx context.Context, key string) error { +func (r *redisProductRepository) DeleteProduct( + ctx context.Context, + key string, +) error { ctx, span := r.tracer.Start(ctx, "redisRepository.DeleteProduct") - span.SetAttributes(attribute2.String("PrefixKey", r.getRedisProductPrefixKey())) + span.SetAttributes( + attribute2.String("PrefixKey", r.getRedisProductPrefixKey()), + ) span.SetAttributes(attribute2.String("Key", key)) defer span.End() if err := r.redisClient.HDel(ctx, r.getRedisProductPrefixKey(), key).Err(); err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, fmt.Sprintf( - "[redisProductRepository_DeleteProduct.HDel] error in deleting product with key %s", + "error in deleting product with key %s", key, ), ), @@ -159,7 +174,7 @@ func (r *redisProductRepository) DeleteProduct(ctx context.Context, key string) r.log.Infow( fmt.Sprintf( - "[redisProductRepository.DeleteProduct] product with key %s, prefix: %s deleted successfully", + "product with key %s, prefix: %s deleted successfully", key, r.getRedisProductPrefixKey(), ), @@ -171,21 +186,23 @@ func (r *redisProductRepository) DeleteProduct(ctx context.Context, key string) func (r *redisProductRepository) DeleteAllProducts(ctx context.Context) error { ctx, span := r.tracer.Start(ctx, "redisRepository.DeleteAllProducts") - span.SetAttributes(attribute2.String("PrefixKey", r.getRedisProductPrefixKey())) + span.SetAttributes( + attribute2.String("PrefixKey", r.getRedisProductPrefixKey()), + ) defer span.End() if err := r.redisClient.Del(ctx, r.getRedisProductPrefixKey()).Err(); err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrStatusFromSpan( span, errors.WrapIf( err, - "[redisProductRepository_DeleteAllProducts.Del] error in deleting all products", + "error in deleting all products", ), ) } r.log.Infow( - "[redisProductRepository.DeleteAllProducts] all products deleted", + "all products deleted", logger.Fields{"PrefixKey": r.getRedisProductPrefixKey()}, ) diff --git a/internal/services/catalog_read_service/internal/products/dto/product_dto.go b/internal/services/catalogreadservice/internal/products/dto/product_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/dto/product_dto.go rename to internal/services/catalogreadservice/internal/products/dto/product_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product.go b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product.go similarity index 98% rename from internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product.go rename to internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product.go index 16671916..2fb186d0 100644 --- a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product.go +++ b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product.go @@ -1,4 +1,4 @@ -package commands +package v1 import ( "time" diff --git a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product_handler.go b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product_handler.go similarity index 66% rename from internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product_handler.go rename to internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product_handler.go index b3db69bf..fa59ec4b 100644 --- a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/commands/create_product_handler.go +++ b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/create_product_handler.go @@ -1,18 +1,15 @@ -package commands +package v1 import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - - attribute2 "go.opentelemetry.io/otel/attribute" ) type CreateProductHandler struct { @@ -40,12 +37,6 @@ func (c *CreateProductHandler) Handle( ctx context.Context, command *CreateProduct, ) (*dtos.CreateProductResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "CreateProductHandler.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductId)) - span.SetAttributes(attribute.Object("Command", command)) - - defer span.End() - product := &models.Product{ Id: command.Id, // we generate id ourselves because auto generate mongo string id column with type _id is not an uuid ProductId: command.ProductId, @@ -57,31 +48,27 @@ func (c *CreateProductHandler) Handle( createdProduct, err := c.mongoRepository.CreateProduct(ctx, product) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[CreateProductHandler_Handle.CreateProduct] error in creating product in the mongo repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in creating product in the mongo repository", ) } err = c.redisRepository.PutProduct(ctx, createdProduct.Id, createdProduct) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[CreateProductHandler_Handle.PutProduct] error in creating product in the redis repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in creating product in the redis repository", ) } response := &dtos.CreateProductResponseDto{Id: createdProduct.Id} - span.SetAttributes(attribute.Object("CreateProductResponseDto", response)) c.log.Infow( - fmt.Sprintf("[CreateProductHandler.Handle] product with id: {%s} created", product.Id), + fmt.Sprintf( + "product with id: {%s} created", + product.Id, + ), logger.Fields{"ProductId": command.ProductId, "Id": product.Id}, ) diff --git a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go rename to internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos/create_product_response_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created.go b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created.go similarity index 94% rename from internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created.go rename to internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created.go index 5333c384..03519291 100644 --- a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created.go +++ b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created.go @@ -3,7 +3,7 @@ package externalEvents import ( "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type ProductCreatedV1 struct { diff --git a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created_consumer.go b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created_consumer.go similarity index 52% rename from internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created_consumer.go rename to internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created_consumer.go index f8a07529..479f39b0 100644 --- a/internal/services/catalog_read_service/internal/products/features/creating_product/v1/events/integration_events/external_events/product_created_consumer.go +++ b/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents/product_created_consumer.go @@ -4,14 +4,12 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - messageTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/commands" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos" "emperror.dev/errors" @@ -30,7 +28,11 @@ func NewProductCreatedConsumer( validator *validator.Validate, tracer tracing.AppTracer, ) consumer.ConsumerHandler { - return &productCreatedConsumer{logger: logger, validator: validator, tracer: tracer} + return &productCreatedConsumer{ + logger: logger, + validator: validator, + tracer: tracer, + } } func (c *productCreatedConsumer) Handle( @@ -42,11 +44,7 @@ func (c *productCreatedConsumer) Handle( return errors.New("error in casting message to ProductCreatedV1") } - ctx, span := c.tracer.Start(ctx, "productCreatedConsumer.Handle") - span.SetAttributes(attribute.Object("Message", consumeContext.Message())) - defer span.End() - - command, err := commands.NewCreateProduct( + command, err := v1.NewCreateProduct( product.ProductId, product.Name, product.Description, @@ -56,30 +54,22 @@ func (c *productCreatedConsumer) Handle( if err != nil { validationErr := customErrors.NewValidationErrorWrap( err, - "[productCreatedConsumer_Handle.StructCtx] command validation failed", - ) - c.logger.Errorf( - fmt.Sprintf( - "[productCreatedConsumer_Handle.StructCtx] err: {%v}", - messageTracing.TraceMessagingErrFromSpan(span, validationErr), - ), + "command validation failed", ) - return err + return validationErr } - _, err = mediatr.Send[*commands.CreateProduct, *dtos.CreateProductResponseDto](ctx, command) + _, err = mediatr.Send[*v1.CreateProduct, *dtos.CreateProductResponseDto]( + ctx, + command, + ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[productCreatedConsumer_Handle.Send] error in sending CreateProduct", - ) - c.logger.Errorw( fmt.Sprintf( - "[productCreatedConsumer_Handle.Send] id: {%s}, err: {%v}", + "error in sending CreateProduct with id: {%s}", command.ProductId, - messageTracing.TraceMessagingErrFromSpan(span, err), ), - logger.Fields{"Id": command.ProductId}, ) } c.logger.Info("Product consumer handled.") diff --git a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/commands/delete_product.go b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands/delete_product.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/deleting_products/v1/commands/delete_product.go rename to internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands/delete_product.go diff --git a/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands/delete_product_handler.go b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands/delete_product_handler.go new file mode 100644 index 00000000..bfe4f51e --- /dev/null +++ b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands/delete_product_handler.go @@ -0,0 +1,87 @@ +package commands + +import ( + "context" + "fmt" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" + + "github.com/mehdihadeli/go-mediatr" +) + +type DeleteProductCommand struct { + log logger.Logger + mongoRepository data.ProductRepository + redisRepository data.ProductCacheRepository + tracer tracing.AppTracer +} + +func NewDeleteProductHandler( + log logger.Logger, + repository data.ProductRepository, + redisRepository data.ProductCacheRepository, + tracer tracing.AppTracer, +) *DeleteProductCommand { + return &DeleteProductCommand{ + log: log, + mongoRepository: repository, + redisRepository: redisRepository, + tracer: tracer, + } +} + +func (c *DeleteProductCommand) Handle( + ctx context.Context, + command *DeleteProduct, +) (*mediatr.Unit, error) { + product, err := c.mongoRepository.GetProductByProductId( + ctx, + command.ProductId.String(), + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + fmt.Sprintf( + "error in fetching product with productId %s in the mongo repository", + command.ProductId, + ), + ) + } + if product == nil { + return nil, customErrors.NewNotFoundErrorWrap( + err, + fmt.Sprintf( + "product with productId %s not found", + command.ProductId, + ), + ) + } + + if err := c.mongoRepository.DeleteProductByID(ctx, product.Id); err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in deleting product in the mongo repository", + ) + } + + err = c.redisRepository.DeleteProduct(ctx, product.Id) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in deleting product in the redis repository", + ) + } + + c.log.Infow( + fmt.Sprintf( + "product with id: {%s} deleted", + product.Id, + ), + logger.Fields{"ProductId": command.ProductId, "Id": product.Id}, + ) + + return &mediatr.Unit{}, nil +} diff --git a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go similarity index 89% rename from internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go rename to internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go index a93dfa62..17f790ec 100644 --- a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go +++ b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted.go @@ -1,7 +1,7 @@ package externalEvents import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type ProductDeletedV1 struct { diff --git a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go similarity index 59% rename from internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go rename to internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go index cfc220a5..378b1188 100644 --- a/internal/services/catalog_read_service/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go +++ b/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events/product_deleted_consumer.go @@ -2,15 +2,12 @@ package externalEvents import ( "context" - "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - messageTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands" "emperror.dev/errors" @@ -30,7 +27,11 @@ func NewProductDeletedConsumer( validator *validator.Validate, tracer tracing.AppTracer, ) consumer.ConsumerHandler { - return &productDeletedConsumer{logger: logger, validator: validator, tracer: tracer} + return &productDeletedConsumer{ + logger: logger, + validator: validator, + tracer: tracer, + } } func (c *productDeletedConsumer) Handle( @@ -42,43 +43,29 @@ func (c *productDeletedConsumer) Handle( return errors.New("error in casting message to ProductDeletedV1") } - ctx, span := c.tracer.Start(ctx, "productDeletedConsumer.Handle") - span.SetAttributes(attribute.Object("Message", consumeContext.Message())) - defer span.End() - productUUID, err := uuid.FromString(message.ProductId) if err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[productDeletedConsumer_Handle.uuid.FromString] error in the converting uuid", - ) - c.logger.Errorf( - fmt.Sprintf( - "[productDeletedConsumer_Handle.uuid.FromString] err: %v", - messageTracing.TraceMessagingErrFromSpan(span, badRequestErr), - ), + "error in the converting uuid", ) - return err + return badRequestErr } command, err := commands.NewDeleteProduct(productUUID) if err != nil { validationErr := customErrors.NewValidationErrorWrap( err, - "[productDeletedConsumer_Handle.StructCtx] command validation failed", - ) - c.logger.Errorf( - fmt.Sprintf( - "[productDeletedConsumer_Consume.StructCtx] err: {%v}", - messageTracing.TraceMessagingErrFromSpan(span, validationErr), - ), + "command validation failed", ) - return err + return validationErr } _, err = mediatr.Send[*commands.DeleteProduct, *mediatr.Unit](ctx, command) + c.logger.Info("productDeletedConsumer executed successfully.") + return err } diff --git a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_request_dto.go b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_request_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_request_dto.go rename to internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_request_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_response_dto.go b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_response_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_response_dto.go rename to internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos/get_product_by_id_response_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go similarity index 69% rename from internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go rename to internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go index 87788619..63659962 100644 --- a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go +++ b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/endpoints/get_product_by_id_endpoint.go @@ -1,12 +1,10 @@ package endpoints import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/params" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries" @@ -44,17 +42,14 @@ func (ep *getProductByIdEndpoint) MapEndpoint() { func (ep *getProductByIdEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.GetProductByIdHttpRequests.Add(ctx, 1) request := &dtos.GetProductByIdRequestDto{} if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[getProductByIdEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[getProductByIdEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } @@ -62,9 +57,9 @@ func (ep *getProductByIdEndpoint) handler() echo.HandlerFunc { if err != nil { validationErr := customErrors.NewValidationErrorWrap( err, - "[getProductByIdEndpoint_handler.StructCtx] query validation failed", + "query validation failed", ) - ep.Logger.Errorf("[getProductByIdEndpoint_handler.StructCtx] err: {%v}", validationErr) + return validationErr } @@ -73,19 +68,10 @@ func (ep *getProductByIdEndpoint) handler() echo.HandlerFunc { query, ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[getProductByIdEndpoint_handler.Send] error in sending GetProductById", - ) - ep.Logger.Errorw( - fmt.Sprintf( - "[getProductByIdEndpoint_handler.Send] id: {%s}, err: {%v}", - query.Id, - err, - ), - logger.Fields{"ProductId": query.Id}, + "error in sending GetProductById", ) - return err } return c.JSON(http.StatusOK, queryResult) diff --git a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/queries/get_product_by_id.go b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries/get_product_by_id.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/queries/get_product_by_id.go rename to internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries/get_product_by_id.go diff --git a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go similarity index 63% rename from internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go rename to internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go index ded05f65..fabf678c 100644 --- a/internal/services/catalog_read_service/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go +++ b/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries/get_product_by_id_handler.go @@ -4,17 +4,14 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/dto" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - - attribute2 "go.opentelemetry.io/otel/attribute" ) type GetProductByIdHandler struct { @@ -42,36 +39,29 @@ func (q *GetProductByIdHandler) Handle( ctx context.Context, query *GetProductById, ) (*dtos.GetProductByIdResponseDto, error) { - ctx, span := q.tracer.Start(ctx, "getProductByIdHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - span.SetAttributes(attribute2.String("Id", query.Id.String())) - - redisProduct, err := q.redisRepository.GetProductById(ctx, query.Id.String()) - - var product *models.Product - - defer span.End() - + redisProduct, err := q.redisRepository.GetProductById( + ctx, + query.Id.String(), + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - fmt.Sprintf( - "[GetProductByIdHandler_Handle.GetProductById] error in getting product with id %d in the redis repository", - query.Id, - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + fmt.Sprintf( + "error in getting product with id %d in the redis repository", + query.Id, ), ) } + var product *models.Product + if redisProduct != nil { product = redisProduct } else { var mongoProduct *models.Product mongoProduct, err = q.mongoRepository.GetProductById(ctx, query.Id.String()) if err != nil { - return nil, tracing.TraceErrFromSpan(span, customErrors.NewApplicationErrorWrap(err, fmt.Sprintf("[GetProductByIdHandler_Handle.GetProductById] error in getting product with id %d in the mongo repository", query.Id))) + return nil, customErrors.NewApplicationErrorWrap(err, fmt.Sprintf("error in getting product with id %d in the mongo repository", query.Id)) } if mongoProduct == nil { mongoProduct, err = q.mongoRepository.GetProductByProductId(ctx, query.Id.String()) @@ -83,23 +73,23 @@ func (q *GetProductByIdHandler) Handle( product = mongoProduct err = q.redisRepository.PutProduct(ctx, product.Id, product) if err != nil { - return new(dtos.GetProductByIdResponseDto), tracing.TraceErrFromSpan(span, err) + return new(dtos.GetProductByIdResponseDto), err } } productDto, err := mapper.Map[*dto.ProductDto](product) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductByIdHandler_Handle.Map] error in the mapping product", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping product", ) } q.log.Infow( - fmt.Sprintf("[GetProductByIdHandler.Handle] product with id: {%s} fetched", query.Id), + fmt.Sprintf( + "product with id: {%s} fetched", + query.Id, + ), logger.Fields{"ProductId": product.ProductId, "Id": product.Id}, ) diff --git a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go rename to internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go rename to internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go similarity index 73% rename from internal/services/catalog_read_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go rename to internal/services/catalogreadservice/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go index 2179f01e..2369558b 100644 --- a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go +++ b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go @@ -1,12 +1,11 @@ package endpoints import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/params" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries" @@ -44,32 +43,24 @@ func (ep *getProductsEndpoint) MapEndpoint() { func (ep *getProductsEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.GetProductsHttpRequests.Add(ctx, 1) listQuery, err := utils.GetListQueryFromCtx(c) if err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[getProductsEndpoint_handler.GetListQueryFromCtx] error in getting data from query string", + "error in getting data from query string", ) - ep.Logger.Errorf( - fmt.Sprintf( - "[getProductsEndpoint_handler.GetListQueryFromCtx] err: %v", - badRequestErr, - ), - ) - return err + + return badRequestErr } request := queries.NewGetProducts(listQuery) if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[getProductsEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[getProductsEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } query := &queries.GetProducts{ListQuery: request.ListQuery} @@ -79,12 +70,10 @@ func (ep *getProductsEndpoint) handler() echo.HandlerFunc { query, ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[getProductsEndpoint_handler.Send] error in sending GetProducts", + "error in sending GetProducts", ) - ep.Logger.Error(fmt.Sprintf("[getProductsEndpoint_handler.Send] err: {%v}", err)) - return err } return c.JSON(http.StatusOK, queryResult) diff --git a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/queries/get_products.go b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries/get_products.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/getting_products/v1/queries/get_products.go rename to internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries/get_products.go diff --git a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/queries/get_products_handler.go b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries/get_products_handler.go similarity index 61% rename from internal/services/catalog_read_service/internal/products/features/getting_products/v1/queries/get_products_handler.go rename to internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries/get_products_handler.go index 85e8034d..3fd0a93c 100644 --- a/internal/services/catalog_read_service/internal/products/features/getting_products/v1/queries/get_products_handler.go +++ b/internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries/get_products_handler.go @@ -3,10 +3,9 @@ package queries import ( "context" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/dto" @@ -24,40 +23,36 @@ func NewGetProductsHandler( mongoRepository data.ProductRepository, tracer tracing.AppTracer, ) *GetProductsHandler { - return &GetProductsHandler{log: log, mongoRepository: mongoRepository, tracer: tracer} + return &GetProductsHandler{ + log: log, + mongoRepository: mongoRepository, + tracer: tracer, + } } func (c *GetProductsHandler) Handle( ctx context.Context, query *GetProducts, ) (*dtos.GetProductsResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "GetProductsHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - defer span.End() - products, err := c.mongoRepository.GetAllProducts(ctx, query.ListQuery) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductsHandler_Handle.GetAllProducts] error in getting products in the repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in getting products in the repository", ) } - listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto](products) + listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto]( + products, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[GetProductsHandler_Handle.ListResultToListResultDto] error in the mapping ListResultToListResultDto", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping ListResultToListResultDto", ) } - c.log.Info("[GetProductsHandler.Handle] products fetched") + c.log.Info("products fetched") return &dtos.GetProductsResponseDto{Products: listResultDto}, nil } diff --git a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/dtos/search_products_request_dto.go b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/dtos/search_products_request_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/searching_products/v1/dtos/search_products_request_dto.go rename to internal/services/catalogreadservice/internal/products/features/searching_products/v1/dtos/search_products_request_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/dtos/search_products_response_dto.go b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/dtos/search_products_response_dto.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/searching_products/v1/dtos/search_products_response_dto.go rename to internal/services/catalogreadservice/internal/products/features/searching_products/v1/dtos/search_products_response_dto.go diff --git a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go similarity index 71% rename from internal/services/catalog_read_service/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go rename to internal/services/catalogreadservice/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go index 36892c0a..2455d0d1 100644 --- a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go +++ b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/endpoints/search_products_endpoint.go @@ -1,12 +1,11 @@ package endpoints import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/params" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/searching_products/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries" @@ -44,21 +43,15 @@ func (ep *searchProductsEndpoint) MapEndpoint() { func (ep *searchProductsEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.SearchProductHttpRequests.Add(ctx, 1) listQuery, err := utils.GetListQueryFromCtx(c) if err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[searchProductsEndpoint_handler.GetListQueryFromCtx] error in getting data from query string", + "error in getting data from query string", ) - ep.Logger.Errorf( - fmt.Sprintf( - "[searchProductsEndpoint_handler.GetListQueryFromCtx] err: %v", - badRequestErr, - ), - ) - return err + + return badRequestErr } request := &dtos.SearchProductsRequestDto{ListQuery: listQuery} @@ -66,11 +59,9 @@ func (ep *searchProductsEndpoint) handler() echo.HandlerFunc { if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[searchProductsEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[searchProductsEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } @@ -82,9 +73,9 @@ func (ep *searchProductsEndpoint) handler() echo.HandlerFunc { if err := query.Validate(); err != nil { validationErr := customErrors.NewValidationErrorWrap( err, - "[searchProductsEndpoint_handler.StructCtx] query validation failed", + "query validation failed", ) - ep.Logger.Errorf("[searchProductsEndpoint_handler.StructCtx] err: {%v}", validationErr) + return validationErr } @@ -93,12 +84,10 @@ func (ep *searchProductsEndpoint) handler() echo.HandlerFunc { query, ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[searchProductsEndpoint_handler.Send] error in sending SearchProducts", + "error in sending SearchProducts", ) - ep.Logger.Error(fmt.Sprintf("[searchProductsEndpoint_handler.Send] err: {%v}", err)) - return err } return c.JSON(http.StatusOK, queryResult) diff --git a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/queries/search_products.go b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries/search_products.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/searching_products/v1/queries/search_products.go rename to internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries/search_products.go diff --git a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/queries/search_products_handler.go b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries/search_products_handler.go similarity index 57% rename from internal/services/catalog_read_service/internal/products/features/searching_products/v1/queries/search_products_handler.go rename to internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries/search_products_handler.go index 588fe8d4..81c95ccb 100644 --- a/internal/services/catalog_read_service/internal/products/features/searching_products/v1/queries/search_products_handler.go +++ b/internal/services/catalogreadservice/internal/products/features/searching_products/v1/queries/search_products_handler.go @@ -3,10 +3,9 @@ package queries import ( "context" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/dto" @@ -24,39 +23,39 @@ func NewSearchProductsHandler( repository data.ProductRepository, tracer tracing.AppTracer, ) *SearchProductsHandler { - return &SearchProductsHandler{log: log, mongoRepository: repository, tracer: tracer} + return &SearchProductsHandler{ + log: log, + mongoRepository: repository, + tracer: tracer, + } } func (c *SearchProductsHandler) Handle( ctx context.Context, query *SearchProducts, ) (*dtos.SearchProductsResponseDto, error) { - ctx, span := c.tracer.Start(ctx, "SearchProductsHandler.Handle") - span.SetAttributes(attribute.Object("Query", query)) - defer span.End() - - products, err := c.mongoRepository.SearchProducts(ctx, query.SearchText, query.ListQuery) + products, err := c.mongoRepository.SearchProducts( + ctx, + query.SearchText, + query.ListQuery, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[SearchProductsHandler_Handle.SearchProducts] error in searching products in the repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in searching products in the repository", ) } - listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto](products) + listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto]( + products, + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[SearchProductsHandler_Handle.ListResultToListResultDto] error in the mapping ListResultToListResultDto", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping ListResultToListResultDto", ) } - c.log.Info("[SearchProductsHandler.Handle] products fetched") + c.log.Info("products fetched") return &dtos.SearchProductsResponseDto{Products: listResultDto}, nil } diff --git a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/commands/update_product.go b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands/update_product.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/features/updating_products/v1/commands/update_product.go rename to internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands/update_product.go diff --git a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/commands/update_product_handler.go b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands/update_product_handler.go similarity index 51% rename from internal/services/catalog_read_service/internal/products/features/updating_products/v1/commands/update_product_handler.go rename to internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands/update_product_handler.go index 90faf295..702d9155 100644 --- a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/commands/update_product_handler.go +++ b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands/update_product_handler.go @@ -4,14 +4,12 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/contracts/data" "github.com/mehdihadeli/go-mediatr" - attribute2 "go.opentelemetry.io/otel/attribute" ) type UpdateProductHandler struct { @@ -39,34 +37,26 @@ func (c *UpdateProductHandler) Handle( ctx context.Context, command *UpdateProduct, ) (*mediatr.Unit, error) { - ctx, span := c.tracer.Start(ctx, "UpdateProductHandler.Handle") - span.SetAttributes(attribute2.String("ProductId", command.ProductId.String())) - span.SetAttributes(attribute.Object("Command", command)) - defer span.End() - - product, err := c.mongoRepository.GetProductByProductId(ctx, command.ProductId.String()) + product, err := c.mongoRepository.GetProductByProductId( + ctx, + command.ProductId.String(), + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - fmt.Sprintf( - "[UpdateProductHandler_Handle.GetProductById] error in fetching product with productId %s in the mongo repository", - command.ProductId, - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + fmt.Sprintf( + "error in fetching product with productId %s in the mongo repository", + command.ProductId, ), ) } if product == nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewNotFoundErrorWrap( - err, - fmt.Sprintf( - "[UpdateProductHandler_Handle.GetProductById] product with productId %s not found", - command.ProductId, - ), + return nil, customErrors.NewNotFoundErrorWrap( + err, + fmt.Sprintf( + "product with productId %s not found", + command.ProductId, ), ) } @@ -78,28 +68,25 @@ func (c *UpdateProductHandler) Handle( _, err = c.mongoRepository.UpdateProduct(ctx, product) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[UpdateProductHandler_Handle.UpdateProduct] error in updating product in the mongo repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in updating product in the mongo repository", ) } err = c.redisRepository.PutProduct(ctx, product.Id, product) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - customErrors.NewApplicationErrorWrap( - err, - "[UpdateProductHandler_Handle.PutProduct] error in updating product in the redis repository", - ), + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in updating product in the redis repository", ) } c.log.Infow( - fmt.Sprintf("[UpdateProductHandler.Handle] product with id: {%s} updated", product.Id), + fmt.Sprintf( + "product with id: {%s} updated", + product.Id, + ), logger.Fields{"ProductId": command.ProductId, "Id": product.Id}, ) diff --git a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go similarity index 94% rename from internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go rename to internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go index 289e1342..27cf00e7 100644 --- a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go +++ b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated.go @@ -3,7 +3,7 @@ package externalEvents import ( "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" ) type ProductUpdatedV1 struct { diff --git a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go similarity index 85% rename from internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go rename to internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go index 4a92ac93..1df5b42e 100644 --- a/internal/services/catalog_read_service/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go +++ b/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events/product_updated_consumer.go @@ -4,13 +4,13 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/consumer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/consumer" - messageTracing "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands" "emperror.dev/errors" @@ -30,7 +30,11 @@ func NewProductUpdatedConsumer( validator *validator.Validate, tracer tracing.AppTracer, ) consumer.ConsumerHandler { - return &productUpdatedConsumer{logger: logger, validator: validator, tracer: tracer} + return &productUpdatedConsumer{ + logger: logger, + validator: validator, + tracer: tracer, + } } func (c *productUpdatedConsumer) Handle( @@ -56,7 +60,7 @@ func (c *productUpdatedConsumer) Handle( c.logger.Errorf( fmt.Sprintf( "[updateProductConsumer_Consume.uuid.FromString] err: %v", - messageTracing.TraceMessagingErrFromSpan(span, badRequestErr), + utils.TraceErrStatusFromSpan(span, badRequestErr), ), ) return err @@ -76,7 +80,7 @@ func (c *productUpdatedConsumer) Handle( c.logger.Errorf( fmt.Sprintf( "[updateProductConsumer_Consume.StructCtx] err: {%v}", - messageTracing.TraceMessagingErrFromSpan(span, validationErr), + utils.TraceErrStatusFromSpan(span, validationErr), ), ) return err @@ -92,7 +96,7 @@ func (c *productUpdatedConsumer) Handle( fmt.Sprintf( "[updateProductConsumer_Consume.Send] id: {%s}, err: {%v}", command.ProductId, - messageTracing.TraceMessagingErrFromSpan(span, err), + utils.TraceErrStatusFromSpan(span, err), ), logger.Fields{"Id": command.ProductId}, ) diff --git a/internal/services/catalog_read_service/internal/products/models/product.go b/internal/services/catalogreadservice/internal/products/models/product.go similarity index 100% rename from internal/services/catalog_read_service/internal/products/models/product.go rename to internal/services/catalogreadservice/internal/products/models/product.go diff --git a/internal/services/catalog_read_service/internal/products/products_fx.go b/internal/services/catalogreadservice/internal/products/products_fx.go similarity index 97% rename from internal/services/catalog_read_service/internal/products/products_fx.go rename to internal/services/catalogreadservice/internal/products/products_fx.go index 3be53eaf..732157e6 100644 --- a/internal/services/catalog_read_service/internal/products/products_fx.go +++ b/internal/services/catalogreadservice/internal/products/products_fx.go @@ -1,8 +1,8 @@ package products import ( - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/data/repositories" getProductByIdV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/endpoints" getProductsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/getting_products/v1/endpoints" diff --git a/internal/services/catalog_read_service/internal/shared/app/app.go b/internal/services/catalogreadservice/internal/shared/app/app.go similarity index 100% rename from internal/services/catalog_read_service/internal/shared/app/app.go rename to internal/services/catalogreadservice/internal/shared/app/app.go diff --git a/internal/services/catalog_read_service/internal/shared/app/application.go b/internal/services/catalogreadservice/internal/shared/app/application.go similarity index 93% rename from internal/services/catalog_read_service/internal/shared/app/application.go rename to internal/services/catalogreadservice/internal/shared/app/application.go index 4ff43358..bd2e0ce3 100644 --- a/internal/services/catalog_read_service/internal/shared/app/application.go +++ b/internal/services/catalogreadservice/internal/shared/app/application.go @@ -1,7 +1,7 @@ package app import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/configurations/catalogs" @@ -18,7 +18,7 @@ func NewCatalogsReadApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *CatalogsReadApplication { app := fxapp.NewApplication(providers, decorates, options, logger, environment) return &CatalogsReadApplication{ diff --git a/internal/services/catalog_read_service/internal/shared/app/application_builder.go b/internal/services/catalogreadservice/internal/shared/app/application_builder.go similarity index 100% rename from internal/services/catalog_read_service/internal/shared/app/application_builder.go rename to internal/services/catalogreadservice/internal/shared/app/application_builder.go diff --git a/internal/services/catalog_read_service/internal/shared/app/test/test_app.go b/internal/services/catalogreadservice/internal/shared/app/test/test_app.go similarity index 99% rename from internal/services/catalog_read_service/internal/shared/app/test/test_app.go rename to internal/services/catalogreadservice/internal/shared/app/test/test_app.go index ca8518c1..6430781c 100644 --- a/internal/services/catalog_read_service/internal/shared/app/test/test_app.go +++ b/internal/services/catalogreadservice/internal/shared/app/test/test_app.go @@ -7,7 +7,7 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/config" + config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" diff --git a/internal/services/catalog_read_service/internal/shared/app/test/test_application.go b/internal/services/catalogreadservice/internal/shared/app/test/test_application.go similarity index 95% rename from internal/services/catalog_read_service/internal/shared/app/test/test_application.go rename to internal/services/catalogreadservice/internal/shared/app/test/test_application.go index e093ff9d..04b63352 100644 --- a/internal/services/catalog_read_service/internal/shared/app/test/test_application.go +++ b/internal/services/catalogreadservice/internal/shared/app/test/test_application.go @@ -1,7 +1,7 @@ package test import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/test" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/app" @@ -22,7 +22,7 @@ func NewCatalogsReadTestApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *CatalogsReadTestApplication { testApp := test.NewTestApplication( tb, diff --git a/internal/services/catalog_read_service/internal/shared/app/test/test_application_builder.go b/internal/services/catalogreadservice/internal/shared/app/test/test_application_builder.go similarity index 100% rename from internal/services/catalog_read_service/internal/shared/app/test/test_application_builder.go rename to internal/services/catalogreadservice/internal/shared/app/test/test_application_builder.go diff --git a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator.go b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator.go similarity index 95% rename from internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator.go rename to internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator.go index 6cad889b..18682207 100644 --- a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator.go +++ b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure" @@ -49,7 +49,7 @@ func (ic *CatalogsServiceConfigurator) MapCatalogsEndpoints() { func(catalogsServer customEcho.EchoHttpServer, cfg *config.Config) error { catalogsServer.SetupDefaultMiddlewares() - // Config catalogs root endpoint + // config catalogs root endpoint catalogsServer.RouteBuilder(). RegisterRoutes(func(e *echo.Echo) { e.GET("", func(ec echo.Context) error { @@ -63,7 +63,7 @@ func (ic *CatalogsServiceConfigurator) MapCatalogsEndpoints() { }) }) - // Config catalogs swagger + // config catalogs swagger ic.configSwagger(catalogsServer.RouteBuilder()) return nil diff --git a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go similarity index 94% rename from internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go rename to internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go index 8a2fcfbc..180f501a 100644 --- a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go +++ b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go @@ -1,7 +1,7 @@ package catalogs import ( - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/docs" "github.com/labstack/echo/v4" diff --git a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_fx.go b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_fx.go similarity index 100% rename from internal/services/catalog_read_service/internal/shared/configurations/catalogs/catalogs_fx.go rename to internal/services/catalogreadservice/internal/shared/configurations/catalogs/catalogs_fx.go diff --git a/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go new file mode 100644 index 00000000..da5f2fed --- /dev/null +++ b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go @@ -0,0 +1,48 @@ +package infrastructure + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + loggingpipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/pipelines" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + metricspipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics/mediatr/pipelines" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + tracingpipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/mediatr/pipelines" + postgrespipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/pipelines" + + "github.com/mehdihadeli/go-mediatr" + "gorm.io/gorm" +) + +type InfrastructureConfigurator struct { + contracts.Application +} + +func NewInfrastructureConfigurator( + app contracts.Application, +) *InfrastructureConfigurator { + return &InfrastructureConfigurator{ + Application: app, + } +} + +func (ic *InfrastructureConfigurator) ConfigInfrastructures() { + ic.ResolveFunc( + func(l logger.Logger, tracer tracing.AppTracer, metrics metrics.AppMetrics, db *gorm.DB) error { + err := mediatr.RegisterRequestPipelineBehaviors( + loggingpipelines.NewMediatorLoggingPipeline(l), + tracingpipelines.NewMediatorTracingPipeline( + tracer, + tracingpipelines.WithLogger(l), + ), + metricspipelines.NewMediatorMetricsPipeline( + metrics, + metricspipelines.WithLogger(l), + ), + postgrespipelines.NewMediatorTransactionPipeline(l, db), + ) + + return err + }, + ) +} diff --git a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go similarity index 95% rename from internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go rename to internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go index 52f6a59f..ab9682d3 100644 --- a/internal/services/catalog_read_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go +++ b/internal/services/catalogreadservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go @@ -4,10 +4,10 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" @@ -26,7 +26,6 @@ var Module = fx.Module( customEcho.Module, grpc.Module, mongodb.Module, - otel.Module, redis.Module, rabbitmq.ModuleFunc( func(v *validator.Validate, l logger.Logger, tracer tracing.AppTracer) configurations.RabbitMQConfigurationBuilderFuc { @@ -36,6 +35,8 @@ var Module = fx.Module( }, ), health.Module, + tracing.Module, + metrics.Module, // Other provides fx.Provide(validator.New), diff --git a/internal/services/catalog_read_service/internal/shared/contracts/catalogs_metrics.go b/internal/services/catalogreadservice/internal/shared/contracts/catalogs_metrics.go similarity index 66% rename from internal/services/catalog_read_service/internal/shared/contracts/catalogs_metrics.go rename to internal/services/catalogreadservice/internal/shared/contracts/catalogs_metrics.go index f6701e6a..7db30b63 100644 --- a/internal/services/catalog_read_service/internal/shared/contracts/catalogs_metrics.go +++ b/internal/services/catalogreadservice/internal/shared/contracts/catalogs_metrics.go @@ -10,12 +10,6 @@ type CatalogsMetrics struct { DeleteProductGrpcRequests metric.Float64Counter GetProductByIdGrpcRequests metric.Float64Counter SearchProductGrpcRequests metric.Float64Counter - CreateProductHttpRequests metric.Float64Counter - UpdateProductHttpRequests metric.Float64Counter - DeleteProductHttpRequests metric.Float64Counter - GetProductByIdHttpRequests metric.Float64Counter - GetProductsHttpRequests metric.Float64Counter - SearchProductHttpRequests metric.Float64Counter SuccessRabbitMQMessages metric.Float64Counter ErrorRabbitMQMessages metric.Float64Counter CreateProductRabbitMQMessages metric.Float64Counter diff --git a/internal/services/catalog_read_service/internal/shared/test_fixture/integration/integration_test_fixture.go b/internal/services/catalogreadservice/internal/shared/testfixture/integration/integration_test_fixture.go similarity index 96% rename from internal/services/catalog_read_service/internal/shared/test_fixture/integration/integration_test_fixture.go rename to internal/services/catalogreadservice/internal/shared/testfixture/integration/integration_test_fixture.go index 2dbb824a..d485a7d4 100644 --- a/internal/services/catalog_read_service/internal/shared/test_fixture/integration/integration_test_fixture.go +++ b/internal/services/catalogreadservice/internal/shared/testfixture/integration/integration_test_fixture.go @@ -5,9 +5,9 @@ import ( "testing" "time" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" @@ -73,8 +73,8 @@ func NewIntegrationTestSharedFixture( return shared } -func (i *IntegrationTestSharedFixture) InitializeTest() { - i.Log.Info("InitializeTest started") +func (i *IntegrationTestSharedFixture) SetupTest() { + i.Log.Info("SetupTest started") // seed data in each test res, err := seedData(i.mongoClient, i.MongoOptions.Database) @@ -85,8 +85,8 @@ func (i *IntegrationTestSharedFixture) InitializeTest() { i.Items = res } -func (i *IntegrationTestSharedFixture) DisposeTest() { - i.Log.Info("DisposeTest started") +func (i *IntegrationTestSharedFixture) TearDownTest() { + i.Log.Info("TearDownTest started") // cleanup test containers with their hooks if err := i.cleanupRabbitmqData(); err != nil { diff --git a/internal/services/catalog_read_service/mocks/ProductCacheRepository.go b/internal/services/catalogreadservice/mocks/ProductCacheRepository.go similarity index 100% rename from internal/services/catalog_read_service/mocks/ProductCacheRepository.go rename to internal/services/catalogreadservice/mocks/ProductCacheRepository.go diff --git a/internal/services/catalog_read_service/mocks/ProductRepository.go b/internal/services/catalogreadservice/mocks/ProductRepository.go similarity index 100% rename from internal/services/catalog_read_service/mocks/ProductRepository.go rename to internal/services/catalogreadservice/mocks/ProductRepository.go diff --git a/internal/services/catalog_read_service/mocks/UnsafeProductsServiceServer.go b/internal/services/catalogreadservice/mocks/UnsafeProductsServiceServer.go similarity index 100% rename from internal/services/catalog_read_service/mocks/UnsafeProductsServiceServer.go rename to internal/services/catalogreadservice/mocks/UnsafeProductsServiceServer.go diff --git a/internal/services/catalog_read_service/readme.md b/internal/services/catalogreadservice/readme.md similarity index 100% rename from internal/services/catalog_read_service/readme.md rename to internal/services/catalogreadservice/readme.md diff --git a/internal/services/catalog_read_service/revive-config.toml b/internal/services/catalogreadservice/revive-config.toml similarity index 100% rename from internal/services/catalog_read_service/revive-config.toml rename to internal/services/catalogreadservice/revive-config.toml diff --git a/internal/services/catalog_read_service/staticcheck.conf b/internal/services/catalogreadservice/staticcheck.conf similarity index 100% rename from internal/services/catalog_read_service/staticcheck.conf rename to internal/services/catalogreadservice/staticcheck.conf diff --git a/internal/services/catalog_read_service/taskfile.yml b/internal/services/catalogreadservice/taskfile.yml similarity index 100% rename from internal/services/catalog_read_service/taskfile.yml rename to internal/services/catalogreadservice/taskfile.yml diff --git a/internal/services/catalog_read_service/taskfile_db.yml b/internal/services/catalogreadservice/taskfile_db.yml similarity index 100% rename from internal/services/catalog_read_service/taskfile_db.yml rename to internal/services/catalogreadservice/taskfile_db.yml diff --git a/internal/services/catalog_read_service/taskfile_test.yml b/internal/services/catalogreadservice/taskfile_test.yml similarity index 100% rename from internal/services/catalog_read_service/taskfile_test.yml rename to internal/services/catalogreadservice/taskfile_test.yml diff --git a/internal/services/catalog_read_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go b/internal/services/catalogreadservice/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go similarity index 88% rename from internal/services/catalog_read_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go rename to internal/services/catalogreadservice/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go index 19746ca7..d37b30e9 100644 --- a/internal/services/catalog_read_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go +++ b/internal/services/catalogreadservice/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/gavv/httpexpect/v2" @@ -19,7 +19,7 @@ func TestGetProductById(t *testing.T) { e2eFixture := integration.NewIntegrationTestSharedFixture(t) Convey("Get Product By Id Feature", t, func() { - e2eFixture.InitializeTest() + e2eFixture.SetupTest() ctx := context.Background() id := e2eFixture.Items[0].Id @@ -39,6 +39,6 @@ func TestGetProductById(t *testing.T) { }) }) - e2eFixture.DisposeTest() + e2eFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go b/internal/services/catalogreadservice/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go similarity index 86% rename from internal/services/catalog_read_service/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go rename to internal/services/catalogreadservice/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go index 124c17e9..8c7833c5 100644 --- a/internal/services/catalog_read_service/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go +++ b/internal/services/catalogreadservice/test/end_to_end/products/features/getting_products/v1/get_products_endpoint_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/gavv/httpexpect/v2" @@ -19,7 +19,7 @@ func TestGetAllProducts(t *testing.T) { e2eFixture := integration.NewIntegrationTestSharedFixture(t) Convey("Get All Products Feature", t, func() { - e2eFixture.InitializeTest() + e2eFixture.SetupTest() ctx := context.Background() Convey("Get all products returns ok status", func() { @@ -35,6 +35,6 @@ func TestGetAllProducts(t *testing.T) { }) }) - e2eFixture.DisposeTest() + e2eFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/data/mongo_product_repository_test.go b/internal/services/catalogreadservice/test/integration/products/data/mongo_product_repository_test.go similarity index 96% rename from internal/services/catalog_read_service/test/integration/products/data/mongo_product_repository_test.go rename to internal/services/catalogreadservice/test/integration/products/data/mongo_product_repository_test.go index 7349a946..e9dab242 100644 --- a/internal/services/catalog_read_service/test/integration/products/data/mongo_product_repository_test.go +++ b/internal/services/catalogreadservice/test/integration/products/data/mongo_product_repository_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -24,7 +24,7 @@ func TestProductPostgresRepository(t *testing.T) { // scenario Convey("Product Repository", t, func() { - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() Convey("When we create the new product in the database", func() { ctx := context.Background() @@ -165,6 +165,6 @@ func TestProductPostgresRepository(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/commands/create_product_test.go b/internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/commands/create_product_test.go similarity index 81% rename from internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/commands/create_product_test.go rename to internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/commands/create_product_test.go index e4db5731..cc66cb18 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/commands/create_product_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/commands/create_product_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/commands" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/brianvoe/gofakeit/v6" "github.com/mehdihadeli/go-mediatr" @@ -24,7 +24,7 @@ func TestCreateProduct(t *testing.T) { Convey("Creating Product Feature", t, func() { ctx := context.Background() - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -32,7 +32,7 @@ func TestCreateProduct(t *testing.T) { "Creating a new product and saving it to the database for a none-existing product", func() { Convey("Given new product doesn't exists in the system", func() { - command, err := commands.NewCreateProduct( + command, err := v1.NewCreateProduct( uuid.NewV4().String(), gofakeit.Name(), gofakeit.AdjectiveDescriptive(), @@ -44,7 +44,7 @@ func TestCreateProduct(t *testing.T) { Convey( "When the CreateProduct command is executed and product doesn't exists", func() { - result, err := mediatr.Send[*commands.CreateProduct, *dtos.CreateProductResponseDto]( + result, err := mediatr.Send[*v1.CreateProduct, *dtos.CreateProductResponseDto]( ctx, command, ) @@ -78,6 +78,6 @@ func TestCreateProduct(t *testing.T) { }, ) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/events/product_created_test.go b/internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/events/product_created_test.go similarity index 94% rename from internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/events/product_created_test.go rename to internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/events/product_created_test.go index 65bf888d..752f13c9 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/creating_product/v1/events/product_created_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/creating_product/v1/events/product_created_test.go @@ -10,12 +10,12 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" - externalEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integration_events/external_events" + externalEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/creating_product/v1/events/integrationevents/externalevents" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -33,7 +33,7 @@ func TestProductCreatedConsumer(t *testing.T) { Convey("Product Created Feature", t, func() { // will execute with each subtest - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() ctx := context.Background() // https://specflow.org/learn/gherkin/#learn-gherkin @@ -107,7 +107,7 @@ func TestProductCreatedConsumer(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) integrationTestSharedFixture.Log.Info("TearDownSuite started") diff --git a/internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go b/internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go similarity index 91% rename from internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go rename to internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go index 7deae9de..41cbc2cb 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/commands/delete_product_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/mehdihadeli/go-mediatr" uuid "github.com/satori/go.uuid" @@ -23,7 +23,7 @@ func TestDeleteProduct(t *testing.T) { Convey("Deleting Product Feature", t, func() { ctx := context.Background() - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -65,6 +65,6 @@ func TestDeleteProduct(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go b/internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go similarity index 95% rename from internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go rename to internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go index 8cf628db..bd5b8a0b 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/deleting_product/v1/events/product_deleted_test.go @@ -8,12 +8,12 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" externalEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/deleting_products/v1/events/integration_events/external_events" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" uuid "github.com/satori/go.uuid" @@ -31,7 +31,7 @@ func TestProductDeleted(t *testing.T) { Convey("Product Deleted Feature", t, func() { ctx := context.Background() // will execute with each subtest - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -105,7 +105,7 @@ func TestProductDeleted(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) integrationTestSharedFixture.Log.Info("TearDownSuite started") diff --git a/internal/services/catalog_read_service/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go b/internal/services/catalogreadservice/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go similarity index 94% rename from internal/services/catalog_read_service/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go rename to internal/services/catalogreadservice/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go index 5f57ce80..ac39eb72 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/getting_product_by_id/v1/queries/get_product_by_id_test.go @@ -9,7 +9,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/get_product_by_id/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/mehdihadeli/go-mediatr" uuid "github.com/satori/go.uuid" @@ -22,7 +22,7 @@ func TestGetProductById(t *testing.T) { ctx := context.Background() Convey("Get Product by ID Feature", t, func() { - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() knownProductID, err := uuid.FromString(integrationTestSharedFixture.Items[0].Id) unknownProductID := uuid.NewV4() @@ -81,6 +81,6 @@ func TestGetProductById(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go b/internal/services/catalogreadservice/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go similarity index 93% rename from internal/services/catalog_read_service/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go rename to internal/services/catalogreadservice/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go index 69e0e70d..36d938f4 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/getting_products/v1/queries/get_products_handler_test.go @@ -10,7 +10,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/getting_products/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/getting_products/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/mehdihadeli/go-mediatr" @@ -22,7 +22,7 @@ func TestGetProducts(t *testing.T) { Convey("Get All Products Feature", t, func() { ctx := context.Background() - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -61,6 +61,6 @@ func TestGetProducts(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go b/internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go similarity index 93% rename from internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go rename to internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go index a87094da..edf612b3 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/commands/update_product_handler_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/updating_products/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/brianvoe/gofakeit/v6" "github.com/mehdihadeli/go-mediatr" @@ -22,7 +22,7 @@ func TestUpdateProduct(t *testing.T) { Convey("Updating Product Feature", t, func() { ctx := context.Background() - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -66,6 +66,6 @@ func TestUpdateProduct(t *testing.T) { }) }) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) } diff --git a/internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/events/product_updated_test.go b/internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/events/product_updated_test.go similarity index 96% rename from internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/events/product_updated_test.go rename to internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/events/product_updated_test.go index 37282c39..fbac4a19 100644 --- a/internal/services/catalog_read_service/test/integration/products/features/updating_product/v1/events/product_updated_test.go +++ b/internal/services/catalogreadservice/test/integration/products/features/updating_product/v1/events/product_updated_test.go @@ -8,12 +8,12 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" testUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/utils" externalEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/features/updating_products/v1/events/integration_events/external_events" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/test_fixture/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogreadservice/internal/shared/testfixture/integration" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -33,7 +33,7 @@ func TestProductUpdatedConsumer(t *testing.T) { Convey("Product Created Feature", t, func() { ctx := context.Background() - integrationTestSharedFixture.InitializeTest() + integrationTestSharedFixture.SetupTest() // https://specflow.org/learn/gherkin/#learn-gherkin // scenario @@ -143,7 +143,7 @@ func TestProductUpdatedConsumer(t *testing.T) { }, ) - integrationTestSharedFixture.DisposeTest() + integrationTestSharedFixture.TearDownTest() }) integrationTestSharedFixture.Log.Info("TearDownSuite started") diff --git a/internal/services/catalog_read_service/test/load_tests/.openapi-generator-ignore b/internal/services/catalogreadservice/test/load_tests/.openapi-generator-ignore similarity index 100% rename from internal/services/catalog_read_service/test/load_tests/.openapi-generator-ignore rename to internal/services/catalogreadservice/test/load_tests/.openapi-generator-ignore diff --git a/internal/services/catalog_read_service/test/load_tests/.openapi-generator/FILES b/internal/services/catalogreadservice/test/load_tests/.openapi-generator/FILES similarity index 100% rename from internal/services/catalog_read_service/test/load_tests/.openapi-generator/FILES rename to internal/services/catalogreadservice/test/load_tests/.openapi-generator/FILES diff --git a/internal/services/catalog_read_service/test/load_tests/.openapi-generator/VERSION b/internal/services/catalogreadservice/test/load_tests/.openapi-generator/VERSION similarity index 100% rename from internal/services/catalog_read_service/test/load_tests/.openapi-generator/VERSION rename to internal/services/catalogreadservice/test/load_tests/.openapi-generator/VERSION diff --git a/internal/services/catalog_read_service/test/load_tests/README.md b/internal/services/catalogreadservice/test/load_tests/README.md similarity index 100% rename from internal/services/catalog_read_service/test/load_tests/README.md rename to internal/services/catalogreadservice/test/load_tests/README.md diff --git a/internal/services/catalog_read_service/test/load_tests/script.js b/internal/services/catalogreadservice/test/load_tests/script.js similarity index 100% rename from internal/services/catalog_read_service/test/load_tests/script.js rename to internal/services/catalogreadservice/test/load_tests/script.js diff --git a/internal/services/catalog_write_service/.air.toml b/internal/services/catalogwriteservice/.air.toml similarity index 100% rename from internal/services/catalog_write_service/.air.toml rename to internal/services/catalogwriteservice/.air.toml diff --git a/internal/services/catalog_write_service/.dockerignore b/internal/services/catalogwriteservice/.dockerignore similarity index 100% rename from internal/services/catalog_write_service/.dockerignore rename to internal/services/catalogwriteservice/.dockerignore diff --git a/internal/services/catalogwriteservice/.env b/internal/services/catalogwriteservice/.env new file mode 100644 index 00000000..2e06f00a --- /dev/null +++ b/internal/services/catalogwriteservice/.env @@ -0,0 +1 @@ +PROJECT_NAME=catalogwriteservice diff --git a/internal/services/catalog_write_service/.gitignore b/internal/services/catalogwriteservice/.gitignore similarity index 100% rename from internal/services/catalog_write_service/.gitignore rename to internal/services/catalogwriteservice/.gitignore diff --git a/internal/services/catalog_write_service/.golangci.yml b/internal/services/catalogwriteservice/.golangci.yml similarity index 100% rename from internal/services/catalog_write_service/.golangci.yml rename to internal/services/catalogwriteservice/.golangci.yml diff --git a/internal/services/catalog_write_service/Dockerfile b/internal/services/catalogwriteservice/Dockerfile similarity index 100% rename from internal/services/catalog_write_service/Dockerfile rename to internal/services/catalogwriteservice/Dockerfile diff --git a/internal/services/catalog_write_service/Makefile b/internal/services/catalogwriteservice/Makefile similarity index 100% rename from internal/services/catalog_write_service/Makefile rename to internal/services/catalogwriteservice/Makefile diff --git a/internal/services/catalog_write_service/arch-go.yml b/internal/services/catalogwriteservice/arch-go.yml similarity index 100% rename from internal/services/catalog_write_service/arch-go.yml rename to internal/services/catalogwriteservice/arch-go.yml diff --git a/internal/services/catalog_write_service/atlas.hcl b/internal/services/catalogwriteservice/atlas.hcl similarity index 100% rename from internal/services/catalog_write_service/atlas.hcl rename to internal/services/catalogwriteservice/atlas.hcl diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/events/domain_events/.gitkeep b/internal/services/catalogwriteservice/client.http similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/creating_product/v1/events/domain_events/.gitkeep rename to internal/services/catalogwriteservice/client.http diff --git a/internal/services/catalog_write_service/cmd/app/main.go b/internal/services/catalogwriteservice/cmd/app/main.go similarity index 100% rename from internal/services/catalog_write_service/cmd/app/main.go rename to internal/services/catalogwriteservice/cmd/app/main.go diff --git a/internal/services/catalog_write_service/cmd/migration/main.go b/internal/services/catalogwriteservice/cmd/migration/main.go similarity index 75% rename from internal/services/catalog_write_service/cmd/migration/main.go rename to internal/services/catalogwriteservice/cmd/migration/main.go index 216fd3f4..8f7d3a88 100644 --- a/internal/services/catalog_write_service/cmd/migration/main.go +++ b/internal/services/catalogwriteservice/cmd/migration/main.go @@ -5,15 +5,15 @@ import ( "os" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/default_logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/goose" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" appconfig "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" "github.com/spf13/cobra" @@ -39,7 +39,7 @@ var ( if len(args) == 0 { cmd.SetArgs([]string{"up"}) if err := cmd.Execute(); err != nil { - defaultLogger.Logger.Error(err) + defaultLogger.GetLogger().Error(err) os.Exit(1) } } @@ -66,11 +66,11 @@ var ( func executeMigration(cmd *cobra.Command, commandType migration.CommandType) { version, err := cmd.Flags().GetUint("version") if err != nil { - defaultLogger.Logger.Fatal(err) + defaultLogger.GetLogger().Fatal(err) } app := fx.New( - config.ModuleFunc(environemnt.Development), + config.ModuleFunc(environment.Development), zap.Module, fxlog.FxLogger, gormPostgres.Module, @@ -79,37 +79,37 @@ func executeMigration(cmd *cobra.Command, commandType migration.CommandType) { //gomigrate.Module, // use go-migrate library for migration goose.Module, - fx.Invoke(func(migrationRunner contracts.PostgresMigrationRunner, logger logger.Logger) { - logger.Info("Migration process started...") - switch commandType { - case migration.Up: - err = migrationRunner.Up(context.Background(), version) - case migration.Down: - err = migrationRunner.Down(context.Background(), version) - } - if err != nil { - logger.Fatalf("migration failed, err: %s", err) - } - logger.Info("Migration completed...") - }), + fx.Invoke( + func(migrationRunner contracts.PostgresMigrationRunner, logger logger.Logger) { + logger.Info("Migration process started...") + switch commandType { + case migration.Up: + err = migrationRunner.Up(context.Background(), version) + case migration.Down: + err = migrationRunner.Down(context.Background(), version) + } + if err != nil { + logger.Fatalf("migration failed, err: %s", err) + } + logger.Info("Migration completed...") + }, + ), ) err = app.Start(context.Background()) if err != nil { - defaultLogger.Logger.Fatal(err) + defaultLogger.GetLogger().Fatal(err) } err = app.Stop(context.Background()) if err != nil { - defaultLogger.Logger.Fatal(err) + defaultLogger.GetLogger().Fatal(err) } } func main() { - defaultLogger.SetupDefaultLogger() - if err := rootCmd.Execute(); err != nil { - defaultLogger.Logger.Error(err) + defaultLogger.GetLogger().Error(err) os.Exit(1) } } diff --git a/internal/services/catalog_write_service/config/app_options.go b/internal/services/catalogwriteservice/config/app_options.go similarity index 72% rename from internal/services/catalog_write_service/config/app_options.go rename to internal/services/catalogwriteservice/config/app_options.go index ff1470f7..137534c0 100644 --- a/internal/services/catalog_write_service/config/app_options.go +++ b/internal/services/catalogwriteservice/config/app_options.go @@ -4,19 +4,19 @@ import ( "strings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" "github.com/iancoleman/strcase" ) type AppOptions struct { DeliveryType string `mapstructure:"deliveryType" env:"DeliveryType"` - ServiceName string `mapstructure:"serviceName" env:"ServiceName"` + ServiceName string `mapstructure:"serviceName" env:"serviceName"` } -func NewAppOptions(environment environemnt.Environment) (*AppOptions, error) { - optionName := strcase.ToLowerCamel(typeMapper.GetTypeNameByT[AppOptions]()) +func NewAppOptions(environment environment.Environment) (*AppOptions, error) { + optionName := strcase.ToLowerCamel(typeMapper.GetGenericTypeNameByT[AppOptions]()) cfg, err := config.BindConfigKey[*AppOptions](optionName, environment) if err != nil { return nil, err diff --git a/internal/services/catalogwriteservice/config/config.development.json b/internal/services/catalogwriteservice/config/config.development.json new file mode 100644 index 00000000..bcdc2233 --- /dev/null +++ b/internal/services/catalogwriteservice/config/config.development.json @@ -0,0 +1,113 @@ +{ + "appOptions": { + "serviceName": "catalogwriteservice", + "deliveryType": "http" + }, + "grpcOptions": { + "name": "catalogwriteservice", + "port": ":6003", + "host": "localhost", + "development": true + }, + "echoHttpOptions": { + "name": "catalogwriteservice", + "port": ":7000", + "development": true, + "timeout": 30, + "basePath": "/api/v1", + "host": "http://localhost", + "productsPath": "products", + "debugHeaders": true, + "httpClientDebug": true, + "debugErrorsResponse": true, + "ignoreLogUrls": [ + "metrics" + ] + }, + "logOptions": { + "level": "debug", + "logType": 0, + "callerEnabled": false + }, + "gormOptions": { + "host": "localhost", + "port": 5432, + "user": "postgres", + "password": "postgres", + "dbName": "catalogs_service", + "sslMode": false + }, + "rabbitmqOptions": { + "autoStart": true, + "reconnecting": true, + "rabbitmqHostOptions": { + "userName": "guest", + "password": "guest", + "hostName": "localhost", + "port": 5672, + "httpPort": 15672 + } + }, + "tracingOptions": { + "enable": true, + "serviceName": "catalogs-write-service", + "instrumentationName": "io.opentelemetry.traces.catalogs-write-service", + "id": 1, + "useStdout": false, + "alwaysOnSampler": true, + "jaegerExporterOptions": { + "otlpEndpoint": "localhost:4320", + "enabled": true + }, + "tempoExporterOptions": { + "otlpEndpoint": "localhost:4322", + "enabled": true + }, + "zipkinExporterOptions": { + "url": "http://localhost:9411/api/v2/spans" + }, + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": { + "uptrace-dsn": "https://@uptrace.dev/" + } + }, + { + "name": "elastic-apm", + "enabled": false, + "otlpEndpoint": "host.docker.internal:4317", + "otlpHeaders": { + "Authorization": "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + } + }, + { + "name": "signoz", + "enabled": false, + "otlpEndpoint": "localhost:4317" + }, + { + "name": "grafana-tempo", + "enabled": false, + "otlpEndpoint": "localhost:4322" + } + ] + }, + "metricsOptions": { + "metricsRoutePath": "metrics", + "serviceName": "catalogs-write-service", + "instrumentationName": "io.opentelemetry.metrics.catalogs-write-service" + }, + "migrationOptions": { + "host": "localhost", + "port": 5432, + "user": "postgres", + "password": "postgres", + "dbName": "catalogs_service", + "sslMode": false, + "migrationsDir": "db/migrations/goose-migrate", + "skipMigration": false + } +} diff --git a/internal/services/catalog_write_service/config/config.test.json b/internal/services/catalogwriteservice/config/config.test.json similarity index 69% rename from internal/services/catalog_write_service/config/config.test.json rename to internal/services/catalogwriteservice/config/config.test.json index 58a80583..8de7c47b 100644 --- a/internal/services/catalog_write_service/config/config.test.json +++ b/internal/services/catalogwriteservice/config/config.test.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "catalogs_write_service", + "serviceName": "catalogwriteservice", "deliveryType": "http" }, "grpcOptions": { - "name": "catalogs_write_service", + "name": "catalogwriteservice", "port": ":3301", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "catalogs_write_service", + "name": "catalogwriteservice", "port": ":6001", "development": true, "timeout": 30, @@ -48,27 +48,33 @@ "httpPort": 15672 } }, - "openTelemetryOptions": { + "tracingOptions": { "enable": true, "serviceName": "catalogs-write-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice", + "instrumentationName": "io.opentelemetry.traces.catalogs-write-service", "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4320", + "enabled": true }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" }, - "otelMetricsOptions": { - "port": ":3001", - "metricsRoutePath": "/metrics", - "name": "catalogs-write-service" - } + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": {} + } + ] }, - "eventStoreDbOptions": { - "connectionString": "esdb://localhost:2113?tls=false" + "metricsOptions": { + "metricsRoutePath": "metrics", + "serviceName": "catalogs-write-service", + "instrumentationName": "io.opentelemetry.metrics.catalogs-write-service" }, "migrationOptions": { "host": "localhost", diff --git a/internal/services/catalog_write_service/config/config_fx.go b/internal/services/catalogwriteservice/config/config_fx.go similarity index 100% rename from internal/services/catalog_write_service/config/config_fx.go rename to internal/services/catalogwriteservice/config/config_fx.go diff --git a/internal/services/catalog_write_service/db/fixtures/products/products.yaml b/internal/services/catalogwriteservice/db/fixtures/products/products.yaml similarity index 100% rename from internal/services/catalog_write_service/db/fixtures/products/products.yaml rename to internal/services/catalogwriteservice/db/fixtures/products/products.yaml diff --git a/internal/services/catalog_write_service/db/migrations/atlas/20230919170700.sql b/internal/services/catalogwriteservice/db/migrations/atlas/20230919170700.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/20230919170700.sql rename to internal/services/catalogwriteservice/db/migrations/atlas/20230919170700.sql diff --git a/internal/services/catalog_write_service/db/migrations/atlas/atlas.sum b/internal/services/catalogwriteservice/db/migrations/atlas/atlas.sum similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/atlas.sum rename to internal/services/catalogwriteservice/db/migrations/atlas/atlas.sum diff --git a/internal/services/catalog_write_service/db/migrations/atlas/goose/00001_enable_uuid_extension.sql b/internal/services/catalogwriteservice/db/migrations/atlas/goose/00001_enable_uuid_extension.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/goose/00001_enable_uuid_extension.sql rename to internal/services/catalogwriteservice/db/migrations/atlas/goose/00001_enable_uuid_extension.sql diff --git a/internal/services/catalog_write_service/db/migrations/atlas/goose/00002_create_products_table.sql b/internal/services/catalogwriteservice/db/migrations/atlas/goose/00002_create_products_table.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/goose/00002_create_products_table.sql rename to internal/services/catalogwriteservice/db/migrations/atlas/goose/00002_create_products_table.sql diff --git a/internal/services/catalog_write_service/db/migrations/atlas/goose/atlas.sum b/internal/services/catalogwriteservice/db/migrations/atlas/goose/atlas.sum similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/goose/atlas.sum rename to internal/services/catalogwriteservice/db/migrations/atlas/goose/atlas.sum diff --git a/internal/services/catalog_write_service/db/migrations/atlas/readme.md b/internal/services/catalogwriteservice/db/migrations/atlas/readme.md similarity index 100% rename from internal/services/catalog_write_service/db/migrations/atlas/readme.md rename to internal/services/catalogwriteservice/db/migrations/atlas/readme.md diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/000001_enable_uuid_extension.down.sql b/internal/services/catalogwriteservice/db/migrations/go-migrate/000001_enable_uuid_extension.down.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/000001_enable_uuid_extension.down.sql rename to internal/services/catalogwriteservice/db/migrations/go-migrate/000001_enable_uuid_extension.down.sql diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/000001_enable_uuid_extension.up.sql b/internal/services/catalogwriteservice/db/migrations/go-migrate/000001_enable_uuid_extension.up.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/000001_enable_uuid_extension.up.sql rename to internal/services/catalogwriteservice/db/migrations/go-migrate/000001_enable_uuid_extension.up.sql diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/000002_create_products_table.down.sql b/internal/services/catalogwriteservice/db/migrations/go-migrate/000002_create_products_table.down.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/000002_create_products_table.down.sql rename to internal/services/catalogwriteservice/db/migrations/go-migrate/000002_create_products_table.down.sql diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/000002_create_products_table.up.sql b/internal/services/catalogwriteservice/db/migrations/go-migrate/000002_create_products_table.up.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/000002_create_products_table.up.sql rename to internal/services/catalogwriteservice/db/migrations/go-migrate/000002_create_products_table.up.sql diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/atlas.sum b/internal/services/catalogwriteservice/db/migrations/go-migrate/atlas.sum similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/atlas.sum rename to internal/services/catalogwriteservice/db/migrations/go-migrate/atlas.sum diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/readme.md b/internal/services/catalogwriteservice/db/migrations/go-migrate/readme.md similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/readme.md rename to internal/services/catalogwriteservice/db/migrations/go-migrate/readme.md diff --git a/internal/services/catalog_write_service/db/migrations/go-migrate/schema.sql b/internal/services/catalogwriteservice/db/migrations/go-migrate/schema.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/go-migrate/schema.sql rename to internal/services/catalogwriteservice/db/migrations/go-migrate/schema.sql diff --git a/internal/services/catalog_write_service/db/migrations/goose-migrate/00001_enable_uuid_extension.sql b/internal/services/catalogwriteservice/db/migrations/goose-migrate/00001_enable_uuid_extension.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/goose-migrate/00001_enable_uuid_extension.sql rename to internal/services/catalogwriteservice/db/migrations/goose-migrate/00001_enable_uuid_extension.sql diff --git a/internal/services/catalog_write_service/db/migrations/goose-migrate/00002_create_products_table.sql b/internal/services/catalogwriteservice/db/migrations/goose-migrate/00002_create_products_table.sql similarity index 100% rename from internal/services/catalog_write_service/db/migrations/goose-migrate/00002_create_products_table.sql rename to internal/services/catalogwriteservice/db/migrations/goose-migrate/00002_create_products_table.sql diff --git a/internal/services/catalog_write_service/db/migrations/goose-migrate/readme.md b/internal/services/catalogwriteservice/db/migrations/goose-migrate/readme.md similarity index 100% rename from internal/services/catalog_write_service/db/migrations/goose-migrate/readme.md rename to internal/services/catalogwriteservice/db/migrations/goose-migrate/readme.md diff --git a/internal/services/catalog_write_service/docs/docs.go b/internal/services/catalogwriteservice/docs/docs.go similarity index 100% rename from internal/services/catalog_write_service/docs/docs.go rename to internal/services/catalogwriteservice/docs/docs.go diff --git a/internal/services/catalog_write_service/docs/swagger.json b/internal/services/catalogwriteservice/docs/swagger.json similarity index 100% rename from internal/services/catalog_write_service/docs/swagger.json rename to internal/services/catalogwriteservice/docs/swagger.json diff --git a/internal/services/catalog_write_service/docs/swagger.yaml b/internal/services/catalogwriteservice/docs/swagger.yaml similarity index 100% rename from internal/services/catalog_write_service/docs/swagger.yaml rename to internal/services/catalogwriteservice/docs/swagger.yaml diff --git a/internal/services/catalog_write_service/go.mod b/internal/services/catalogwriteservice/go.mod similarity index 71% rename from internal/services/catalog_write_service/go.mod rename to internal/services/catalogwriteservice/go.mod index 4be37c64..1d9dfd62 100644 --- a/internal/services/catalog_write_service/go.mod +++ b/internal/services/catalogwriteservice/go.mod @@ -7,8 +7,9 @@ replace github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg => ../../ require ( emperror.dev/errors v0.8.1 - github.com/brianvoe/gofakeit/v6 v6.23.2 + github.com/brianvoe/gofakeit/v6 v6.25.0 github.com/gavv/httpexpect/v2 v2.3.1 + github.com/glebarez/sqlite v1.10.0 github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-playground/validator v9.31.0+incompatible github.com/goccy/go-json v0.10.2 @@ -16,23 +17,23 @@ require ( github.com/labstack/echo/v4 v4.11.1 github.com/lib/pq v1.10.9 github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg v0.0.0-20230831075934-be8df319f588 - github.com/mehdihadeli/go-mediatr v1.1.10 + github.com/mehdihadeli/go-mediatr v1.3.0 github.com/michaelklishin/rabbit-hole v1.5.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/pterm/pterm v0.12.69 github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 github.com/swaggo/echo-swagger v1.4.1 github.com/swaggo/swag v1.16.2 - go.opentelemetry.io/otel v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 - go.opentelemetry.io/otel/trace v1.17.0 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/metric v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/fx v1.20.0 - google.golang.org/grpc v1.57.0 + google.golang.org/grpc v1.58.2 gopkg.in/khaiql/dbcleaner.v2 v2.3.0 - gorm.io/gorm v1.25.4 + gorm.io/gorm v1.25.5 ) require ( @@ -42,11 +43,11 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/ClickHouse/ch-go v0.58.2 // indirect - github.com/ClickHouse/clickhouse-go/v2 v2.13.4 // indirect - github.com/EventStore/EventStore-Client-Go v1.0.2 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.14.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -57,22 +58,25 @@ require ( github.com/caarlos0/env/v8 v8.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/containerd/console v1.0.3 // indirect - github.com/containerd/containerd v1.7.5 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/doug-martin/goqu/v9 v9.18.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fatih/structs v1.0.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.6.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.20.9 // indirect @@ -81,17 +85,16 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-testfixtures/testfixtures/v3 v3.9.0 // indirect github.com/goccy/go-reflect v1.2.0 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-migrate/migrate/v4 v4.16.2 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.3.1 // indirect github.com/gookit/color v1.5.4 // indirect github.com/gorilla/websocket v1.4.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/imkira/go-interpol v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -110,12 +113,13 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kamva/mgm/v3 v3.5.0 // indirect github.com/khaiql/dbcleaner v2.3.0+incompatible // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -130,7 +134,7 @@ require ( github.com/nolleh/caption_json_formatter v0.2.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.9 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/paulmach/orb v0.10.0 // indirect @@ -138,30 +142,39 @@ require ( github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/pressly/goose/v3 v3.15.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/rabbitmq/amqp091-go v1.8.1 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect - github.com/redis/go-redis/v9 v9.0.5 // indirect + github.com/redis/go-redis/v9 v9.2.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/samber/lo v1.38.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/viper v1.16.0 // indirect - github.com/streadway/amqp v1.1.0 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect - github.com/testcontainers/testcontainers-go v0.23.0 // indirect + github.com/testcontainers/testcontainers-go v0.25.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - github.com/uptrace/bun v1.1.14 // indirect - github.com/uptrace/bun/driver/pgdriver v1.1.14 // indirect + github.com/ulule/limiter/v3 v3.11.2 // indirect + github.com/uptrace/bun v1.1.16 // indirect + github.com/uptrace/bun/driver/pgdriver v1.1.16 // indirect + github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.27.0 // indirect + github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -174,19 +187,26 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yudai/gojsondiff v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 // indirect - go.opentelemetry.io/contrib/propagators/ot v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.40.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.17.0 // indirect - go.opentelemetry.io/otel/sdk v1.17.0 // indirect - go.opentelemetry.io/otel/sdk/metric v0.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/host v0.45.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.19.0 // indirect + go.opentelemetry.io/otel/sdk v1.19.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.25.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect @@ -196,10 +216,16 @@ require ( golang.org/x/term v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gorm.io/driver/postgres v1.5.2 // indirect + gorm.io/plugin/opentelemetry v0.1.4 // indirect mellium.im/sasl v0.3.1 // indirect + modernc.org/libc v1.24.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.6.0 // indirect + modernc.org/sqlite v1.25.0 // indirect moul.io/http2curl v1.0.1-0.20190925090545-5cd742060b0e // indirect ) @@ -213,10 +239,10 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/internal/services/catalog_write_service/go.sum b/internal/services/catalogwriteservice/go.sum similarity index 87% rename from internal/services/catalog_write_service/go.sum rename to internal/services/catalogwriteservice/go.sum index b75314c5..1f0d24e9 100644 --- a/internal/services/catalog_write_service/go.sum +++ b/internal/services/catalogwriteservice/go.sum @@ -6,7 +6,6 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs= atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU= -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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= @@ -33,8 +32,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -56,19 +55,16 @@ emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0= emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= -github.com/ClickHouse/clickhouse-go/v2 v2.13.4 h1:NcvYN9ONZn3vlPMfQVUBSG5LKz+1y2wk4vaaz5QZXIg= -github.com/ClickHouse/clickhouse-go/v2 v2.13.4/go.mod h1:u1AUh8E0XqN1sU1EDzbiGLTI4KWOd+lOHimNSsdyJec= +github.com/ClickHouse/clickhouse-go/v2 v2.14.1 h1:5C2hhmZEGUVdy8CPpY3iPpfBv2kRbx5iOcflU49Rzws= +github.com/ClickHouse/clickhouse-go/v2 v2.14.1/go.mod h1:PHqbMvJTQ0EI4a1vJhmbmL/Ajr+Cin2O+WJjnYctJvg= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/EventStore/EventStore-Client-Go v1.0.2 h1:onM2TIInLhWUJwUQ/5a/8blNrrbhwrtm7Tpmg13ohiw= -github.com/EventStore/EventStore-Client-Go v1.0.2/go.mod h1:NOqSOtNxqGizr1Qnf7joGGLK6OkeoLV/QEI893A43H0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= @@ -84,13 +80,10 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w= github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZDE= @@ -112,25 +105,24 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8= -github.com/brianvoe/gofakeit/v6 v6.23.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/brianvoe/gofakeit/v6 v6.25.0 h1:ZpFjktOpLZUeF8q223o0rUuXtA+m5qW5srjvVi+JkXk= +github.com/brianvoe/gofakeit/v6 v6.25.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= -github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 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/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -139,27 +131,19 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.5 h1:i9T9XpAWMe11BHMN7pu1BZqOGjXaKTPyz2v+KYOZgkY= -github.com/containerd/containerd v1.7.5/go.mod h1:ieJNCSzASw2shSGYLHx8NAE7WsZ/gEigo5fQ78W5Zvw= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 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= @@ -168,20 +152,16 @@ github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= -github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -190,15 +170,14 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m 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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fasthttp/websocket v1.4.3-rc.6 h1:omHqsl8j+KXpmzRjF8bmzOSYJ8GnS0E3efi1wYT+niY= github.com/fasthttp/websocket v1.4.3-rc.6/go.mod h1:43W9OM2T8FeXpCWMsBd9Cb7nE2CACNqNvCqQCoty/Lc= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -209,6 +188,10 @@ github.com/gavv/httpexpect/v2 v2.3.1 h1:sGLlKMn8AuHS9ztK9Sb7AJ7OxIL8v2PcLdyxfKt1 github.com/gavv/httpexpect/v2 v2.3.1/go.mod h1:yOE8m/aqFYQDNrgprMeXgq4YynfN9h1NgcE1+1suV64= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= +github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= @@ -223,6 +206,9 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -259,8 +245,6 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-reflect v1.2.0 h1:O0T8rZCuNmGXewnATuKYnkL0xm6o8UNOJZd/gOkb9ms= github.com/goccy/go-reflect v1.2.0/go.mod h1:n0oYZn8VcV2CkWTxi8B9QjkCoq6GTtCEdfmR66YhFtE= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -276,6 +260,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 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/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -286,8 +272,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -321,6 +305,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -339,11 +324,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= @@ -355,12 +338,12 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40= -github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/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/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -376,11 +359,8 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imkira/go-interpol v1.0.0 h1:HrmLyvOLJyjR0YofMw8QGdCIuYOs4TJUBDNU5sJC09E= github.com/imkira/go-interpol v1.0.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -456,8 +436,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -485,17 +465,18 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -527,18 +508,16 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= -github.com/mehdihadeli/go-mediatr v1.1.10 h1:NAzg4065c90lgYeb+Vzbd2WKH0tUFpxzL0mpx6hkU/A= -github.com/mehdihadeli/go-mediatr v1.1.10/go.mod h1:lwgZl7qVL/RKomObBblhG3uEte/r4nJDV95Vd+nGrMw= +github.com/mehdihadeli/go-mediatr v1.3.0 h1:hrb5Scp/nsiR3Y62mjZ0Tc5UX/dRJl4nDFkINBEIESA= +github.com/mehdihadeli/go-mediatr v1.3.0/go.mod h1:lsG+hyH+pEOhmZiZl0KPO72BcZiEReF03CBk4GVJB0k= github.com/michaelklishin/rabbit-hole v1.5.0 h1:Bex27BiFDsijCM9D0ezSHqyy0kehpYHuNKaPqq/a4RM= github.com/michaelklishin/rabbit-hole v1.5.0/go.mod h1:vvI1uOitYZi0O5HEGXhaWC1XT80Gy+HvFheJ+5Krlhk= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -546,7 +525,6 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8 github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nolleh/caption_json_formatter v0.2.2 h1:EKsOr/fCllNQF2ZoajfbSDlV73BNV1bDu1aTTSRrlN0= github.com/nolleh/caption_json_formatter v0.2.2/go.mod h1:5FYofZA8NAej/eFxa12FvyQKosU1LfyKizZPlY0JojU= @@ -558,31 +536,22 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= -github.com/ory/dockertest/v3 v3.6.3/go.mod h1:EFLcVUOl8qCwp9NyDAcCDtq/QviLtYswW/VbWzUnTNE= -github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= -github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= @@ -590,24 +559,26 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.15.0 h1:6tY5aDqFknY6VZkorFGgZtWygodZQxfmmEF4rqyJW9k= github.com/pressly/goose/v3 v3.15.0/go.mod h1:LlIo3zGccjb/YUgG+Svdb9Er14vefRdlDI7URCDrwYo= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= @@ -623,8 +594,9 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -636,7 +608,6 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= @@ -644,34 +615,33 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873 h1:N3Af8f13ooDKcIhsmFT7Z05CStZWu4C7Md0uDEy4q6o= github.com/savsgio/gotils v0.0.0-20210617111740-97865ed5a873/go.mod h1:dmPawKuiAeG/aFYVs2i+Dyosoo7FNcm+Pi8iK6ZUrX8= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= +github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= @@ -705,34 +675,42 @@ github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= -github.com/testcontainers/testcontainers-go/modules/postgres v0.23.0 h1:OEGUC1YTN1RyS4xqsHmlyYkBWm9lMJcswoV4JSHJQOM= -github.com/testcontainers/testcontainers-go/modules/postgres v0.23.0/go.mod h1:YnqIhPwhjqVbJBuvSRJS6pa9Cy1PDRJcrM6T63Uw2ms= +github.com/testcontainers/testcontainers-go v0.25.0 h1:erH6cQjsaJrH+rJDU9qIf89KFdhK0Bft0aEZHlYC3Vs= +github.com/testcontainers/testcontainers-go v0.25.0/go.mod h1:4sC9SiJyzD1XFi59q8umTQYWxnkweEc5OjVtTUlJzqQ= +github.com/testcontainers/testcontainers-go/modules/postgres v0.25.0 h1:8WNK1Edo9ohRYPrDCXWdoVY2cbg/oFh9y5uWZGSBESo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.25.0/go.mod h1:XpwOhyUXheL31hz73L8be8maW1rQq8H48x5qZeHtYr0= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM= -github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8= -github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA= -github.com/uptrace/bun/driver/pgdriver v1.1.14/go.mod h1:D4FjWV9arDYct6sjMJhFoyU71SpllZRHXFRRP2Kd0Kw= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= +github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= +github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A= +github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8= +github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7QiNOCPLqPFJjK4= +github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 h1:m5eNyOhch/7tyK6aN6eRRpNoD1vM8PNh64dA05X22Js= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3/go.mod h1:APPUXm9BbpH7NFkfpbw04raZSitzl19/3NOCu0rbI4E= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 h1:LyGS9cIZV0YVhE81zwfMhIE2l2flcj3wn5IoK4VkbWA= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3/go.mod h1:RvCYhPchLhvQ9l9C9goblbgO7BaKt597kBMf5mgKyo0= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 h1:2na5W81H38Z4qXCQCuzlcdSMiTWgPJ6XeZIArq6VIJE= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3/go.mod h1:9IVEh9mPv3NwFf99dVLX15FqVgdpZJ8RMDo/Cr0vK74= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.27.0 h1:gDefRDL9aqSiwXV6aRW8aSBPs82y4KizSzHrBLf4NDI= github.com/valyala/fasthttp v1.27.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608= +github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= @@ -769,8 +747,9 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= @@ -782,32 +761,42 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 h1:pcWR7mkuO5XMK3f0KeGpr70OTR9/ikPk0D1hHEd5dp4= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0/go.mod h1:azTPpa9PvRDSNU/lQbe2CRDQoTcau5moOjS4EzhpyAY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0 h1:hhSlPVi9AQwOmbMmptPNLfRZOLgENdRM2kb7z9LFe1A= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0/go.mod h1:qtt+pEu23D7UVP+j33G4i7LopmVu8/6/IwGu3hEm100= -go.opentelemetry.io/contrib/propagators/ot v1.18.0 h1:VmzxO7BjUU6oo0ChcKuGdKaSR0vchPxwahHZl64zVUM= -go.opentelemetry.io/contrib/propagators/ot v1.18.0/go.mod h1:5VwcOJ7OjS0uPxaxuwKHwJtkt+EAC+cgjXleXMe51z4= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ERB6R034zmsHjBRLyxrAR8= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0 h1:oi5+xMN3pflqWSd4EX6FiO+Cn3KbFBBzeQmD5LMIf0c= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0/go.mod h1:pNir+S6/f0HFGfbXhobXLTFu60KtAzw8aGSUpt9A6VU= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= -go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 h1:bldpPC7XAv7f7LKTwNfRkNdzRhjtXaWybZFFa16dAb8= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0/go.mod h1:xhkNpJG3D+kmuaciNTco7cdK27Fb77J9Iqcq5CMe4Y8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0 h1:1uzNKJDqZ6y6F5J6aKWgJjRREpKiGhBvKHlWon/bqB4= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0/go.mod h1:vlqPvzDsmB4+jlERxBRXsdLCD6Q0LoBzxHqNXp3qvG4= +go.opentelemetry.io/contrib/propagators/ot v1.20.0 h1:duH7mgL6VGQH7e7QEAVOFkCQXWpCb4PjTtrhdrYrJRQ= +go.opentelemetry.io/contrib/propagators/ot v1.20.0/go.mod h1:gijQzxOq0JLj9lyZhTvqjDddGV/zaNagpPIn+2r8CEI= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0 h1:EGY0h5mGliP9o/nIkVuLI0vRiQqmsYOcbwCuotksO1o= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0/go.mod h1:JQgTGJP11yi3o4GHzIWYodhPisxANdqxF1eHwDSnJrI= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -833,9 +822,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -892,7 +880,6 @@ 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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -911,7 +898,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -932,7 +918,6 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -948,8 +933,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -977,15 +962,14 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w 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= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1003,12 +987,11 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1017,8 +1000,6 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1033,8 +1014,11 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1077,7 +1061,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1122,7 +1105,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= @@ -1189,7 +1171,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1198,8 +1179,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1216,8 +1201,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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= @@ -1233,7 +1218,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1242,7 +1226,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= @@ -1255,7 +1238,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1267,9 +1249,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= -gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= -gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/opentelemetry v0.1.4 h1:7p0ocWELjSSRI7NCKPW2mVe6h43YPini99sNJcbsTuc= +gorm.io/plugin/opentelemetry v0.1.4/go.mod h1:tndJHOdvPT0pyGhOb8E2209eXJCUxhC5UpKw7bGVWeI= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/services/catalogwriteservice/internal/products/configurations/endpoints/endpoints.go b/internal/services/catalogwriteservice/internal/products/configurations/endpoints/endpoints.go new file mode 100644 index 00000000..d4e283a4 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/configurations/endpoints/endpoints.go @@ -0,0 +1,13 @@ +package endpoints + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" +) + +func RegisterEndpoints(endpoints []route.Endpoint) error { + for _, endpoint := range endpoints { + endpoint.MapEndpoint() + } + + return nil +} diff --git a/internal/services/catalog_write_service/internal/products/configurations/mappings/mapping_profile.go b/internal/services/catalogwriteservice/internal/products/configurations/mappings/mapping_profile.go similarity index 59% rename from internal/services/catalog_write_service/internal/products/configurations/mappings/mapping_profile.go rename to internal/services/catalogwriteservice/internal/products/configurations/mappings/mapping_profile.go index bf93fb6a..41ed2dc7 100644 --- a/internal/services/catalog_write_service/internal/products/configurations/mappings/mapping_profile.go +++ b/internal/services/catalogwriteservice/internal/products/configurations/mappings/mapping_profile.go @@ -2,7 +2,8 @@ package mappings import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" productsService "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" @@ -20,13 +21,23 @@ func ConfigureProductsMappings() error { return err } + err = mapper.CreateMap[*datamodel.ProductDataModel, *models.Product]() + if err != nil { + return err + } + + err = mapper.CreateMap[*models.Product, *datamodel.ProductDataModel]() + if err != nil { + return err + } + err = mapper.CreateCustomMap[*dtoV1.ProductDto, *productsService.Product]( func(product *dtoV1.ProductDto) *productsService.Product { if product == nil { return nil } return &productsService.Product{ - ProductId: product.ProductId.String(), + ProductId: product.Id.String(), Name: product.Name, Description: product.Description, Price: product.Price, @@ -39,16 +50,18 @@ func ConfigureProductsMappings() error { return err } - err = mapper.CreateCustomMap(func(product *models.Product) *productsService.Product { - return &productsService.Product{ - ProductId: product.ProductId.String(), - Name: product.Name, - Description: product.Description, - Price: product.Price, - CreatedAt: timestamppb.New(product.CreatedAt), - UpdatedAt: timestamppb.New(product.UpdatedAt), - } - }) + err = mapper.CreateCustomMap( + func(product *models.Product) *productsService.Product { + return &productsService.Product{ + ProductId: product.Id.String(), + Name: product.Name, + Description: product.Description, + Price: product.Price, + CreatedAt: timestamppb.New(product.CreatedAt), + UpdatedAt: timestamppb.New(product.UpdatedAt), + } + }, + ) return nil } diff --git a/internal/services/catalogwriteservice/internal/products/configurations/mediator/mediator.go b/internal/services/catalogwriteservice/internal/products/configurations/mediator/mediator.go new file mode 100644 index 00000000..32a1acb2 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/configurations/mediator/mediator.go @@ -0,0 +1,14 @@ +package mediator + +import "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + +func RegisterMediatorHandlers(handlers []cqrs.HandlerRegisterer) error { + for _, handler := range handlers { + err := handler.RegisterHandler() + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/services/catalogwriteservice/internal/products/configurations/products_module_configurator.go b/internal/services/catalogwriteservice/internal/products/configurations/products_module_configurator.go new file mode 100644 index 00000000..396d8af8 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/configurations/products_module_configurator.go @@ -0,0 +1,66 @@ +package configurations + +import ( + fxcontracts "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" + grpcServer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/endpoints" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mappings" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mediator" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc" + productsservice "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" + + googleGrpc "google.golang.org/grpc" +) + +type ProductsModuleConfigurator struct { + fxcontracts.Application +} + +func NewProductsModuleConfigurator( + fxapp fxcontracts.Application, +) *ProductsModuleConfigurator { + return &ProductsModuleConfigurator{ + Application: fxapp, + } +} + +func (c *ProductsModuleConfigurator) ConfigureProductsModule() error { + // config products mappings + err := mappings.ConfigureProductsMappings() + if err != nil { + return err + } + + // register products request handler on mediator + c.ResolveFuncWithParamTag( + mediator.RegisterMediatorHandlers, + `group:"product-handlers"`, + ) + + return nil +} + +func (c *ProductsModuleConfigurator) MapProductsEndpoints() error { + // config endpoints + c.ResolveFuncWithParamTag( + endpoints.RegisterEndpoints, + `group:"product-routes"`, + ) + + // config Products Grpc Endpoints + c.ResolveFunc( + func(catalogsGrpcServer grpcServer.GrpcServer, grpcService *grpc.ProductGrpcServiceServer) error { + catalogsGrpcServer.GrpcServiceBuilder(). + RegisterRoutes(func(server *googleGrpc.Server) { + productsservice.RegisterProductsServiceServer( + server, + grpcService, + ) + }) + + return nil + }, + ) + + return nil +} diff --git a/internal/services/catalog_write_service/internal/products/configurations/rabbitmq/rabbitmq_configurations.go b/internal/services/catalogwriteservice/internal/products/configurations/rabbitmq/rabbitmq_configurations.go similarity index 50% rename from internal/services/catalog_write_service/internal/products/configurations/rabbitmq/rabbitmq_configurations.go rename to internal/services/catalogwriteservice/internal/products/configurations/rabbitmq/rabbitmq_configurations.go index d5fa1ab3..8f982ba0 100644 --- a/internal/services/catalog_write_service/internal/products/configurations/rabbitmq/rabbitmq_configurations.go +++ b/internal/services/catalogwriteservice/internal/products/configurations/rabbitmq/rabbitmq_configurations.go @@ -3,12 +3,15 @@ package rabbitmq import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" producerConfigurations "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/producer/configurations" - createProductIntegrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/events/integration_events" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents" ) -func ConfigProductsRabbitMQ(builder configurations.RabbitMQConfigurationBuilder) { +func ConfigProductsRabbitMQ( + builder configurations.RabbitMQConfigurationBuilder, +) { builder.AddProducer( - createProductIntegrationEvents.ProductCreatedV1{}, + integrationevents.ProductCreatedV1{}, func(builder producerConfigurations.RabbitMQProducerConfigurationBuilder) { - }) + }, + ) } diff --git a/internal/services/catalog_write_service/internal/products/contracts/data/product_repository.go b/internal/services/catalogwriteservice/internal/products/contracts/product_repository.go similarity index 97% rename from internal/services/catalog_write_service/internal/products/contracts/data/product_repository.go rename to internal/services/catalogwriteservice/internal/products/contracts/product_repository.go index a9c1b347..aff647d7 100644 --- a/internal/services/catalog_write_service/internal/products/contracts/data/product_repository.go +++ b/internal/services/catalogwriteservice/internal/products/contracts/product_repository.go @@ -1,4 +1,4 @@ -package data +package contracts import ( "context" diff --git a/internal/services/catalogwriteservice/internal/products/data/datamodels/product_data_model.go b/internal/services/catalogwriteservice/internal/products/data/datamodels/product_data_model.go new file mode 100644 index 00000000..8242d06e --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/data/datamodels/product_data_model.go @@ -0,0 +1,35 @@ +package datamodels + +import ( + "time" + + "github.com/goccy/go-json" + uuid "github.com/satori/go.uuid" + "gorm.io/gorm" +) + +// https://gorm.io/docs/conventions.html +// https://gorm.io/docs/models.html#gorm-Model + +// ProductDataModel data model +type ProductDataModel struct { + Id uuid.UUID `gorm:"primaryKey"` + Name string + Description string + Price float64 + CreatedAt time.Time `gorm:"default:current_timestamp"` + UpdatedAt time.Time + // for soft delete - https://gorm.io/docs/delete.html#Soft-Delete + gorm.DeletedAt +} + +// TableName overrides the table name used by ProductDataModel to `products` - https://gorm.io/docs/conventions.html#TableName +func (p *ProductDataModel) TableName() string { + return "products" +} + +func (p *ProductDataModel) String() string { + j, _ := json.Marshal(p) + + return string(j) +} diff --git a/internal/services/catalog_write_service/internal/products/data/repositories/pg_product_repository.go b/internal/services/catalogwriteservice/internal/products/data/repositories/pg_product_repository.go similarity index 64% rename from internal/services/catalog_write_service/internal/products/data/repositories/pg_product_repository.go rename to internal/services/catalogwriteservice/internal/products/data/repositories/pg_product_repository.go index 2fa22c82..9d3294fd 100644 --- a/internal/services/catalog_write_service/internal/products/data/repositories/pg_product_repository.go +++ b/internal/services/catalogwriteservice/internal/products/data/repositories/pg_product_repository.go @@ -5,12 +5,13 @@ import ( "fmt" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres/repository" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + utils2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/repository" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" + data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" "emperror.dev/errors" @@ -46,20 +47,23 @@ func (p *postgresProductRepository) GetAllProducts( defer span.End() result, err := p.gormGenericRepository.GetAll(ctx, listQuery) + err = utils2.TraceStatusFromContext( + ctx, + errors.WrapIf( + err, + "error in the paginate", + ), + ) + if err != nil { - return nil, tracing.TraceErrFromContext( - ctx, - errors.WrapIf( - err, - "[postgresProductRepository_GetAllProducts.Paginate] error in the paginate", - ), - ) + return nil, err } p.log.Infow( - "[postgresProductRepository.GetAllProducts] products loaded", + "products loaded", logger.Fields{"ProductsResult": result}, ) + span.SetAttributes(attribute.Object("ProductsResult", result)) return result, nil @@ -75,19 +79,20 @@ func (p *postgresProductRepository) SearchProducts( defer span.End() result, err := p.gormGenericRepository.Search(ctx, searchText, listQuery) + err = utils2.TraceStatusFromContext( + ctx, + errors.WrapIf( + err, + "error in the paginate", + ), + ) if err != nil { - return nil, tracing.TraceErrFromContext( - ctx, - errors.WrapIf( - err, - "[postgresProductRepository_SearchProducts.Paginate] error in the paginate", - ), - ) + return nil, err } p.log.Infow( fmt.Sprintf( - "[postgresProductRepository.SearchProducts] products loaded for search term '%s'", + "products loaded for search term '%s'", searchText, ), logger.Fields{"ProductsResult": result}, @@ -102,30 +107,32 @@ func (p *postgresProductRepository) GetProductById( uuid uuid.UUID, ) (*models.Product, error) { ctx, span := p.tracer.Start(ctx, "postgresProductRepository.GetProductById") - span.SetAttributes(attribute2.String("ProductId", uuid.String())) + span.SetAttributes(attribute2.String("Id", uuid.String())) defer span.End() product, err := p.gormGenericRepository.GetById(ctx, uuid) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - errors.WrapIf( - err, - fmt.Sprintf( - "[postgresProductRepository_GetProductById.First] can't find the product with id %s into the database.", - uuid, - ), + err = utils2.TraceStatusFromSpan( + span, + errors.WrapIf( + err, + fmt.Sprintf( + "can't find the product with id %s into the database.", + uuid, ), - ) + ), + ) + + if err != nil { + return nil, err } span.SetAttributes(attribute.Object("Product", product)) p.log.Infow( fmt.Sprintf( - "[postgresProductRepository.GetProductById] product with id %s laoded", + "product with id %s laoded", uuid.String(), ), - logger.Fields{"Product": product, "ProductId": uuid}, + logger.Fields{"Product": product, "Id": uuid}, ) return product, nil @@ -139,23 +146,24 @@ func (p *postgresProductRepository) CreateProduct( defer span.End() err := p.gormGenericRepository.Add(ctx, product) + err = utils2.TraceStatusFromSpan( + span, + errors.WrapIf( + err, + "error in the inserting product into the database.", + ), + ) if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - errors.WrapIf( - err, - "[postgresProductRepository_CreateProduct.Create] error in the inserting product into the database.", - ), - ) + return nil, err } span.SetAttributes(attribute.Object("Product", product)) p.log.Infow( fmt.Sprintf( - "[postgresProductRepository.CreateProduct] product with id '%s' created", - product.ProductId, + "product with id '%s' created", + product.Id, ), - logger.Fields{"Product": product, "ProductId": product.ProductId}, + logger.Fields{"Product": product, "Id": product.Id}, ) return product, nil @@ -169,47 +177,57 @@ func (p *postgresProductRepository) UpdateProduct( defer span.End() err := p.gormGenericRepository.Update(ctx, updateProduct) - if err != nil { - return nil, tracing.TraceErrFromSpan( - span, - errors.WrapIf( - err, - fmt.Sprintf( - "[postgresProductRepository_UpdateProduct.Save] error in updating product with id %s into the database.", - updateProduct.ProductId, - ), + err = utils2.TraceStatusFromSpan( + span, + errors.WrapIf( + err, + fmt.Sprintf( + "error in updating product with id %s into the database.", + updateProduct.Id, ), - ) + ), + ) + + if err != nil { + return nil, err } span.SetAttributes(attribute.Object("Product", updateProduct)) p.log.Infow( fmt.Sprintf( - "[postgresProductRepository.UpdateProduct] product with id '%s' updated", - updateProduct.ProductId, + "product with id '%s' updated", + updateProduct.Id, ), - logger.Fields{"Product": updateProduct, "ProductId": updateProduct.ProductId}, + logger.Fields{ + "Product": updateProduct, + "Id": updateProduct.Id, + }, ) return updateProduct, nil } -func (p *postgresProductRepository) DeleteProductByID(ctx context.Context, uuid uuid.UUID) error { +func (p *postgresProductRepository) DeleteProductByID( + ctx context.Context, + uuid uuid.UUID, +) error { ctx, span := p.tracer.Start(ctx, "postgresProductRepository.UpdateProduct") - span.SetAttributes(attribute2.String("ProductId", uuid.String())) + span.SetAttributes(attribute2.String("Id", uuid.String())) defer span.End() err := p.gormGenericRepository.Delete(ctx, uuid) + err = utils2.TraceStatusFromSpan(span, errors.WrapIf(err, fmt.Sprintf( + "error in the deleting product with id %s into the database.", + uuid, + ))) + if err != nil { - return tracing.TraceErrFromSpan(span, errors.WrapIf(err, fmt.Sprintf( - "[postgresProductRepository_DeleteProductByID.Delete] error in the deleting product with id %s into the database.", - uuid, - ))) + return err } p.log.Infow( fmt.Sprintf( - "[postgresProductRepository.DeleteProductByID] product with id %s deleted", + "product with id %s deleted", uuid, ), logger.Fields{"Product": uuid}, diff --git a/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_handler_params.go b/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_handler_params.go new file mode 100644 index 00000000..208df77e --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_handler_params.go @@ -0,0 +1,19 @@ +package fxparams + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data/dbcontext" + + "go.uber.org/fx" +) + +type ProductHandlerParams struct { + fx.In + + Log logger.Logger + CatalogsDBContext *dbcontext.CatalogsGormDBContext + RabbitmqProducer producer.Producer + Tracer tracing.AppTracer +} diff --git a/internal/services/catalog_write_service/internal/products/contracts/params/product_route_params.go b/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_route_params.go similarity index 96% rename from internal/services/catalog_write_service/internal/products/contracts/params/product_route_params.go rename to internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_route_params.go index d5b0cc00..f2e8ce06 100644 --- a/internal/services/catalog_write_service/internal/products/contracts/params/product_route_params.go +++ b/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams/product_route_params.go @@ -1,4 +1,4 @@ -package params +package fxparams import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" diff --git a/internal/services/catalog_write_service/internal/products/dto/v1/product_dto.go b/internal/services/catalogwriteservice/internal/products/dtos/v1/product_dto.go similarity index 83% rename from internal/services/catalog_write_service/internal/products/dto/v1/product_dto.go rename to internal/services/catalogwriteservice/internal/products/dtos/v1/product_dto.go index 70f4e9c9..9a3a386d 100644 --- a/internal/services/catalog_write_service/internal/products/dto/v1/product_dto.go +++ b/internal/services/catalogwriteservice/internal/products/dtos/v1/product_dto.go @@ -1,4 +1,4 @@ -package dtoV1 +package v1 import ( "time" @@ -7,7 +7,7 @@ import ( ) type ProductDto struct { - ProductId uuid.UUID `json:"productId"` + Id uuid.UUID `json:"id"` Name string `json:"name"` Description string `json:"description"` Price float64 `json:"price"` diff --git a/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product.go new file mode 100644 index 00000000..72258679 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product.go @@ -0,0 +1,85 @@ +package v1 + +import ( + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + + validation "github.com/go-ozzo/ozzo-validation" + uuid "github.com/satori/go.uuid" +) + +// https://echo.labstack.com/guide/request/ +// https://github.com/go-playground/validator + +type CreateProduct struct { + cqrs.Command + ProductID uuid.UUID + Name string + Description string + Price float64 + CreatedAt time.Time +} + +// NewCreateProduct Create a new product +func NewCreateProduct( + name string, + description string, + price float64, +) *CreateProduct { + command := &CreateProduct{ + Command: cqrs.NewCommandByT[CreateProduct](), + ProductID: uuid.NewV4(), + Name: name, + Description: description, + Price: price, + CreatedAt: time.Now(), + } + + return command +} + +// NewCreateProductWithValidation Create a new product with inline validation - for defensive programming and ensuring validation even without using middleware +func NewCreateProductWithValidation( + name string, + description string, + price float64, +) (*CreateProduct, error) { + command := NewCreateProduct(name, description, price) + err := command.Validate() + + return command, err +} + +func (c *CreateProduct) isTxRequest() bool { + return true +} + +func (c *CreateProduct) Validate() error { + err := validation.ValidateStruct( + c, + validation.Field(&c.ProductID, validation.Required), + validation.Field( + &c.Name, + validation.Required, + validation.Length(0, 255), + ), + validation.Field( + &c.Description, + validation.Required, + validation.Length(0, 5000), + ), + validation.Field( + &c.Price, + validation.Required, + validation.Min(0.0).Exclusive(), + ), + validation.Field(&c.CreatedAt, validation.Required), + ) + if err != nil { + return customErrors.NewValidationErrorWrap(err, "validation error") + } + + return nil +} diff --git a/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_endpoint.go new file mode 100644 index 00000000..41ec53a0 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_endpoint.go @@ -0,0 +1,75 @@ +package v1 + +import ( + "net/http" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + + "emperror.dev/errors" + "github.com/labstack/echo/v4" + "github.com/mehdihadeli/go-mediatr" +) + +type createProductEndpoint struct { + fxparams.ProductRouteParams +} + +func NewCreteProductEndpoint( + params fxparams.ProductRouteParams, +) route.Endpoint { + return &createProductEndpoint{ProductRouteParams: params} +} + +func (ep *createProductEndpoint) MapEndpoint() { + ep.ProductsGroup.POST("", ep.handler()) +} + +// CreateProduct +// @Tags Products +// @Summary Create product +// @Description Create new product item +// @Accept json +// @Produce json +// @Param CreateProductRequestDto body dtos.CreateProductRequestDto true "Product data" +// @Success 201 {object} dtos.CreateProductResponseDto +// @Router /api/v1/products [post] +func (ep *createProductEndpoint) handler() echo.HandlerFunc { + return func(c echo.Context) error { + ctx := c.Request().Context() + + request := &dtos.CreateProductRequestDto{} + if err := c.Bind(request); err != nil { + badRequestErr := customErrors.NewBadRequestErrorWrap( + err, + "error in the binding request", + ) + + return badRequestErr + } + + command, err := NewCreateProductWithValidation( + request.Name, + request.Description, + request.Price, + ) + if err != nil { + return err + } + + result, err := mediatr.Send[*CreateProduct, *dtos.CreateProductResponseDto]( + ctx, + command, + ) + if err != nil { + return errors.WithMessage( + err, + "error in sending CreateProduct", + ) + } + + return c.JSON(http.StatusCreated, result) + } +} diff --git a/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_handler.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_handler.go new file mode 100644 index 00000000..04d02cab --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/create_product_handler.go @@ -0,0 +1,107 @@ +package v1 + +import ( + "context" + "fmt" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dtosv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "github.com/mehdihadeli/go-mediatr" +) + +type createProductHandler struct { + fxparams.ProductHandlerParams +} + +func NewCreateProductHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*CreateProduct, *dtos.CreateProductResponseDto] { + return &createProductHandler{ + ProductHandlerParams: params, + } +} + +func (c *createProductHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*CreateProduct, *dtos.CreateProductResponseDto]( + c, + ) +} + +func (c *createProductHandler) Handle( + ctx context.Context, + command *CreateProduct, +) (*dtos.CreateProductResponseDto, error) { + product := &models.Product{ + Id: command.ProductID, + Name: command.Name, + Description: command.Description, + Price: command.Price, + CreatedAt: command.CreatedAt, + } + + var createProductResult *dtos.CreateProductResponseDto + + result, err := gormdbcontext.AddModel[*datamodel.ProductDataModel, *models.Product]( + ctx, + c.CatalogsDBContext, + product, + ) + if err != nil { + return nil, err + } + + productDto, err := mapper.Map[*dtosv1.ProductDto](result) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping ProductDto", + ) + } + + productCreated := integrationevents.NewProductCreatedV1( + productDto, + ) + + err = c.RabbitmqProducer.PublishMessage(ctx, productCreated, nil) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in publishing ProductCreated integration_events event", + ) + } + + c.Log.Infow( + fmt.Sprintf( + "ProductCreated message with messageId `%s` published to the rabbitmq broker", + productCreated.MessageId, + ), + logger.Fields{"MessageId": productCreated.MessageId}, + ) + + createProductResult = &dtos.CreateProductResponseDto{ + ProductID: product.Id, + } + + c.Log.Infow( + fmt.Sprintf( + "product with id '%s' created", + command.ProductID, + ), + logger.Fields{ + "Id": command.ProductID, + "MessageId": productCreated.MessageId, + }, + ) + + return createProductResult, err +} diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/dtos/create_product_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos/create_product_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/creating_product/v1/dtos/create_product_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos/create_product_request_dto.go diff --git a/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos/create_product_response_dto.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos/create_product_response_dto.go new file mode 100644 index 00000000..e00a1ff9 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos/create_product_response_dto.go @@ -0,0 +1,16 @@ +package dtos + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/serializer/json" + + uuid "github.com/satori/go.uuid" +) + +// https://echo.labstack.com/guide/response/ +type CreateProductResponseDto struct { + ProductID uuid.UUID `json:"productId"` +} + +func (c *CreateProductResponseDto) String() string { + return json.PrettyPrint(c) +} diff --git a/internal/services/order_service/cmd/migration/.gitkeep b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/domainevents/.gitkeep similarity index 100% rename from internal/services/order_service/cmd/migration/.gitkeep rename to internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/domainevents/.gitkeep diff --git a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/events/integration_events/product_created.go b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents/product_created.go similarity index 80% rename from internal/services/catalog_write_service/internal/products/features/creating_product/v1/events/integration_events/product_created.go rename to internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents/product_created.go index c47c2546..53dd041f 100644 --- a/internal/services/catalog_write_service/internal/products/features/creating_product/v1/events/integration_events/product_created.go +++ b/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents/product_created.go @@ -1,8 +1,8 @@ -package integration_events +package integrationevents import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" uuid "github.com/satori/go.uuid" ) diff --git a/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product.go b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product.go new file mode 100644 index 00000000..d560852d --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product.go @@ -0,0 +1,46 @@ +package v1 + +import ( + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + + validation "github.com/go-ozzo/ozzo-validation" + "github.com/go-ozzo/ozzo-validation/is" + uuid "github.com/satori/go.uuid" +) + +type DeleteProduct struct { + ProductID uuid.UUID +} + +// NewDeleteProduct delete a product +func NewDeleteProduct(productID uuid.UUID) *DeleteProduct { + command := &DeleteProduct{ProductID: productID} + + return command +} + +// NewDeleteProductWithValidation delete a product with inline validation - for defensive programming and ensuring validation even without using middleware +func NewDeleteProductWithValidation(productID uuid.UUID) (*DeleteProduct, error) { + command := NewDeleteProduct(productID) + err := command.Validate() + + return command, err +} + +// IsTxRequest for enabling transactions on the mediatr pipeline +func (c *DeleteProduct) isTxRequest() bool { + return true +} + +func (c *DeleteProduct) Validate() error { + err := validation.ValidateStruct( + c, + validation.Field(&c.ProductID, validation.Required), + validation.Field(&c.ProductID, is.UUIDv4), + ) + if err != nil { + return customErrors.NewValidationErrorWrap(err, "validation error") + } + + return nil +} diff --git a/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_endpoint.go new file mode 100644 index 00000000..74b036da --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_endpoint.go @@ -0,0 +1,72 @@ +package v1 + +import ( + "net/http" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/dtos" + + "emperror.dev/errors" + "github.com/labstack/echo/v4" + "github.com/mehdihadeli/go-mediatr" +) + +type deleteProductEndpoint struct { + fxparams.ProductRouteParams +} + +func NewDeleteProductEndpoint( + params fxparams.ProductRouteParams, +) route.Endpoint { + return &deleteProductEndpoint{ProductRouteParams: params} +} + +func (ep *deleteProductEndpoint) MapEndpoint() { + ep.ProductsGroup.DELETE("/:id", ep.handler()) +} + +// DeleteProduct +// @Tags Products +// @Summary Delete product +// @Description Delete existing product +// @Accept json +// @Produce json +// @Success 204 +// @Param id path string true "Product ID" +// @Router /api/v1/products/{id} [delete] +func (ep *deleteProductEndpoint) handler() echo.HandlerFunc { + return func(c echo.Context) error { + ctx := c.Request().Context() + + request := &dtos.DeleteProductRequestDto{} + if err := c.Bind(request); err != nil { + badRequestErr := customErrors.NewBadRequestErrorWrap( + err, + "error in the binding request", + ) + + return badRequestErr + } + + command, err := NewDeleteProductWithValidation(request.ProductID) + if err != nil { + return err + } + + _, err = mediatr.Send[*DeleteProduct, *mediatr.Unit]( + ctx, + command, + ) + + if err != nil { + return errors.WithMessage( + err, + "error in sending DeleteProduct", + ) + } + + return c.NoContent(http.StatusNoContent) + } +} diff --git a/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_handler.go b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_handler.go new file mode 100644 index 00000000..84b85cb9 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/delete_product_handler.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "context" + "fmt" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/events/integrationevents" + + "github.com/mehdihadeli/go-mediatr" +) + +type deleteProductHandler struct { + fxparams.ProductHandlerParams +} + +func NewDeleteProductHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*DeleteProduct, *mediatr.Unit] { + return &deleteProductHandler{ + ProductHandlerParams: params, + } +} + +func (c *deleteProductHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*DeleteProduct, *mediatr.Unit]( + c, + ) +} + +// IsTxRequest for enabling transactions on the mediatr pipeline +func (c *deleteProductHandler) isTxRequest() bool { + return true +} + +func (c *deleteProductHandler) Handle( + ctx context.Context, + command *DeleteProduct, +) (*mediatr.Unit, error) { + err := gormdbcontext.DeleteDataModelByID[*datamodels.ProductDataModel](ctx, c.CatalogsDBContext, command.ProductID) + if err != nil { + return nil, err + } + + productDeleted := integrationEvents.NewProductDeletedV1( + command.ProductID.String(), + ) + + if err = c.RabbitmqProducer.PublishMessage(ctx, productDeleted, nil); err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in publishing 'ProductDeleted' message", + ) + } + + c.Log.Infow( + fmt.Sprintf( + "ProductDeleted message with messageId '%s' published to the rabbitmq broker", + productDeleted.MessageId, + ), + logger.Fields{"MessageId": productDeleted.MessageId}, + ) + + c.Log.Infow( + fmt.Sprintf( + "product with id '%s' deleted", + command.ProductID, + ), + logger.Fields{"Id": command.ProductID}, + ) + + return &mediatr.Unit{}, err +} diff --git a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/dtos/delete_product_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/dtos/delete_product_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/deleting_product/v1/dtos/delete_product_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/dtos/delete_product_request_dto.go diff --git a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/events/integration_events/product_deleted.go b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/events/integrationevents/product_deleted.go similarity index 94% rename from internal/services/catalog_write_service/internal/products/features/deleting_product/v1/events/integration_events/product_deleted.go rename to internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/events/integrationevents/product_deleted.go index 219aead7..bc449dd6 100644 --- a/internal/services/catalog_write_service/internal/products/features/deleting_product/v1/events/integration_events/product_deleted.go +++ b/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/events/integrationevents/product_deleted.go @@ -1,7 +1,7 @@ package integrationEvents import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" uuid "github.com/satori/go.uuid" ) diff --git a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/dtos/get_product_by_id_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos/get_product_by_id_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/dtos/get_product_by_id_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos/get_product_by_id_request_dto.go diff --git a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/dtos/get_product_by_id_response_dto.go b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos/get_product_by_id_response_dto.go similarity index 76% rename from internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/dtos/get_product_by_id_response_dto.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos/get_product_by_id_response_dto.go index 0bace22c..6539a8da 100644 --- a/internal/services/catalog_write_service/internal/products/features/getting_product_by_id/v1/dtos/get_product_by_id_response_dto.go +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos/get_product_by_id_response_dto.go @@ -1,6 +1,6 @@ package dtos -import dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" +import dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" // https://echo.labstack.com/guide/response/ type GetProductByIdResponseDto struct { diff --git a/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id.go b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id.go new file mode 100644 index 00000000..f88a07f3 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id.go @@ -0,0 +1,46 @@ +package v1 + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + + validation "github.com/go-ozzo/ozzo-validation" + "github.com/go-ozzo/ozzo-validation/is" + uuid "github.com/satori/go.uuid" +) + +// https://echo.labstack.com/guide/request/ +// https://github.com/go-playground/validator + +type GetProductById struct { + cqrs.Query + ProductID uuid.UUID +} + +func NewGetProductById(productId uuid.UUID) *GetProductById { + query := &GetProductById{ + Query: cqrs.NewQueryByT[GetProductById](), + ProductID: productId, + } + + return query +} + +func NewGetProductByIdWithValidation(productId uuid.UUID) (*GetProductById, error) { + query := NewGetProductById(productId) + err := query.Validate() + + return query, err +} + +func (p *GetProductById) Validate() error { + err := validation.ValidateStruct( + p, + validation.Field(&p.ProductID, validation.Required, is.UUIDv4), + ) + if err != nil { + return customErrors.NewValidationErrorWrap(err, "validation error") + } + + return nil +} diff --git a/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_endpoint.go new file mode 100644 index 00000000..450f6682 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_endpoint.go @@ -0,0 +1,71 @@ +package v1 + +import ( + "net/http" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos" + + "emperror.dev/errors" + "github.com/labstack/echo/v4" + "github.com/mehdihadeli/go-mediatr" +) + +type getProductByIdEndpoint struct { + fxparams.ProductRouteParams +} + +func NewGetProductByIdEndpoint( + params fxparams.ProductRouteParams, +) route.Endpoint { + return &getProductByIdEndpoint{ProductRouteParams: params} +} + +func (ep *getProductByIdEndpoint) MapEndpoint() { + ep.ProductsGroup.GET("/:id", ep.handler()) +} + +// GetProductByID +// @Tags Products +// @Summary Get product by id +// @Description Get product by id +// @Accept json +// @Produce json +// @Param id path string true "Product ID" +// @Success 200 {object} dtos.GetProductByIdResponseDto +// @Router /api/v1/products/{id} [get] +func (ep *getProductByIdEndpoint) handler() echo.HandlerFunc { + return func(c echo.Context) error { + ctx := c.Request().Context() + + request := &dtos.GetProductByIdRequestDto{} + if err := c.Bind(request); err != nil { + badRequestErr := customErrors.NewBadRequestErrorWrap( + err, + "error in the binding request", + ) + + return badRequestErr + } + + query, err := NewGetProductByIdWithValidation(request.ProductId) + if err != nil { + return err + } + + queryResult, err := mediatr.Send[*GetProductById, *dtos.GetProductByIdResponseDto]( + ctx, + query, + ) + if err != nil { + return errors.WithMessage( + err, + "error in sending GetProductById", + ) + } + + return c.JSON(http.StatusOK, queryResult) + } +} diff --git a/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_handler.go b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_handler.go new file mode 100644 index 00000000..2e913bfc --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/get_product_by_id_handler.go @@ -0,0 +1,69 @@ +package v1 + +import ( + "context" + "fmt" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "github.com/mehdihadeli/go-mediatr" +) + +type GetProductByIDHandler struct { + fxparams.ProductHandlerParams +} + +func NewGetProductByIDHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*GetProductById, *dtos.GetProductByIdResponseDto] { + return &GetProductByIDHandler{ + ProductHandlerParams: params, + } +} + +func (c *GetProductByIDHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*GetProductById, *dtos.GetProductByIdResponseDto]( + c, + ) +} + +func (c *GetProductByIDHandler) Handle( + ctx context.Context, + query *GetProductById, +) (*dtos.GetProductByIdResponseDto, error) { + product, err := gormdbcontext.FindModelByID[*datamodels.ProductDataModel, *models.Product]( + ctx, + c.CatalogsDBContext, + query.ProductID, + ) + if err != nil { + return nil, err + } + + productDto, err := mapper.Map[*dtoV1.ProductDto](product) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping product", + ) + } + + c.Log.Infow( + fmt.Sprintf( + "product with id: {%s} fetched", + query.ProductID, + ), + logger.Fields{"Id": query.ProductID.String()}, + ) + + return &dtos.GetProductByIdResponseDto{Product: productDto}, nil +} diff --git a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos/get_products_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/getting_products/v1/dtos/get_products_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos/get_products_request_dto.go diff --git a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos/get_products_response_dto.go similarity index 83% rename from internal/services/catalog_write_service/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos/get_products_response_dto.go index a28df2e3..d51f19e4 100644 --- a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/dtos/get_products_response_dto.go +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos/get_products_response_dto.go @@ -2,7 +2,7 @@ package dtos import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" ) // https://echo.labstack.com/guide/response/ diff --git a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products.go b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products.go similarity index 94% rename from internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products.go index 7e8676b0..290150e9 100644 --- a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/queries/get_products.go +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products.go @@ -1,4 +1,4 @@ -package queries +package v1 import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" diff --git a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_endpoint.go similarity index 56% rename from internal/services/catalog_write_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go rename to internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_endpoint.go index 6fb80faf..272d0228 100644 --- a/internal/services/catalog_write_service/internal/products/features/getting_products/v1/endpoints/get_products_endpoint.go +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_endpoint.go @@ -1,15 +1,13 @@ -package endpoints +package v1 import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/queries" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos" "emperror.dev/errors" "github.com/labstack/echo/v4" @@ -17,11 +15,11 @@ import ( ) type getProductsEndpoint struct { - params.ProductRouteParams + fxparams.ProductRouteParams } func NewGetProductsEndpoint( - params params.ProductRouteParams, + params fxparams.ProductRouteParams, ) route.Endpoint { return &getProductsEndpoint{ProductRouteParams: params} } @@ -42,51 +40,41 @@ func (ep *getProductsEndpoint) MapEndpoint() { func (ep *getProductsEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.GetProductsHttpRequests.Add(ctx, 1) listQuery, err := utils.GetListQueryFromCtx(c) if err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[getProductsEndpoint_handler.GetListQueryFromCtx] error in getting data from query string", + "error in getting data from query string", ) - ep.Logger.Errorf( - fmt.Sprintf( - "[getProductsEndpoint_handler.GetListQueryFromCtx] err: %v", - badRequestErr, - ), - ) - return err + + return badRequestErr } request := &dtos.GetProductsRequestDto{ListQuery: listQuery} if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[getProductsEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[getProductsEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } - query, err := queries.NewGetProducts(request.ListQuery) + query, err := NewGetProducts(request.ListQuery) if err != nil { return err } - queryResult, err := mediatr.Send[*queries.GetProducts, *dtos.GetProductsResponseDto]( + queryResult, err := mediatr.Send[*GetProducts, *dtos.GetProductsResponseDto]( ctx, query, ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[getProductsEndpoint_handler.Send] error in sending GetProducts", + "error in sending GetProducts", ) - ep.Logger.Error(fmt.Sprintf("[getProductsEndpoint_handler.Send] err: {%v}", err)) - return err } return c.JSON(http.StatusOK, queryResult) diff --git a/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_handler.go b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_handler.go new file mode 100644 index 00000000..62e27ce9 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/get_products_handler.go @@ -0,0 +1,66 @@ +package v1 + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dtosv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "github.com/mehdihadeli/go-mediatr" +) + +type getProductsHandler struct { + fxparams.ProductHandlerParams +} + +func NewGetProductsHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*GetProducts, *dtos.GetProductsResponseDto] { + return &getProductsHandler{ + ProductHandlerParams: params, + } +} + +func (c *getProductsHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*GetProducts, *dtos.GetProductsResponseDto]( + c, + ) +} + +func (c *getProductsHandler) Handle( + ctx context.Context, + query *GetProducts, +) (*dtos.GetProductsResponseDto, error) { + products, err := gormextensions.Paginate[*datamodel.ProductDataModel, *models.Product]( + ctx, + query.ListQuery, + c.CatalogsDBContext.DB(), + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the fetching products", + ) + } + + listResultDto, err := utils.ListResultToListResultDto[*dtosv1.ProductDto]( + products, + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping", + ) + } + + c.Log.Info("products fetched") + + return &dtos.GetProductsResponseDto{Products: listResultDto}, nil +} diff --git a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/dtos/search_products_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos/search_products_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/searching_product/v1/dtos/search_products_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos/search_products_request_dto.go diff --git a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/dtos/search_products_response_dto.go b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos/search_products_response_dto.go similarity index 80% rename from internal/services/catalog_write_service/internal/products/features/searching_product/v1/dtos/search_products_response_dto.go rename to internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos/search_products_response_dto.go index 6c3e630f..d3ebf020 100644 --- a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/dtos/search_products_response_dto.go +++ b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos/search_products_response_dto.go @@ -2,7 +2,7 @@ package dtos import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" ) type SearchProductsResponseDto struct { diff --git a/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products.go b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products.go new file mode 100644 index 00000000..b32647c8 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products.go @@ -0,0 +1,39 @@ +package v1 + +import ( + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + + validation "github.com/go-ozzo/ozzo-validation" +) + +type SearchProducts struct { + SearchText string + *utils.ListQuery +} + +func NewSearchProducts(searchText string, query *utils.ListQuery) *SearchProducts { + searchProductQuery := &SearchProducts{ + SearchText: searchText, + ListQuery: query, + } + + return searchProductQuery +} + +func NewSearchProductsWithValidation(searchText string, query *utils.ListQuery) (*SearchProducts, error) { + searchProductQuery := NewSearchProducts(searchText, query) + + err := searchProductQuery.Validate() + + return searchProductQuery, err +} + +func (p *SearchProducts) Validate() error { + err := validation.ValidateStruct(p, validation.Field(&p.SearchText, validation.Required)) + if err != nil { + return customErrors.NewValidationErrorWrap(err, "validation error") + } + + return nil +} diff --git a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/endpoints/search_products_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_endpoint.go similarity index 51% rename from internal/services/catalog_write_service/internal/products/features/searching_product/v1/endpoints/search_products_endpoint.go rename to internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_endpoint.go index 2b3f8e5f..efd9ad54 100644 --- a/internal/services/catalog_write_service/internal/products/features/searching_product/v1/endpoints/search_products_endpoint.go +++ b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_endpoint.go @@ -1,15 +1,13 @@ -package endpoints +package v1 import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searching_product/v1/queries" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos" "emperror.dev/errors" "github.com/labstack/echo/v4" @@ -17,11 +15,11 @@ import ( ) type searchProductsEndpoint struct { - params.ProductRouteParams + fxparams.ProductRouteParams } func NewSearchProductsEndpoint( - params params.ProductRouteParams, + params fxparams.ProductRouteParams, ) route.Endpoint { return &searchProductsEndpoint{ProductRouteParams: params} } @@ -42,56 +40,44 @@ func (ep *searchProductsEndpoint) MapEndpoint() { func (ep *searchProductsEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.SearchProductHttpRequests.Add(ctx, 1) listQuery, err := utils.GetListQueryFromCtx(c) if err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[searchProductsEndpoint_handler.GetListQueryFromCtx] error in getting data from query string", + "error in getting data from query string", ) - ep.Logger.Errorf( - fmt.Sprintf( - "[searchProductsEndpoint_handler.GetListQueryFromCtx] err: %v", - badRequestErr, - ), - ) - return err + + return badRequestErr } request := &dtos.SearchProductsRequestDto{ListQuery: listQuery} if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[searchProductsEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[searchProductsEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } - query, err := queries.NewSearchProducts(request.SearchText, request.ListQuery) + query, err := NewSearchProductsWithValidation( + request.SearchText, + request.ListQuery, + ) if err != nil { - validationErr := customErrors.NewValidationErrorWrap( - err, - "[searchProductsEndpoint_handler.StructCtx] query validation failed", - ) - ep.Logger.Errorf("[searchProductsEndpoint_handler.StructCtx] err: {%v}", validationErr) - return validationErr + return err } - queryResult, err := mediatr.Send[*queries.SearchProducts, *dtos.SearchProductsResponseDto]( + queryResult, err := mediatr.Send[*SearchProducts, *dtos.SearchProductsResponseDto]( ctx, query, ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[searchProductsEndpoint_handler.Send] error in sending SearchProducts", + "error in sending SearchProducts", ) - ep.Logger.Error(fmt.Sprintf("[searchProductsEndpoint_handler.Send] err: {%v}", err)) - return err } return c.JSON(http.StatusOK, queryResult) diff --git a/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_handler.go b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_handler.go new file mode 100644 index 00000000..f251cf01 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/search_products_handler.go @@ -0,0 +1,98 @@ +package v1 + +import ( + "context" + "fmt" + "reflect" + "strings" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + reflectionHelper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/reflectionhelper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "github.com/iancoleman/strcase" + "github.com/mehdihadeli/go-mediatr" + "gorm.io/gorm" +) + +type searchProductsHandler struct { + fxparams.ProductHandlerParams +} + +func NewSearchProductsHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*SearchProducts, *dtos.SearchProductsResponseDto] { + return &searchProductsHandler{ + ProductHandlerParams: params, + } +} + +func (c *searchProductsHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*SearchProducts, *dtos.SearchProductsResponseDto]( + c, + ) +} + +func (c *searchProductsHandler) Handle( + ctx context.Context, + query *SearchProducts, +) (*dtos.SearchProductsResponseDto, error) { + dbQuery := c.prepareSearchDBQuery(query) + + products, err := gormPostgres.Paginate[*datamodel.ProductDataModel, *models.Product]( + ctx, + query.ListQuery, + dbQuery, + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in searching products in the repository", + ) + } + + listResultDto, err := utils.ListResultToListResultDto[*dto.ProductDto]( + products, + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping ListResultToListResultDto", + ) + } + + c.Log.Info("products fetched") + + return &dtos.SearchProductsResponseDto{Products: listResultDto}, nil +} + +func (c *searchProductsHandler) prepareSearchDBQuery( + query *SearchProducts, +) *gorm.DB { + fields := reflectionHelper.GetAllFields( + typeMapper.GetGenericTypeByT[*datamodel.ProductDataModel](), + ) + + dbQuery := c.CatalogsDBContext.DB() + + for _, field := range fields { + if field.Type.Kind() != reflect.String { + continue + } + + dbQuery = dbQuery.Or( + fmt.Sprintf("%s LIKE ?", strcase.ToSnake(field.Name)), + "%"+strings.ToLower(query.SearchText)+"%", + ) + } + + return dbQuery +} diff --git a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/dtos/update_product_request_dto.go b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/dtos/update_product_request_dto.go similarity index 100% rename from internal/services/catalog_write_service/internal/products/features/updating_product/v1/dtos/update_product_request_dto.go rename to internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/dtos/update_product_request_dto.go diff --git a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/events/integration_events/product_updated.go b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/events/integrationevents/product_updated.go similarity index 80% rename from internal/services/catalog_write_service/internal/products/features/updating_product/v1/events/integration_events/product_updated.go rename to internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/events/integrationevents/product_updated.go index ae0c190e..7626a209 100644 --- a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/events/integration_events/product_updated.go +++ b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/events/integrationevents/product_updated.go @@ -1,8 +1,8 @@ -package integration_events +package integrationevents import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" - dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" + dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" uuid "github.com/satori/go.uuid" ) diff --git a/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product.go b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product.go new file mode 100644 index 00000000..256e3d66 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product.go @@ -0,0 +1,76 @@ +package v1 + +import ( + "time" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + + validation "github.com/go-ozzo/ozzo-validation" + uuid "github.com/satori/go.uuid" +) + +type UpdateProduct struct { + ProductID uuid.UUID + Name string + Description string + Price float64 + UpdatedAt time.Time +} + +func NewUpdateProduct( + productID uuid.UUID, + name string, + description string, + price float64, +) *UpdateProduct { + command := &UpdateProduct{ + ProductID: productID, + Name: name, + Description: description, + Price: price, + UpdatedAt: time.Now(), + } + + return command +} + +func NewUpdateProductWithValidation( + productID uuid.UUID, + name string, + description string, + price float64, +) (*UpdateProduct, error) { + command := NewUpdateProduct(productID, name, description, price) + err := command.Validate() + + return command, err +} + +// IsTxRequest for enabling transactions on the mediatr pipeline +func (c *UpdateProduct) isTxRequest() bool { + return true +} + +func (c *UpdateProduct) Validate() error { + err := validation.ValidateStruct( + c, + validation.Field(&c.ProductID, validation.Required), + validation.Field( + &c.Name, + validation.Required, + validation.Length(0, 255), + ), + validation.Field( + &c.Description, + validation.Required, + validation.Length(0, 5000), + ), + validation.Field(&c.Price, validation.Required, validation.Min(0.0)), + validation.Field(&c.UpdatedAt, validation.Required), + ) + if err != nil { + return customErrors.NewValidationErrorWrap(err, "validation error") + } + + return nil +} diff --git a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/endpoints/update_product_endpoint.go b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_endpoint.go similarity index 50% rename from internal/services/catalog_write_service/internal/products/features/updating_product/v1/endpoints/update_product_endpoint.go rename to internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_endpoint.go index 333eb5b8..5e93a64e 100644 --- a/internal/services/catalog_write_service/internal/products/features/updating_product/v1/endpoints/update_product_endpoint.go +++ b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_endpoint.go @@ -1,15 +1,12 @@ -package endpoints +package v1 import ( - "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/params" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/dtos" "emperror.dev/errors" "github.com/labstack/echo/v4" @@ -17,11 +14,11 @@ import ( ) type updateProductEndpoint struct { - params.ProductRouteParams + fxparams.ProductRouteParams } func NewUpdateProductEndpoint( - params params.ProductRouteParams, + params fxparams.ProductRouteParams, ) route.Endpoint { return &updateProductEndpoint{ProductRouteParams: params} } @@ -43,52 +40,36 @@ func (ep *updateProductEndpoint) MapEndpoint() { func (ep *updateProductEndpoint) handler() echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() - ep.CatalogsMetrics.UpdateProductHttpRequests.Add(ctx, 1) request := &dtos.UpdateProductRequestDto{} if err := c.Bind(request); err != nil { badRequestErr := customErrors.NewBadRequestErrorWrap( err, - "[updateProductEndpoint_handler.Bind] error in the binding request", - ) - ep.Logger.Errorf( - fmt.Sprintf("[updateProductEndpoint_handler.Bind] err: %v", badRequestErr), + "error in the binding request", ) + return badRequestErr } - command, err := commands.NewUpdateProduct( + command, err := NewUpdateProductWithValidation( request.ProductID, request.Name, request.Description, request.Price, ) if err != nil { - validationErr := customErrors.NewValidationErrorWrap( - err, - "[updateProductEndpoint_handler.StructCtx] command validation failed", - ) - ep.Logger.Errorf( - fmt.Sprintf("[updateProductEndpoint_handler.StructCtx] err: {%v}", validationErr), - ) - return validationErr + return err } - _, err = mediatr.Send[*commands.UpdateProduct, *mediatr.Unit](ctx, command) + _, err = mediatr.Send[*UpdateProduct, *mediatr.Unit]( + ctx, + command, + ) if err != nil { - err = errors.WithMessage( + return errors.WithMessage( err, - "[updateProductEndpoint_handler.Send] error in sending UpdateProduct", + "error in sending UpdateProduct", ) - ep.Logger.Errorw( - fmt.Sprintf( - "[updateProductEndpoint_handler.Send] id: {%s}, err: {%v}", - command.ProductID, - err, - ), - logger.Fields{"ProductId": command.ProductID}, - ) - return err } return c.NoContent(http.StatusNoContent) diff --git a/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_handler.go b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_handler.go new file mode 100644 index 00000000..cb592ebd --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/update_product_handler.go @@ -0,0 +1,118 @@ +package v1 + +import ( + "context" + "fmt" + "net/http" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + dto "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/events/integrationevents" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "github.com/mehdihadeli/go-mediatr" +) + +type updateProductHandler struct { + fxparams.ProductHandlerParams + cqrs.HandlerRegisterer +} + +func NewUpdateProductHandler( + params fxparams.ProductHandlerParams, +) cqrs.RequestHandlerWithRegisterer[*UpdateProduct, *mediatr.Unit] { + return &updateProductHandler{ + ProductHandlerParams: params, + } +} + +func (c *updateProductHandler) RegisterHandler() error { + return mediatr.RegisterRequestHandler[*UpdateProduct, *mediatr.Unit]( + c, + ) +} + +// IsTxRequest for enabling transactions on the mediatr pipeline +func (c *updateProductHandler) isTxRequest() bool { + return true +} + +func (c *updateProductHandler) Handle( + ctx context.Context, + command *UpdateProduct, +) (*mediatr.Unit, error) { + product, err := gormdbcontext.FindModelByID[*datamodels.ProductDataModel, *models.Product]( + ctx, + c.CatalogsDBContext, + command.ProductID, + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrapWithCode( + err, + http.StatusNotFound, + fmt.Sprintf( + "product with id `%s` not found", + command.ProductID, + ), + ) + } + + product.Name = command.Name + product.Price = command.Price + product.Description = command.Description + product.UpdatedAt = command.UpdatedAt + + updatedProduct, err := gormdbcontext.UpdateModel[*datamodels.ProductDataModel, *models.Product]( + ctx, + c.CatalogsDBContext, + product, + ) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in updating product in the repository", + ) + } + + productDto, err := mapper.Map[*dto.ProductDto](updatedProduct) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in the mapping ProductDto", + ) + } + + productUpdated := integrationevents.NewProductUpdatedV1(productDto) + + err = c.RabbitmqProducer.PublishMessage(ctx, productUpdated, nil) + if err != nil { + return nil, customErrors.NewApplicationErrorWrap( + err, + "error in publishing 'ProductUpdated' message", + ) + } + + c.Log.Infow( + fmt.Sprintf( + "product with id '%s' updated", + command.ProductID, + ), + logger.Fields{"Id": command.ProductID}, + ) + + c.Log.Infow( + fmt.Sprintf( + "ProductUpdated message with messageId `%s` published to the rabbitmq broker", + productUpdated.MessageId, + ), + logger.Fields{"MessageId": productUpdated.MessageId}, + ) + + return &mediatr.Unit{}, err +} diff --git a/internal/services/catalogwriteservice/internal/products/models/product.go b/internal/services/catalogwriteservice/internal/products/models/product.go new file mode 100644 index 00000000..913c5a7a --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/models/product.go @@ -0,0 +1,17 @@ +package models + +import ( + "time" + + uuid "github.com/satori/go.uuid" +) + +// Product model +type Product struct { + Id uuid.UUID + Name string + Description string + Price float64 + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/internal/services/catalogwriteservice/internal/products/products_fx.go b/internal/services/catalogwriteservice/internal/products/products_fx.go new file mode 100644 index 00000000..72bde291 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/products/products_fx.go @@ -0,0 +1,95 @@ +package products + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/repositories" + creatingproductv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1" + deletingproductv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1" + gettingproductbyidv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1" + gettingproductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1" + searchingproductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1" + updatingoroductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc" + + "github.com/labstack/echo/v4" + "go.uber.org/fx" +) + +var Module = fx.Module( + "productsfx", + + // Other provides + fx.Provide(repositories.NewPostgresProductRepository), + fx.Provide(grpc.NewProductGrpcService), + + fx.Provide( + fx.Annotate(func(catalogsServer contracts.EchoHttpServer) *echo.Group { + var g *echo.Group + catalogsServer.RouteBuilder(). + RegisterGroupFunc("/api/v1", func(v1 *echo.Group) { + group := v1.Group("/products") + g = group + }) + + return g + }, fx.ResultTags(`name:"product-echo-group"`)), + ), + + // add cqrs handlers to DI + fx.Provide( + cqrs.AsHandler( + creatingproductv1.NewCreateProductHandler, + "product-handlers", + ), + cqrs.AsHandler( + gettingproductsv1.NewGetProductsHandler, + "product-handlers", + ), + cqrs.AsHandler( + deletingproductv1.NewDeleteProductHandler, + "product-handlers", + ), + cqrs.AsHandler( + gettingproductbyidv1.NewGetProductByIDHandler, + "product-handlers", + ), + cqrs.AsHandler( + searchingproductsv1.NewSearchProductsHandler, + "product-handlers", + ), + cqrs.AsHandler( + updatingoroductsv1.NewUpdateProductHandler, + "product-handlers", + ), + ), + + // add endpoints to DI + fx.Provide( + route.AsRoute( + creatingproductv1.NewCreteProductEndpoint, + "product-routes", + ), + route.AsRoute( + updatingoroductsv1.NewUpdateProductEndpoint, + "product-routes", + ), + route.AsRoute( + gettingproductsv1.NewGetProductsEndpoint, + "product-routes", + ), + route.AsRoute( + searchingproductsv1.NewSearchProductsEndpoint, + "product-routes", + ), + route.AsRoute( + gettingproductbyidv1.NewGetProductByIdEndpoint, + "product-routes", + ), + route.AsRoute( + deletingproductv1.NewDeleteProductEndpoint, + "product-routes", + ), + ), +) diff --git a/internal/services/catalog_write_service/internal/shared/app/app.go b/internal/services/catalogwriteservice/internal/shared/app/app.go similarity index 52% rename from internal/services/catalog_write_service/internal/shared/app/app.go rename to internal/services/catalogwriteservice/internal/shared/app/app.go index c0a91e37..08756295 100644 --- a/internal/services/catalog_write_service/internal/shared/app/app.go +++ b/internal/services/catalogwriteservice/internal/shared/app/app.go @@ -1,6 +1,9 @@ package app import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/configurations/catalogs" ) @@ -18,10 +21,21 @@ func (a *App) Run() { app := appBuilder.Build() // configure application - app.ConfigureCatalogs() + err := app.ConfigureCatalogs() + if err != nil { + app.Logger().Fatalf("Error in ConfigureCatalogs", err) + } - app.MapCatalogsEndpoints() + err = app.MapCatalogsEndpoints() + if err != nil { + app.Logger().Fatalf("Error in MapCatalogsEndpoints", err) + } app.Logger().Info("Starting catalog_service application") + app.ResolveFunc(func(tracer tracing.AppTracer) { + _, span := tracer.Start(context.Background(), "Application started") + span.End() + }) + app.Run() } diff --git a/internal/services/catalog_write_service/internal/shared/app/application.go b/internal/services/catalogwriteservice/internal/shared/app/application.go similarity index 93% rename from internal/services/catalog_write_service/internal/shared/app/application.go rename to internal/services/catalogwriteservice/internal/shared/app/application.go index a74de8cc..cf8842d4 100644 --- a/internal/services/catalog_write_service/internal/shared/app/application.go +++ b/internal/services/catalogwriteservice/internal/shared/app/application.go @@ -1,7 +1,7 @@ package app import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/configurations/catalogs" @@ -18,7 +18,7 @@ func NewCatalogsWriteApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *CatalogsWriteApplication { app := fxapp.NewApplication(providers, decorates, options, logger, environment) return &CatalogsWriteApplication{ diff --git a/internal/services/catalog_write_service/internal/shared/app/application_builder.go b/internal/services/catalogwriteservice/internal/shared/app/application_builder.go similarity index 100% rename from internal/services/catalog_write_service/internal/shared/app/application_builder.go rename to internal/services/catalogwriteservice/internal/shared/app/application_builder.go diff --git a/internal/services/catalog_write_service/internal/shared/app/test/test_app.go b/internal/services/catalogwriteservice/internal/shared/app/test/test_app.go similarity index 82% rename from internal/services/catalog_write_service/internal/shared/app/test/test_app.go rename to internal/services/catalogwriteservice/internal/shared/app/test/test_app.go index b3ff7249..096cb737 100644 --- a/internal/services/catalog_write_service/internal/shared/app/test/test_app.go +++ b/internal/services/catalogwriteservice/internal/shared/app/test/test_app.go @@ -6,20 +6,19 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" + fxcontracts "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" - config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/config" + config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" contracts2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/contracts" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/goose" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/gorm" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/containers/testcontainer/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/configurations/catalogs" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data/dbcontext" productsService "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" "github.com/stretchr/testify/require" @@ -31,17 +30,16 @@ type TestApp struct{} type TestAppResult struct { Cfg *config.AppOptions Bus bus.RabbitmqBus - Container contracts.Container + Container fxcontracts.Container Logger logger.Logger RabbitmqOptions *config2.RabbitmqOptions EchoHttpOptions *config3.EchoHttpOptions GormOptions *gormPostgres.GormOptions - CatalogUnitOfWorks data.CatalogUnitOfWork - ProductRepository data.ProductRepository Gorm *gorm2.DB ProductServiceClient productsService.ProductsServiceClient GrpcClient grpc.GrpcClient PostgresMigrationRunner contracts2.PostgresMigrationRunner + CatalogsDBContext *dbcontext.CatalogsGormDBContext } func NewTestApp() *TestApp { @@ -54,16 +52,23 @@ func (a *TestApp) Run(t *testing.T) (result *TestAppResult) { // ref: https://github.com/uber-go/fx/blob/master/app_test.go appBuilder := NewCatalogsWriteTestApplicationBuilder(t) appBuilder.ProvideModule(catalogs.CatalogsServiceModule) - appBuilder.ProvideModule(goose.Module) - appBuilder.Decorate(rabbitmq.RabbitmqContainerOptionsDecorator(t, lifetimeCtx)) + appBuilder.Decorate( + rabbitmq.RabbitmqContainerOptionsDecorator(t, lifetimeCtx), + ) appBuilder.Decorate(gorm.GormContainerOptionsDecorator(t, lifetimeCtx)) testApp := appBuilder.Build() - testApp.ConfigureCatalogs() + err := testApp.ConfigureCatalogs() + if err != nil { + testApp.Logger().Fatalf("Error in ConfigureCatalogs, %s", err) + } - testApp.MapCatalogsEndpoints() + err = testApp.MapCatalogsEndpoints() + if err != nil { + testApp.Logger().Fatalf("Error in MapCatalogsEndpoints, %s", err) + } testApp.ResolveFunc( func(cfg *config.AppOptions, @@ -71,9 +76,8 @@ func (a *TestApp) Run(t *testing.T) (result *TestAppResult) { logger logger.Logger, rabbitmqOptions *config2.RabbitmqOptions, gormOptions *gormPostgres.GormOptions, - catalogUnitOfWorks data.CatalogUnitOfWork, - productRepository data.ProductRepository, gorm *gorm2.DB, + catalogsDBContext *dbcontext.CatalogsGormDBContext, echoOptions *config3.EchoHttpOptions, grpcClient grpc.GrpcClient, postgresMigrationRunner contracts2.PostgresMigrationRunner, @@ -87,9 +91,8 @@ func (a *TestApp) Run(t *testing.T) (result *TestAppResult) { Logger: logger, RabbitmqOptions: rabbitmqOptions, GormOptions: gormOptions, - ProductRepository: productRepository, - CatalogUnitOfWorks: catalogUnitOfWorks, Gorm: gorm, + CatalogsDBContext: catalogsDBContext, EchoHttpOptions: echoOptions, PostgresMigrationRunner: postgresMigrationRunner, ProductServiceClient: productsService.NewProductsServiceClient( @@ -105,7 +108,8 @@ func (a *TestApp) Run(t *testing.T) (result *TestAppResult) { // short timeout for handling start hooks and setup dependencies startCtx, cancel := context.WithTimeout(context.Background(), duration) defer cancel() - err := testApp.Start(startCtx) + + err = testApp.Start(startCtx) if err != nil { t.Errorf("Error starting, err: %v", err) os.Exit(1) diff --git a/internal/services/catalog_write_service/internal/shared/app/test/test_application.go b/internal/services/catalogwriteservice/internal/shared/app/test/test_application.go similarity index 95% rename from internal/services/catalog_write_service/internal/shared/app/test/test_application.go rename to internal/services/catalogwriteservice/internal/shared/app/test/test_application.go index e3bf2450..929735e1 100644 --- a/internal/services/catalog_write_service/internal/shared/app/test/test_application.go +++ b/internal/services/catalogwriteservice/internal/shared/app/test/test_application.go @@ -1,7 +1,7 @@ package test import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/test" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/app" @@ -22,7 +22,7 @@ func NewCatalogsWriteTestApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *CatalogsWriteTestApplication { testApp := test.NewTestApplication( tb, diff --git a/internal/services/catalog_write_service/internal/shared/app/test/test_application_builder.go b/internal/services/catalogwriteservice/internal/shared/app/test/test_application_builder.go similarity index 100% rename from internal/services/catalog_write_service/internal/shared/app/test/test_application_builder.go rename to internal/services/catalogwriteservice/internal/shared/app/test/test_application_builder.go diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator.go similarity index 55% rename from internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator.go rename to internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator.go index 063896eb..5aa27dc3 100644 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator.go +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator.go @@ -4,8 +4,10 @@ import ( "fmt" "net/http" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + echocontracts "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" + migrationcontracts "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure" @@ -20,9 +22,13 @@ type CatalogsServiceConfigurator struct { productsModuleConfigurator *configurations.ProductsModuleConfigurator } -func NewCatalogsServiceConfigurator(app contracts.Application) *CatalogsServiceConfigurator { +func NewCatalogsServiceConfigurator( + app contracts.Application, +) *CatalogsServiceConfigurator { infraConfigurator := infrastructure.NewInfrastructureConfigurator(app) - productModuleConfigurator := configurations.NewProductsModuleConfigurator(app) + productModuleConfigurator := configurations.NewProductsModuleConfigurator( + app, + ) return &CatalogsServiceConfigurator{ Application: app, @@ -31,45 +37,59 @@ func NewCatalogsServiceConfigurator(app contracts.Application) *CatalogsServiceC } } -func (ic *CatalogsServiceConfigurator) ConfigureCatalogs() { +func (ic *CatalogsServiceConfigurator) ConfigureCatalogs() error { // Shared // Infrastructure ic.infrastructureConfigurator.ConfigInfrastructures() // Shared // Catalogs configurations - ic.ResolveFunc(func(gorm *gorm.DB) error { - err := ic.migrateCatalogs(gorm) - if err != nil { - return err - } + ic.ResolveFunc( + func(db *gorm.DB, postgresMigrationRunner migrationcontracts.PostgresMigrationRunner) error { + err := ic.migrateCatalogs(postgresMigrationRunner) + if err != nil { + return err + } - return nil - }) + if ic.Environment() != environment.Test { + err = ic.seedCatalogs(db) + if err != nil { + return err + } + } + + return nil + }, + ) // Modules // Product module - ic.productsModuleConfigurator.ConfigureProductsModule() + err := ic.productsModuleConfigurator.ConfigureProductsModule() + + return err } -func (ic *CatalogsServiceConfigurator) MapCatalogsEndpoints() { +func (ic *CatalogsServiceConfigurator) MapCatalogsEndpoints() error { // Shared ic.ResolveFunc( - func(catalogsServer customEcho.EchoHttpServer, options *config.AppOptions) error { + func(catalogsServer echocontracts.EchoHttpServer, options *config.AppOptions) error { catalogsServer.SetupDefaultMiddlewares() - // Config catalogs root endpoint + // config catalogs root endpoint catalogsServer.RouteBuilder(). RegisterRoutes(func(e *echo.Echo) { e.GET("", func(ec echo.Context) error { return ec.String( http.StatusOK, - fmt.Sprintf("%s is running...", options.GetMicroserviceNameUpper()), + fmt.Sprintf( + "%s is running...", + options.GetMicroserviceNameUpper(), + ), ) }) }) - // Config catalogs swagger + // config catalogs swagger ic.configSwagger(catalogsServer.RouteBuilder()) return nil @@ -78,5 +98,7 @@ func (ic *CatalogsServiceConfigurator) MapCatalogsEndpoints() { // Modules // Products CatalogsServiceModule endpoints - ic.productsModuleConfigurator.MapProductsEndpoints() + err := ic.productsModuleConfigurator.MapProductsEndpoints() + + return err } diff --git a/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_migration.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_migration.go new file mode 100644 index 00000000..599fcba2 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_migration.go @@ -0,0 +1,40 @@ +package catalogs + +import ( + "context" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "gorm.io/gorm" +) + +func (ic *CatalogsServiceConfigurator) migrateCatalogs( + runner contracts.PostgresMigrationRunner, +) error { + // - for complex migration and ability to back-track to specific migration revision it is better we use `goose`, but if we want to use built-in gorm migration we can also sync gorm with `atlas` integration migration versioning for getting migration history from grom changes + // - here I used goose for migration, with using cmd/migration file + + // migration with Goorse + return migrateGoose(runner) +} + +func migrateGoose( + runner contracts.PostgresMigrationRunner, +) error { + err := runner.Up(context.Background(), 0) + + return err +} + +func migrateGorm( + db *gorm.DB, +) error { + // https://atlasgo.io/guides/orms/gorm + err := db.AutoMigrate(&models.Product{}) + if err != nil { + return err + } + + return nil +} diff --git a/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_seed.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_seed.go new file mode 100644 index 00000000..7fc9797d --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_seed.go @@ -0,0 +1,104 @@ +package catalogs + +import ( + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/testfixture" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + uuid "github.com/satori/go.uuid" + "gorm.io/gorm" +) + +func (ic *CatalogsServiceConfigurator) seedCatalogs( + db *gorm.DB, +) error { + err := seedDataManually(db) + if err != nil { + return err + } + + return nil +} + +func seedDataManually(gormDB *gorm.DB) error { + var count int64 + + // https://gorm.io/docs/advanced_query.html#Count + gormDB.Model(&datamodel.ProductDataModel{}).Count(&count) + if count > 0 { + return nil + } + + products := []*datamodel.ProductDataModel{ + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + } + + err := gormDB.CreateInBatches(products, len(products)).Error + if err != nil { + return errors.Wrap(err, "error in seed database") + } + + return nil +} + +func seedDataWithFixture(gormDB *gorm.DB) error { + var count int64 + + // https://gorm.io/docs/advanced_query.html#Count + gormDB.Model(&datamodel.ProductDataModel{}).Count(&count) + if count > 0 { + return nil + } + + db, err := gormDB.DB() + if err != nil { + return errors.WrapIf(err, "error in seed database") + } + + // https://github.com/go-testfixtures/testfixtures#templating + // seed data + var data []struct { + Name string + ProductId uuid.UUID + Description string + } + + f := []struct { + Name string + ProductId uuid.UUID + Description string + }{ + {gofakeit.Name(), uuid.NewV4(), gofakeit.AdjectiveDescriptive()}, + {gofakeit.Name(), uuid.NewV4(), gofakeit.AdjectiveDescriptive()}, + } + + data = append(data, f...) + + err = testfixture.RunPostgresFixture( + db, + []string{"db/fixtures/products"}, + map[string]interface{}{ + "Products": data, + }) + if err != nil { + return errors.WrapIf(err, "error in seed database") + } + + return nil +} diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go similarity index 94% rename from internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go rename to internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go index 72938eaf..c5ff5a76 100644 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_configurator_swagger.go @@ -1,7 +1,7 @@ package catalogs import ( - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/docs" "github.com/labstack/echo/v4" diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_fx.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_fx.go similarity index 54% rename from internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_fx.go rename to internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_fx.go index 660bd220..09254c20 100644 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/catalogs_fx.go +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/catalogs_fx.go @@ -5,9 +5,9 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/uow" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data" "go.opentelemetry.io/otel/metric" api "go.opentelemetry.io/otel/metric" @@ -20,13 +20,13 @@ var CatalogsServiceModule = fx.Module( // Shared Modules config.Module, infrastructure.Module, + data.Module, // Features Modules products.Module, // Other provides fx.Provide(provideCatalogsMetrics), - fx.Provide(uow.NewCatalogsUnitOfWork), ) // ref: https://github.com/open-telemetry/opentelemetry-go/blob/main/example/prometheus/main.go @@ -63,8 +63,13 @@ func provideCatalogsMetrics( } getProductByIdGrpcRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_get_product_by_id_grpc_requests_total", cfg.ServiceName), - api.WithDescription("The total number of get product by id grpc requests"), + fmt.Sprintf( + "%s_get_product_by_id_grpc_requests_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of get product by id grpc requests", + ), ) if err != nil { return nil, err @@ -79,88 +84,65 @@ func provideCatalogsMetrics( } createProductRabbitMQMessages, err := meter.Float64Counter( - fmt.Sprintf("%s_create_product_rabbitmq_messages_total", cfg.ServiceName), - api.WithDescription("The total number of create product rabbirmq messages"), + fmt.Sprintf( + "%s_create_product_rabbitmq_messages_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of create product rabbirmq messages", + ), ) if err != nil { return nil, err } updateProductRabbitMQMessages, err := meter.Float64Counter( - fmt.Sprintf("%s_update_product_rabbitmq_messages_total", cfg.ServiceName), - api.WithDescription("The total number of update product rabbirmq messages"), + fmt.Sprintf( + "%s_update_product_rabbitmq_messages_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of update product rabbirmq messages", + ), ) if err != nil { return nil, err } deleteProductRabbitMQMessages, err := meter.Float64Counter( - fmt.Sprintf("%s_delete_product_rabbitmq_messages_total", cfg.ServiceName), - api.WithDescription("The total number of delete product rabbirmq messages"), + fmt.Sprintf( + "%s_delete_product_rabbitmq_messages_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of delete product rabbirmq messages", + ), ) if err != nil { return nil, err } successRabbitMQMessages, err := meter.Float64Counter( - fmt.Sprintf("%s_search_product_rabbitmq_messages_total", cfg.ServiceName), - api.WithDescription("The total number of success rabbitmq processed messages"), + fmt.Sprintf( + "%s_search_product_rabbitmq_messages_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of success rabbitmq processed messages", + ), ) if err != nil { return nil, err } errorRabbitMQMessages, err := meter.Float64Counter( - fmt.Sprintf("%s_error_rabbitmq_processed_messages_total", cfg.ServiceName), - api.WithDescription("The total number of error rabbitmq processed messages"), - ) - if err != nil { - return nil, err - } - - createProductHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_create_product_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of create product http requests"), - ) - if err != nil { - return nil, err - } - - updateProductHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_update_product_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of update product http requests"), - ) - if err != nil { - return nil, err - } - - deleteProductHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_delete_product_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of delete product http requests"), - ) - if err != nil { - return nil, err - } - - getProductByIdHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_get_product_by_id_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of get product by id http requests"), - ) - if err != nil { - return nil, err - } - - getProductsHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_get_products_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of get products http requests"), - ) - if err != nil { - return nil, err - } - - searchProductHttpRequests, err := meter.Float64Counter( - fmt.Sprintf("%s_search_product_http_requests_total", cfg.ServiceName), - api.WithDescription("The total number of search product http requests"), + fmt.Sprintf( + "%s_error_rabbitmq_processed_messages_total", + cfg.ServiceName, + ), + api.WithDescription( + "The total number of error rabbitmq processed messages", + ), ) if err != nil { return nil, err @@ -170,18 +152,12 @@ func provideCatalogsMetrics( CreateProductRabbitMQMessages: createProductRabbitMQMessages, GetProductByIdGrpcRequests: getProductByIdGrpcRequests, CreateProductGrpcRequests: createProductGrpcRequests, - CreateProductHttpRequests: createProductHttpRequests, DeleteProductRabbitMQMessages: deleteProductRabbitMQMessages, DeleteProductGrpcRequests: deleteProductGrpcRequests, - DeleteProductHttpRequests: deleteProductHttpRequests, ErrorRabbitMQMessages: errorRabbitMQMessages, - GetProductByIdHttpRequests: getProductByIdHttpRequests, - GetProductsHttpRequests: getProductsHttpRequests, SearchProductGrpcRequests: searchProductGrpcRequests, - SearchProductHttpRequests: searchProductHttpRequests, SuccessRabbitMQMessages: successRabbitMQMessages, UpdateProductRabbitMQMessages: updateProductRabbitMQMessages, UpdateProductGrpcRequests: updateProductGrpcRequests, - UpdateProductHttpRequests: updateProductHttpRequests, }, nil } diff --git a/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go new file mode 100644 index 00000000..60ad6c9b --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_configurator.go @@ -0,0 +1,50 @@ +package infrastructure + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + loggingpipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/pipelines" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + metricspipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics/mediatr/pipelines" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + tracingpipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/mediatr/pipelines" + postgrespipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/pipelines" + validationpieline "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/validation/pipeline" + + "github.com/mehdihadeli/go-mediatr" + "gorm.io/gorm" +) + +type InfrastructureConfigurator struct { + contracts.Application +} + +func NewInfrastructureConfigurator( + fxapp contracts.Application, +) *InfrastructureConfigurator { + return &InfrastructureConfigurator{ + Application: fxapp, + } +} + +func (ic *InfrastructureConfigurator) ConfigInfrastructures() { + ic.ResolveFunc( + func(l logger.Logger, tracer tracing.AppTracer, metrics metrics.AppMetrics, db *gorm.DB) error { + err := mediatr.RegisterRequestPipelineBehaviors( + loggingpipelines.NewMediatorLoggingPipeline(l), + validationpieline.NewMediatorValidationPipeline(l), + tracingpipelines.NewMediatorTracingPipeline( + tracer, + tracingpipelines.WithLogger(l), + ), + metricspipelines.NewMediatorMetricsPipeline( + metrics, + metricspipelines.WithLogger(l), + ), + postgrespipelines.NewMediatorTransactionPipeline(l, db), + ) + + return err + }, + ) +} diff --git a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go similarity index 71% rename from internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go rename to internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go index 9e020634..b64b65d1 100644 --- a/internal/services/catalog_write_service/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go +++ b/internal/services/catalogwriteservice/internal/shared/configurations/catalogs/infrastructure/infrastructure_fx.go @@ -2,11 +2,14 @@ package infrastructure import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/migration/goose" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresmessaging" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" rabbitmq2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/rabbitmq" @@ -23,8 +26,9 @@ var Module = fx.Module( core.Module, customEcho.Module, grpc.Module, - gormPostgres.Module, - otel.Module, + postgresgorm.Module, + postgresmessaging.Module, + goose.Module, rabbitmq.ModuleFunc( func() configurations.RabbitMQConfigurationBuilderFuc { return func(builder configurations.RabbitMQConfigurationBuilder) { @@ -33,6 +37,8 @@ var Module = fx.Module( }, ), health.Module, + tracing.Module, + metrics.Module, // Other provides fx.Provide(validator.New), diff --git a/internal/services/catalog_write_service/internal/shared/contracts/catalogs_metrics.go b/internal/services/catalogwriteservice/internal/shared/contracts/catalogs_metrics.go similarity index 66% rename from internal/services/catalog_write_service/internal/shared/contracts/catalogs_metrics.go rename to internal/services/catalogwriteservice/internal/shared/contracts/catalogs_metrics.go index f6701e6a..7db30b63 100644 --- a/internal/services/catalog_write_service/internal/shared/contracts/catalogs_metrics.go +++ b/internal/services/catalogwriteservice/internal/shared/contracts/catalogs_metrics.go @@ -10,12 +10,6 @@ type CatalogsMetrics struct { DeleteProductGrpcRequests metric.Float64Counter GetProductByIdGrpcRequests metric.Float64Counter SearchProductGrpcRequests metric.Float64Counter - CreateProductHttpRequests metric.Float64Counter - UpdateProductHttpRequests metric.Float64Counter - DeleteProductHttpRequests metric.Float64Counter - GetProductByIdHttpRequests metric.Float64Counter - GetProductsHttpRequests metric.Float64Counter - SearchProductHttpRequests metric.Float64Counter SuccessRabbitMQMessages metric.Float64Counter ErrorRabbitMQMessages metric.Float64Counter CreateProductRabbitMQMessages metric.Float64Counter diff --git a/internal/services/catalogwriteservice/internal/shared/data/data_fx.go b/internal/services/catalogwriteservice/internal/shared/data/data_fx.go new file mode 100644 index 00000000..dfbcd9bd --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/data/data_fx.go @@ -0,0 +1,18 @@ +package data + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data/dbcontext" + + "go.uber.org/fx" +) + +// https://uber-go.github.io/fx/modules.html +var Module = fx.Module( + "datafx", + // - order is not important in provide + // - provide can have parameter and will resolve if registered + // - execute its func only if it requested + fx.Provide( + dbcontext.NewCatalogsDBContext, + ), +) diff --git a/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext.go b/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext.go new file mode 100644 index 00000000..075eea51 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext.go @@ -0,0 +1,20 @@ +package dbcontext + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + + "gorm.io/gorm" +) + +type CatalogsGormDBContext struct { + // our dbcontext base + contracts.GormDBContext +} + +func NewCatalogsDBContext(db *gorm.DB) *CatalogsGormDBContext { + // initialize base GormContext + c := &CatalogsGormDBContext{GormDBContext: gormdbcontext.NewGormDBContext(db)} + + return c +} diff --git a/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext_test.go b/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext_test.go new file mode 100644 index 00000000..dcb5ab58 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/data/dbcontext/catalogs_dbcontext_test.go @@ -0,0 +1,289 @@ +//go:build unit +// +build unit + +package dbcontext + +import ( + "context" + "os" + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/fxlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/scopes" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mappings" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/suite" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" + "gorm.io/gorm" +) + +// Define the suite +type DBContextTestSuite struct { + suite.Suite + items []*datamodel.ProductDataModel + dbContext *CatalogsGormDBContext + app *fxtest.App + dbFilePath string +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestDBContextTestSuite(t *testing.T) { + suite.Run(t, new(DBContextTestSuite)) +} + +func (s *DBContextTestSuite) Test_FindProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + p, err := gormdbcontext.FindModelByID[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + s.Require().NotNil(p) + + s.Assert().Equal(p.Id, id) +} + +func (s *DBContextTestSuite) Test_ExistsProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + exist := gormdbcontext.Exists[*datamodel.ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + s.Require().True(exist) +} + +func (s *DBContextTestSuite) Test_NoneExistsProductByID() { + s.Require().NotNil(s.dbContext) + + id := uuid.NewV4() + + exist := gormdbcontext.Exists[*datamodel.ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + + s.Require().False(exist) +} + +func (s *DBContextTestSuite) Test_DeleteProductByID() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + err := gormdbcontext.DeleteDataModelByID[*datamodel.ProductDataModel]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + p, err := gormdbcontext.FindModelByID[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().Error(err) + s.Require().Nil(p) + + // https://gorm.io/docs/delete.html#Find-soft-deleted-records + var softDeletedProduct *datamodel.ProductDataModel + s.dbContext.DB().Scopes(scopes.FilterAllItemsWithSoftDeleted).First(&softDeletedProduct, id) + s.Require().NotNil(softDeletedProduct) + + var deletedCount int64 + var allCount int64 + + // https://gorm.io/docs/advanced_query.html#Count + s.dbContext.DB().Model(&datamodel.ProductDataModel{}).Scopes(scopes.FilterAllItemsWithSoftDeleted).Count(&allCount) + s.Equal(allCount, int64(2)) + + s.dbContext.DB().Model(&datamodel.ProductDataModel{}).Scopes(scopes.SoftDeleted).Count(&deletedCount) + s.Equal(deletedCount, int64(1)) +} + +func (s *DBContextTestSuite) Test_CreateProduct() { + s.Require().NotNil(s.dbContext) + + item := &models.Product{ + Id: uuid.NewV4(), + Name: gofakeit.Name(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + } + + res, err := gormdbcontext.AddModel[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + item, + ) + s.Require().NoError(err) + + p, err := gormdbcontext.FindModelByID[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + item.Id, + ) + s.Require().NoError(err) + s.Require().NotNil(p) + + s.Assert().Equal(p.Id, item.Id) + s.Assert().Equal(p.Id, res.Id) +} + +func (s *DBContextTestSuite) Test_UpdateProduct() { + s.Require().NotNil(s.dbContext) + + id := s.items[0].Id + + p, err := gormdbcontext.FindModelByID[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + newName := gofakeit.Name() + item := p + item.Name = newName + + res, err := gormdbcontext.UpdateModel[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + item, + ) + s.Require().NoError(err) + + p2, err := gormdbcontext.FindModelByID[*datamodel.ProductDataModel, *models.Product]( + context.Background(), + s.dbContext, + id, + ) + s.Require().NoError(err) + + s.Assert().Equal(item.Name, p2.Name) + s.Assert().Equal(res.Name, p2.Name) +} + +// TestSuite Hooks + +func (s *DBContextTestSuite) SetupTest() { + err := mappings.ConfigureProductsMappings() + s.Require().NoError(err) + + var gormDBContext *CatalogsGormDBContext + var gormOptions *gormPostgres.GormOptions + + app := fxtest.New( + s.T(), + config.ModuleFunc(environment.Test), + zap.Module, + fxlog.FxLogger, + gormPostgres.Module, + fx.Decorate( + func(cfg *gormPostgres.GormOptions) (*gormPostgres.GormOptions, error) { + // using sql-lite with a database file + cfg.UseSQLLite = true + + return cfg, nil + }, + ), + fx.Provide(NewCatalogsDBContext), + fx.Populate(&gormDBContext), + fx.Populate(&gormOptions), + ).RequireStart() + + s.app = app + s.dbContext = gormDBContext + s.dbFilePath = gormOptions.Dns() + + s.initDB() +} + +func (s *DBContextTestSuite) TearDownTest() { + err := s.cleanupDB() + s.Require().NoError(err) + + mapper.ClearMappings() + + s.app.RequireStop() +} + +func (s *DBContextTestSuite) initDB() { + err := migrateGorm(s.dbContext.DB()) + s.Require().NoError(err) + + products, err := seedData(s.dbContext.DB()) + s.Require().NoError(err) + + s.items = products +} + +func (s *DBContextTestSuite) cleanupDB() error { + sqldb, _ := s.dbContext.DB().DB() + e := sqldb.Close() + s.Require().NoError(e) + + // removing sql-lite file + err := os.Remove(s.dbFilePath) + + return err +} + +func migrateGorm(db *gorm.DB) error { + err := db.AutoMigrate(&datamodel.ProductDataModel{}) + if err != nil { + return err + } + + return nil +} + +func seedData(gormDB *gorm.DB) ([]*datamodel.ProductDataModel, error) { + products := []*datamodel.ProductDataModel{ + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + } + + // seed data + err := gormDB.CreateInBatches(products, len(products)).Error + if err != nil { + return nil, errors.Wrap(err, "error in seed database") + } + + return products, nil +} diff --git a/internal/services/catalogwriteservice/internal/shared/data/scopes/scopes.go b/internal/services/catalogwriteservice/internal/shared/data/scopes/scopes.go new file mode 100644 index 00000000..76877700 --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/data/scopes/scopes.go @@ -0,0 +1,3 @@ +package scopes + +// After scopes, we should have a runner function like Find, Update, Delete diff --git a/internal/services/catalog_write_service/internal/shared/grpc/genproto/products.pb.go b/internal/services/catalogwriteservice/internal/shared/grpc/genproto/products.pb.go similarity index 94% rename from internal/services/catalog_write_service/internal/shared/grpc/genproto/products.pb.go rename to internal/services/catalogwriteservice/internal/shared/grpc/genproto/products.pb.go index 78f454d3..579f3c31 100644 --- a/internal/services/catalog_write_service/internal/shared/grpc/genproto/products.pb.go +++ b/internal/services/catalogwriteservice/internal/shared/grpc/genproto/products.pb.go @@ -2,16 +2,17 @@ // versions: // protoc-gen-go v1.26.0 // protoc v4.23.4 -// source: catalog_write_service/products.proto +// source: catalogwriteservice/products.proto package products_service import ( + reflect "reflect" + sync "sync" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" ) const ( @@ -26,8 +27,8 @@ type Product struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProductId string `protobuf:"bytes,1,opt,name=ProductId,proto3" json:"ProductId,omitempty"` - Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` + ProductId string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=ShortTypeName,proto3" json:"ShortTypeName,omitempty"` Description string `protobuf:"bytes,3,opt,name=Description,proto3" json:"Description,omitempty"` Price float64 `protobuf:"fixed64,4,opt,name=Price,proto3" json:"Price,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` @@ -113,7 +114,7 @@ type CreateProductReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=ShortTypeName,proto3" json:"ShortTypeName,omitempty"` Description string `protobuf:"bytes,2,opt,name=Description,proto3" json:"Description,omitempty"` Price float64 `protobuf:"fixed64,3,opt,name=Price,proto3" json:"Price,omitempty"` } @@ -176,7 +177,7 @@ type CreateProductRes struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProductId string `protobuf:"bytes,1,opt,name=ProductId,proto3" json:"ProductId,omitempty"` + ProductId string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"` } func (x *CreateProductRes) Reset() { @@ -223,8 +224,8 @@ type UpdateProductReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProductId string `protobuf:"bytes,1,opt,name=ProductId,proto3" json:"ProductId,omitempty"` - Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` + ProductId string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=ShortTypeName,proto3" json:"ShortTypeName,omitempty"` Description string `protobuf:"bytes,3,opt,name=Description,proto3" json:"Description,omitempty"` Price float64 `protobuf:"fixed64,4,opt,name=Price,proto3" json:"Price,omitempty"` } @@ -332,7 +333,7 @@ type GetProductByIdReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProductId string `protobuf:"bytes,1,opt,name=ProductId,proto3" json:"ProductId,omitempty"` + ProductId string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"` } func (x *GetProductByIdReq) Reset() { @@ -505,17 +506,20 @@ func file_catalog_write_service_products_proto_rawDescGZIP() []byte { return file_catalog_write_service_products_proto_rawDescData } -var file_catalog_write_service_products_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_catalog_write_service_products_proto_goTypes = []interface{}{ - (*Product)(nil), // 0: products_service.Product - (*CreateProductReq)(nil), // 1: products_service.CreateProductReq - (*CreateProductRes)(nil), // 2: products_service.CreateProductRes - (*UpdateProductReq)(nil), // 3: products_service.UpdateProductReq - (*UpdateProductRes)(nil), // 4: products_service.UpdateProductRes - (*GetProductByIdReq)(nil), // 5: products_service.GetProductByIdReq - (*GetProductByIdRes)(nil), // 6: products_service.GetProductByIdRes - (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp -} +var ( + file_catalog_write_service_products_proto_msgTypes = make([]protoimpl.MessageInfo, 7) + file_catalog_write_service_products_proto_goTypes = []interface{}{ + (*Product)(nil), // 0: products_service.Product + (*CreateProductReq)(nil), // 1: products_service.CreateProductReq + (*CreateProductRes)(nil), // 2: products_service.CreateProductRes + (*UpdateProductReq)(nil), // 3: products_service.UpdateProductReq + (*UpdateProductRes)(nil), // 4: products_service.UpdateProductRes + (*GetProductByIdReq)(nil), // 5: products_service.GetProductByIdReq + (*GetProductByIdRes)(nil), // 6: products_service.GetProductByIdRes + (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp + } +) + var file_catalog_write_service_products_proto_depIdxs = []int32{ 7, // 0: products_service.Product.CreatedAt:type_name -> google.protobuf.Timestamp 7, // 1: products_service.Product.UpdatedAt:type_name -> google.protobuf.Timestamp diff --git a/internal/services/catalog_write_service/internal/shared/grpc/genproto/products_grpc.pb.go b/internal/services/catalogwriteservice/internal/shared/grpc/genproto/products_grpc.pb.go similarity index 98% rename from internal/services/catalog_write_service/internal/shared/grpc/genproto/products_grpc.pb.go rename to internal/services/catalogwriteservice/internal/shared/grpc/genproto/products_grpc.pb.go index e8174210..e509ba2d 100644 --- a/internal/services/catalog_write_service/internal/shared/grpc/genproto/products_grpc.pb.go +++ b/internal/services/catalogwriteservice/internal/shared/grpc/genproto/products_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.3.0 // - protoc v4.23.4 -// source: catalog_write_service/products.proto +// source: catalogwriteservice/products.proto package products_service @@ -177,5 +177,5 @@ var ProductsService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "catalog_write_service/products.proto", + Metadata: "catalogwriteservice/products.proto", } diff --git a/internal/services/catalog_write_service/internal/shared/grpc/product_grpc_service_server.go b/internal/services/catalogwriteservice/internal/shared/grpc/product_grpc_service_server.go similarity index 92% rename from internal/services/catalog_write_service/internal/shared/grpc/product_grpc_service_server.go rename to internal/services/catalogwriteservice/internal/shared/grpc/product_grpc_service_server.go index 9c7d2500..4531b406 100644 --- a/internal/services/catalog_write_service/internal/shared/grpc/product_grpc_service_server.go +++ b/internal/services/catalogwriteservice/internal/shared/grpc/product_grpc_service_server.go @@ -4,15 +4,15 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" - createProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - createProductDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - getProductByIdDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/dtos" - getProductByIdQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - updateProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" + createProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1" + createProductDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + getProductByIdQueryV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1" + getProductByIdDtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos" + updateProductCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/contracts" productsService "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" @@ -39,7 +39,10 @@ func NewProductGrpcService( catalogsMetrics *contracts.CatalogsMetrics, logger logger.Logger, ) *ProductGrpcServiceServer { - return &ProductGrpcServiceServer{catalogsMetrics: catalogsMetrics, logger: logger} + return &ProductGrpcServiceServer{ + catalogsMetrics: catalogsMetrics, + logger: logger, + } } func (s *ProductGrpcServiceServer) CreateProduct( @@ -84,12 +87,14 @@ func (s *ProductGrpcServiceServer) CreateProduct( command.ProductID, err, ), - logger.Fields{"ProductId": command.ProductID}, + logger.Fields{"Id": command.ProductID}, ) return nil, err } - return &productsService.CreateProductRes{ProductId: result.ProductID.String()}, nil + return &productsService.CreateProductRes{ + ProductId: result.ProductID.String(), + }, nil } func (s *ProductGrpcServiceServer) UpdateProduct( @@ -146,7 +151,7 @@ func (s *ProductGrpcServiceServer) UpdateProduct( command.ProductID, err, ), - logger.Fields{"ProductId": command.ProductID}, + logger.Fields{"Id": command.ProductID}, ) return nil, err } @@ -211,7 +216,7 @@ func (s *ProductGrpcServiceServer) GetProductById( query.ProductID, err, ), - logger.Fields{"ProductId": query.ProductID}, + logger.Fields{"Id": query.ProductID}, ) return nil, err } diff --git a/internal/services/catalog_write_service/internal/shared/test_fixtures/integration/integration_test_fixture.go b/internal/services/catalogwriteservice/internal/shared/testfixtures/integration/integration_test_fixture.go similarity index 77% rename from internal/services/catalog_write_service/internal/shared/test_fixtures/integration/integration_test_fixture.go rename to internal/services/catalogwriteservice/internal/shared/testfixtures/integration/integration_test_fixture.go index 5eb2ced3..b68cf4b7 100644 --- a/internal/services/catalog_write_service/internal/shared/test_fixtures/integration/integration_test_fixture.go +++ b/internal/services/catalogwriteservice/internal/shared/testfixtures/integration/integration_test_fixture.go @@ -5,17 +5,17 @@ import ( "testing" "time" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/gorm_postgres" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" + fxcontracts "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" + gormPostgres "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" config2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/testfixture" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/app/test" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data/dbcontext" productsService "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" "emperror.dev/errors" @@ -32,15 +32,14 @@ type IntegrationTestSharedFixture struct { Cfg *config.AppOptions Log logger.Logger Bus bus.Bus - CatalogUnitOfWorks data.CatalogUnitOfWork - ProductRepository data.ProductRepository - Container contracts.Container + CatalogsDBContext *dbcontext.CatalogsGormDBContext + Container fxcontracts.Container DbCleaner dbcleaner.DbCleaner RabbitmqCleaner *rabbithole.Client rabbitmqOptions *config2.RabbitmqOptions Gorm *gorm.DB BaseAddress string - Items []*models.Product + Items []*datamodel.ProductDataModel ProductServiceClient productsService.ProductsServiceClient } @@ -65,8 +64,7 @@ func NewIntegrationTestSharedFixture( Container: result.Container, Cfg: result.Cfg, RabbitmqCleaner: rmqc, - ProductRepository: result.ProductRepository, - CatalogUnitOfWorks: result.CatalogUnitOfWorks, + CatalogsDBContext: result.CatalogsDBContext, Bus: result.Bus, rabbitmqOptions: result.RabbitmqOptions, Gorm: result.Gorm, @@ -74,16 +72,15 @@ func NewIntegrationTestSharedFixture( ProductServiceClient: result.ProductServiceClient, } - migrateDatabase(result) - return shared } -func (i *IntegrationTestSharedFixture) InitializeTest() { - i.Log.Info("InitializeTest started") +func (i *IntegrationTestSharedFixture) SetupTest() { + i.Log.Info("SetupTest started") - // seed data in each test - res, err := seedData(i.Gorm) + // migration will do in app configuration + // seed data for our tests - app seed doesn't run in test environment + res, err := seedDataManually(i.Gorm) if err != nil { i.Log.Error(errors.WrapIf(err, "error in seeding data in postgres")) } @@ -91,8 +88,8 @@ func (i *IntegrationTestSharedFixture) InitializeTest() { i.Items = res } -func (i *IntegrationTestSharedFixture) DisposeTest() { - i.Log.Info("DisposeTest started") +func (i *IntegrationTestSharedFixture) TearDownTest() { + i.Log.Info("TearDownTest started") // cleanup test containers with their hooks if err := i.cleanupRabbitmqData(); err != nil { @@ -139,17 +136,17 @@ func (i *IntegrationTestSharedFixture) cleanupPostgresData() error { return nil } -func seedData(gormDB *gorm.DB) ([]*models.Product, error) { - products := []*models.Product{ +func seedDataManually(gormDB *gorm.DB) ([]*datamodel.ProductDataModel, error) { + products := []*datamodel.ProductDataModel{ { - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Name: gofakeit.Name(), CreatedAt: time.Now(), Description: gofakeit.AdjectiveDescriptive(), Price: gofakeit.Price(100, 1000), }, { - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Name: gofakeit.Name(), CreatedAt: time.Now(), Description: gofakeit.AdjectiveDescriptive(), @@ -157,8 +154,6 @@ func seedData(gormDB *gorm.DB) ([]*models.Product, error) { }, } - // migration will do in app configuration - // seed data err := gormDB.CreateInBatches(products, len(products)).Error if err != nil { return nil, errors.Wrap(err, "error in seed database") @@ -167,13 +162,7 @@ func seedData(gormDB *gorm.DB) ([]*models.Product, error) { return products, nil } -func seedAndMigration(gormDB *gorm.DB) ([]*models.Product, error) { - // migration - err := gormDB.AutoMigrate(models.Product{}) - if err != nil { - return nil, errors.WrapIf(err, "error in seed database") - } - +func seedDataWithFixture(gormDB *gorm.DB) ([]*datamodel.ProductDataModel, error) { db, err := gormDB.DB() if err != nil { return nil, errors.WrapIf(err, "error in seed database") @@ -208,17 +197,11 @@ func seedAndMigration(gormDB *gorm.DB) ([]*models.Product, error) { return nil, errors.WrapIf(err, "error in seed database") } - result, err := gormPostgres.Paginate[*models.Product]( + result, err := gormPostgres.Paginate[*datamodel.ProductDataModel, *datamodel.ProductDataModel]( context.Background(), utils.NewListQuery(10, 1), gormDB, ) - return result.Items, nil -} -func migrateDatabase(result *test.TestAppResult) { - err := result.PostgresMigrationRunner.Up(context.Background(), 0) - if err != nil { - result.Logger.Fatalf("error in catalog_service migration, err: %s", err) - } + return result.Items, nil } diff --git a/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest/unit_test_fixture.go b/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest/unit_test_fixture.go new file mode 100644 index 00000000..03b4889f --- /dev/null +++ b/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest/unit_test_fixture.go @@ -0,0 +1,210 @@ +package unittest + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/mocks" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + defaultLogger "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/defaultlogger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/external/gromlog" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/helpers/gormextensions" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/config" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/configurations/mappings" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/data/dbcontext" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + "github.com/glebarez/sqlite" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + "go.opentelemetry.io/otel/trace" + "gorm.io/gorm" +) + +type UnitTestSharedFixture struct { + Cfg *config.AppOptions + Log logger.Logger + suite.Suite + Products []*datamodel.ProductDataModel + Bus *mocks.Bus + Tracer trace.Tracer + CatalogDBContext *dbcontext.CatalogsGormDBContext + Ctx context.Context + dbFilePath string + dbFileName string +} + +func NewUnitTestSharedFixture(t *testing.T) *UnitTestSharedFixture { + // we could use EmptyLogger if we don't want to log anything + log := defaultLogger.GetLogger() + cfg := &config.AppOptions{} + + // empty tracer, just for testing + nopetracer := trace.NewNoopTracerProvider() + testTracer := nopetracer.Tracer("test_tracer") + + unit := &UnitTestSharedFixture{ + Cfg: cfg, + Log: log, + Tracer: testTracer, + dbFileName: "sqlite.db", + } + + return unit +} + +func (c *UnitTestSharedFixture) BeginTx() { + c.Log.Info("starting transaction") + // seems when we `Begin` a transaction on gorm.DB (with SQLLite in-memory) our previous gormDB before transaction will remove and the new gormDB with tx will go on the memory + tx := c.CatalogDBContext.DB().Begin() + gormContext := gormextensions.SetTxToContext(c.Ctx, tx) + c.Ctx = gormContext + + //// works on both transaction and none-transactional gormdbcontext + //var productData []*datamodel.ProductDataModel + //var productData2 []*datamodel.ProductDataModel + // + //s := c.CatalogDBContext.Find(&productData).Error + //s2 := tx.Find(&productData2).Error +} + +func (c *UnitTestSharedFixture) CommitTx() { + tx := gormextensions.GetTxFromContextIfExists(c.Ctx) + if tx != nil { + c.Log.Info("committing transaction") + tx.Commit() + } +} + +/// Shared Hooks + +func (c *UnitTestSharedFixture) SetupSuite() { + // this fix root working directory problem in our test environment inner our fixture + environment.FixProjectRootWorkingDirectoryPath() + projectRootDir := environment.GetProjectRootWorkingDirectory() + + c.dbFilePath = filepath.Join(projectRootDir, c.dbFileName) +} + +func (c *UnitTestSharedFixture) TearDownSuite() { +} + +func (c *UnitTestSharedFixture) SetupTest() { + ctx := context.Background() + c.Ctx = ctx + + c.setupBus() + + c.setupDB() + + err := mappings.ConfigureProductsMappings() + c.Require().NoError(err) +} + +func (c *UnitTestSharedFixture) TearDownTest() { + err := c.cleanupDB() + c.Require().NoError(err) + + mapper.ClearMappings() +} + +func (c *UnitTestSharedFixture) setupBus() { + // create new mocks + bus := &mocks.Bus{} + + bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + c.Bus = bus +} + +func (c *UnitTestSharedFixture) setupDB() { + dbContext := c.createSQLLiteDBContext() + c.CatalogDBContext = dbContext + + c.initDB(dbContext) +} + +func (c *UnitTestSharedFixture) createSQLLiteDBContext() *dbcontext.CatalogsGormDBContext { + // https://gorm.io/docs/connecting_to_the_database.html#SQLite + // https://github.com/glebarez/sqlite + // https://www.connectionstrings.com/sqlite/ + gormSQLLiteDB, err := gorm.Open( + sqlite.Open(c.dbFilePath), + &gorm.Config{ + Logger: gromlog.NewGormCustomLogger(defaultLogger.GetLogger()), + }) + c.Require().NoError(err) + + dbContext := dbcontext.NewCatalogsDBContext(gormSQLLiteDB) + + return dbContext +} + +func (c *UnitTestSharedFixture) initDB(dbContext *dbcontext.CatalogsGormDBContext) { + // migrations for our database + err := migrateGorm(dbContext) + c.Require().NoError(err) + + // seed data for our tests + items, err := seedDataManually(dbContext) + c.Require().NoError(err) + + c.Products = items +} + +func (c *UnitTestSharedFixture) cleanupDB() error { + sqldb, _ := c.CatalogDBContext.DB().DB() + e := sqldb.Close() + c.Require().NoError(e) + + // removing sql-lite file + err := os.Remove(c.dbFilePath) + + return err +} + +func migrateGorm(dbContext *dbcontext.CatalogsGormDBContext) error { + err := dbContext.DB().AutoMigrate(&datamodel.ProductDataModel{}) + if err != nil { + return err + } + + return nil +} + +func seedDataManually( + dbContext *dbcontext.CatalogsGormDBContext, +) ([]*datamodel.ProductDataModel, error) { + products := []*datamodel.ProductDataModel{ + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + { + Id: uuid.NewV4(), + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(100, 1000), + }, + } + + // seed data + err := dbContext.DB().CreateInBatches(products, len(products)).Error + if err != nil { + return nil, errors.Wrap(err, "error in seed database") + } + + return products, nil +} diff --git a/internal/services/catalog_write_service/mocks/CatalogContext.go b/internal/services/catalogwriteservice/mocks/CatalogContext.go similarity index 97% rename from internal/services/catalog_write_service/mocks/CatalogContext.go rename to internal/services/catalogwriteservice/mocks/CatalogContext.go index 4d0c1ce0..7aa905db 100644 --- a/internal/services/catalog_write_service/mocks/CatalogContext.go +++ b/internal/services/catalogwriteservice/mocks/CatalogContext.go @@ -3,7 +3,8 @@ package mocks import ( - data "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" + data "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts" + mock "github.com/stretchr/testify/mock" ) diff --git a/internal/services/catalog_write_service/mocks/ProductRepository.go b/internal/services/catalogwriteservice/mocks/ProductRepository.go similarity index 99% rename from internal/services/catalog_write_service/mocks/ProductRepository.go rename to internal/services/catalogwriteservice/mocks/ProductRepository.go index e67c2b85..ff299e33 100644 --- a/internal/services/catalog_write_service/mocks/ProductRepository.go +++ b/internal/services/catalogwriteservice/mocks/ProductRepository.go @@ -5,13 +5,11 @@ package mocks import ( context "context" - mock "github.com/stretchr/testify/mock" - - models "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - utils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + models "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" uuid "github.com/satori/go.uuid" + mock "github.com/stretchr/testify/mock" ) // ProductRepository is an autogenerated mock type for the ProductRepository type diff --git a/internal/services/catalog_write_service/mocks/ProductsServiceClient.go b/internal/services/catalogwriteservice/mocks/ProductsServiceClient.go similarity index 100% rename from internal/services/catalog_write_service/mocks/ProductsServiceClient.go rename to internal/services/catalogwriteservice/mocks/ProductsServiceClient.go diff --git a/internal/services/catalog_write_service/mocks/ProductsServiceServer.go b/internal/services/catalogwriteservice/mocks/ProductsServiceServer.go similarity index 100% rename from internal/services/catalog_write_service/mocks/ProductsServiceServer.go rename to internal/services/catalogwriteservice/mocks/ProductsServiceServer.go diff --git a/internal/services/catalog_write_service/mocks/UnsafeProductsServiceServer.go b/internal/services/catalogwriteservice/mocks/UnsafeProductsServiceServer.go similarity index 100% rename from internal/services/catalog_write_service/mocks/UnsafeProductsServiceServer.go rename to internal/services/catalogwriteservice/mocks/UnsafeProductsServiceServer.go diff --git a/internal/services/catalog_write_service/readme.md b/internal/services/catalogwriteservice/readme.md similarity index 100% rename from internal/services/catalog_write_service/readme.md rename to internal/services/catalogwriteservice/readme.md diff --git a/internal/services/catalog_write_service/revive-config.toml b/internal/services/catalogwriteservice/revive-config.toml similarity index 100% rename from internal/services/catalog_write_service/revive-config.toml rename to internal/services/catalogwriteservice/revive-config.toml diff --git a/internal/services/catalog_write_service/taskfile.yml b/internal/services/catalogwriteservice/taskfile.yml similarity index 100% rename from internal/services/catalog_write_service/taskfile.yml rename to internal/services/catalogwriteservice/taskfile.yml diff --git a/internal/services/catalog_write_service/taskfile_db.yml b/internal/services/catalogwriteservice/taskfile_db.yml similarity index 100% rename from internal/services/catalog_write_service/taskfile_db.yml rename to internal/services/catalogwriteservice/taskfile_db.yml diff --git a/internal/services/catalog_write_service/taskfile_test.yml b/internal/services/catalogwriteservice/taskfile_test.yml similarity index 100% rename from internal/services/catalog_write_service/taskfile_test.yml rename to internal/services/catalogwriteservice/taskfile_test.yml diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/creating_product/v1/create_product_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/creatingproduct/v1/create_product_test.go similarity index 91% rename from internal/services/catalog_write_service/test/end_to_end/products/features/creating_product/v1/create_product_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/creatingproduct/v1/create_product_test.go index 530611e3..33ad5dd6 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/creating_product/v1/create_product_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/creatingproduct/v1/create_product_test.go @@ -8,8 +8,8 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/brianvoe/gofakeit/v6" "github.com/gavv/httpexpect/v2" @@ -36,12 +36,12 @@ var _ = Describe("CreateProduct Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the create product API with valid input diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/deleting_product/v1/delete_product_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/deletingproduct/v1/delete_product_test.go similarity index 91% rename from internal/services/catalog_write_service/test/end_to_end/products/features/deleting_product/v1/delete_product_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/deletingproduct/v1/delete_product_test.go index 1ce96a17..42d3f788 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/deleting_product/v1/delete_product_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/deletingproduct/v1/delete_product_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/gavv/httpexpect/v2" uuid "github.com/satori/go.uuid" @@ -35,14 +35,14 @@ var _ = Describe("Delete Product Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() - id = integrationFixture.Items[0].ProductId + id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the delete product API with valid input diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/gettingproductbyid/v1/get_product_by_id_test.go similarity index 91% rename from internal/services/catalog_write_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/gettingproductbyid/v1/get_product_by_id_test.go index 509ce3ab..256ca88c 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/getting_product_by_id/v1/get_product_by_id_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/gettingproductbyid/v1/get_product_by_id_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/gavv/httpexpect/v2" uuid "github.com/satori/go.uuid" @@ -35,14 +35,14 @@ var _ = Describe("Get Product By Id Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() - id = integrationFixture.Items[0].ProductId + id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the get product by ID API with a valid ID diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/getting_products/v1/get_products_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/gettingproducts/v1/get_products_test.go similarity index 89% rename from internal/services/catalog_write_service/test/end_to_end/products/features/getting_products/v1/get_products_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/gettingproducts/v1/get_products_test.go index f125635e..bd6b582a 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/getting_products/v1/get_products_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/gettingproducts/v1/get_products_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/gavv/httpexpect/v2" @@ -31,12 +31,12 @@ var _ = Describe("Get All Products Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the get all products API diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/searching_product/v1/search_products_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/searchingproduct/v1/search_products_test.go similarity index 90% rename from internal/services/catalog_write_service/test/end_to_end/products/features/searching_product/v1/search_products_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/searchingproduct/v1/search_products_test.go index 521374b8..461ff801 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/searching_product/v1/search_products_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/searchingproduct/v1/search_products_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/gavv/httpexpect/v2" @@ -31,12 +31,12 @@ var _ = Describe("Search Products Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the search products API diff --git a/internal/services/catalog_write_service/test/end_to_end/products/features/updating_product/v1/update_product_test.go b/internal/services/catalogwriteservice/test/endtoend/products/features/updatingproduct/v1/update_product_test.go similarity index 90% rename from internal/services/catalog_write_service/test/end_to_end/products/features/updating_product/v1/update_product_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/features/updatingproduct/v1/update_product_test.go index e8db665e..41d46e15 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/features/updating_product/v1/update_product_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/features/updatingproduct/v1/update_product_test.go @@ -8,8 +8,8 @@ import ( "net/http" "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/brianvoe/gofakeit/v6" "github.com/gavv/httpexpect/v2" @@ -38,13 +38,13 @@ var _ = Describe("UpdateProductE2ETest Suite", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() - id = integrationFixture.Items[0].ProductId + integrationFixture.SetupTest() + id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the update product API with valid input diff --git a/internal/services/catalog_write_service/test/end_to_end/products/grpc/product_grpc_service_server_test.go b/internal/services/catalogwriteservice/test/endtoend/products/grpc/product_grpc_service_server_test.go similarity index 88% rename from internal/services/catalog_write_service/test/end_to_end/products/grpc/product_grpc_service_server_test.go rename to internal/services/catalogwriteservice/test/endtoend/products/grpc/product_grpc_service_server_test.go index 0f543601..0bc9e32f 100644 --- a/internal/services/catalog_write_service/test/end_to_end/products/grpc/product_grpc_service_server_test.go +++ b/internal/services/catalogwriteservice/test/endtoend/products/grpc/product_grpc_service_server_test.go @@ -8,7 +8,7 @@ import ( "testing" productService "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/grpc/genproto" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -35,14 +35,14 @@ var _ = Describe("Product Grpc Service Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() - id = integrationFixture.Items[0].ProductId + id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing the creation of a product with valid data in the database @@ -50,7 +50,7 @@ var _ = Describe("Product Grpc Service Feature", func() { // "When" step When("A request is made to create a product with valid data", func() { // "Then" step - It("Should return a non-empty ProductId", func() { + It("Should return a non-empty Id", func() { // Create a gRPC request with valid data request := &productService.CreateProductReq{ Price: gofakeit.Price(100, 1000), @@ -72,7 +72,7 @@ var _ = Describe("Product Grpc Service Feature", func() { // "When" step When("A request is made to retrieve data with a valid ID", func() { // "Then" step - It("Should return data with a matching ProductId", func() { + It("Should return data with a matching Id", func() { // Make the gRPC request to retrieve data by ID res, err := integrationFixture.ProductServiceClient.GetProductById( ctx, diff --git a/internal/services/catalog_write_service/test/integration/products/data/repositories/pg_product_repository_integration_test.go b/internal/services/catalogwriteservice/test/integration/products/data/repositories/pg_product_repository_integration_test.go similarity index 91% rename from internal/services/catalog_write_service/test/integration/products/data/repositories/pg_product_repository_integration_test.go rename to internal/services/catalogwriteservice/test/integration/products/data/repositories/pg_product_repository_integration_test.go index 6f7f4d3a..332b09c2 100644 --- a/internal/services/catalog_write_service/test/integration/products/data/repositories/pg_product_repository_integration_test.go +++ b/internal/services/catalogwriteservice/test/integration/products/data/repositories/pg_product_repository_integration_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -42,14 +42,14 @@ var _ = Describe("Product Repository Suite", func() { _ = BeforeEach(func() { By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() - id = integrationFixture.Items[0].ProductId + id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) _ = BeforeSuite(func() { @@ -76,7 +76,7 @@ var _ = Describe("Product Repository Suite", func() { product = &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), } @@ -97,18 +97,18 @@ var _ = Describe("Product Repository Suite", func() { Expect(createdProduct).NotTo(BeNil()) }) - It("Should have the same ProductId as the input product", func() { - Expect(createdProduct.ProductId).To(Equal(product.ProductId)) + It("Should have the same Id as the input product", func() { + Expect(createdProduct.Id).To(Equal(product.Id)) }) It("Should be able to retrieve the created product from the database", func() { retrievedProduct, err := integrationFixture.ProductRepository.GetProductById( ctx, - createdProduct.ProductId, + createdProduct.Id, ) Expect(err).NotTo(HaveOccurred()) Expect(retrievedProduct).NotTo(BeNil()) - Expect(retrievedProduct.ProductId).To(Equal(createdProduct.ProductId)) + Expect(retrievedProduct.ProductId).To(Equal(createdProduct.Id)) }) }) }) @@ -125,7 +125,7 @@ var _ = Describe("Product Repository Suite", func() { When("UpdateProduct function of ProductRepository executed", func() { BeforeEach(func() { // Update the name of the existing product - existingProduct.Name = "Updated Product Name" + existingProduct.Name = "Updated Product ShortTypeName" _, err = integrationFixture.ProductRepository.UpdateProduct(ctx, existingProduct) }) @@ -137,11 +137,11 @@ var _ = Describe("Product Repository Suite", func() { It("Should be able to retrieve the updated product from the database", func() { updatedProduct, err = integrationFixture.ProductRepository.GetProductById( ctx, - existingProduct.ProductId, + existingProduct.Id, ) Expect(err).To(BeNil()) Expect(updatedProduct).NotTo(BeNil()) - Expect(updatedProduct.Name).To(Equal("Updated Product Name")) + Expect(updatedProduct.Name).To(Equal("Updated Product ShortTypeName")) // You can add more assertions to validate other properties of the updated product }) }) @@ -196,7 +196,7 @@ var _ = Describe("Product Repository Suite", func() { }) It("should retrieve correct data from database by Id", func() { - Expect(existingProduct.ProductId).To(Equal(id)) + Expect(existingProduct.Id).To(Equal(id)) }) }) }) diff --git a/internal/services/catalog_write_service/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go b/internal/services/catalogwriteservice/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go similarity index 94% rename from internal/services/catalog_write_service/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go rename to internal/services/catalogwriteservice/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go index 89769b07..5853b6a9 100644 --- a/internal/services/catalog_write_service/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go +++ b/internal/services/catalogwriteservice/test/integration/products/data/uow/catalogs_unit_of_work_integration_test.go @@ -9,9 +9,9 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts/data" + data2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "emperror.dev/errors" "github.com/brianvoe/gofakeit/v6" @@ -40,12 +40,12 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { _ = BeforeEach(func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" step for testing a UnitOfWork action that should roll back on error @@ -58,7 +58,7 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), }) @@ -87,7 +87,7 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), }) @@ -119,7 +119,7 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), }) @@ -129,7 +129,7 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), }) @@ -160,7 +160,7 @@ var _ = Describe("CatalogsUnitOfWork Feature", func() { &models.Product{ Name: gofakeit.Name(), Description: gofakeit.AdjectiveDescriptive(), - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Price: gofakeit.Price(100, 1000), CreatedAt: time.Now(), }) diff --git a/internal/services/catalogwriteservice/test/integration/products/features/creatingproduct/v1/create_product_test.go b/internal/services/catalogwriteservice/test/integration/products/features/creatingproduct/v1/create_product_test.go new file mode 100644 index 00000000..c561502b --- /dev/null +++ b/internal/services/catalogwriteservice/test/integration/products/features/creatingproduct/v1/create_product_test.go @@ -0,0 +1,243 @@ +//go:build integration +// +build integration + +package v1 + +import ( + "context" + "testing" + "time" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" + createProductCommand "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/events/integrationevents" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" + + "github.com/brianvoe/gofakeit/v6" + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var integrationFixture *integration.IntegrationTestSharedFixture + +func TestCreateProduct(t *testing.T) { + RegisterFailHandler(Fail) + integrationFixture = integration.NewIntegrationTestSharedFixture(t) + RunSpecs(t, "Create Product Integration Tests") +} + +// https://specflow.org/learn/gherkin/#learn-gherkin +// scenario +var _ = Describe("Creating Product Feature", func() { + var ( + ctx context.Context + err error + command *createProductCommand.CreateProduct + result *dtos.CreateProductResponseDto + createdProduct *models.Product + id uuid.UUID + shouldPublish hypothesis.Hypothesis[*integrationEvents.ProductCreatedV1] + ) + + _ = BeforeEach(func() { + By("Seeding the required data") + // call base SetupTest hook before running child hook + integrationFixture.SetupTest() + + // child hook codes should be here + id = integrationFixture.Items[0].Id + }) + + _ = AfterEach(func() { + By("Cleanup test data") + // call base TearDownTest hook before running child hook + integrationFixture.TearDownTest() + + // child hook codes should be here + }) + + _ = BeforeSuite(func() { + ctx = context.Background() + + // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually + err = integrationFixture.Bus.Start(context.Background()) + Expect(err).ShouldNot(HaveOccurred()) + + // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) + time.Sleep(1 * time.Second) + }) + + _ = AfterSuite(func() { + integrationFixture.Log.Info("TearDownSuite started") + err := integrationFixture.Bus.Stop() + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(1 * time.Second) + }) + + // "Scenario" step for testing creating a new product + Describe( + "Creating a new product and saving it to the database when product doesn't exists", + func() { + Context("Given new product doesn't exists in the system", func() { + BeforeEach(func() { + command, err = createProductCommand.NewCreateProduct( + gofakeit.Name(), + gofakeit.AdjectiveDescriptive(), + gofakeit.Price(150, 6000), + ) + Expect(err).ToNot(HaveOccurred()) + Expect(command).ToNot(BeNil()) + }) + + When( + "the CreateProduct command is executed for non-existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( + ctx, + command, + ) + }) + + It("Should create the product successfully", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(result).NotTo(BeNil()) + }) + + It( + "Should have a non-empty product ID matching the command ID", + func() { + Expect( + result.ProductID, + ).To(Equal(command.ProductID)) + }, + ) + + It( + "Should be able to retrieve the product from the database", + func() { + createdProduct, err = integrationFixture.CatalogsDBContext.FindProductByID( + ctx, + result.ProductID, + ) + Expect(err).NotTo(HaveOccurred()) + + Expect(result).NotTo(BeNil()) + Expect( + command.ProductID, + ).To(Equal(result.ProductID)) + Expect(createdProduct).NotTo(BeNil()) + }, + ) + }, + ) + }) + }, + ) + + // "Scenario" step for testing creating a product with duplicate data + Describe( + "Creating a new product with duplicate data and already exists product", + func() { + Context("Given product already exists in the system", func() { + BeforeEach(func() { + command = &createProductCommand.CreateProduct{ + Name: gofakeit.Name(), + Description: gofakeit.AdjectiveDescriptive(), + Price: gofakeit.Price(150, 6000), + ProductID: id, + } + }) + + When( + "the CreateProduct command is executed for existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( + ctx, + command, + ) + }) + + It( + "Should return an error indicating duplicate record", + func() { + Expect(err).To(HaveOccurred()) + Expect( + customErrors.IsConflictError( + err, + ), + ).To(BeTrue()) + }, + ) + + It("Should not return a result", func() { + Expect(result).To(BeNil()) + }) + }, + ) + }) + }, + ) + + // "Scenario" step for testing creating a product with duplicate data + Describe( + "Publishing ProductCreated event to the broker when product saved successfully", + func() { + Context("Given new product doesn't exists in the system", func() { + BeforeEach(func() { + shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductCreatedV1]( + ctx, + integrationFixture.Bus, + nil, + ) + command, err = createProductCommand.NewCreateProduct( + gofakeit.Name(), + gofakeit.AdjectiveDescriptive(), + gofakeit.Price(150, 6000), + ) + Expect(err).ToNot(HaveOccurred()) + }) + + When( + "CreateProduct command is executed for non-existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*createProductCommand.CreateProduct, *dtos.CreateProductResponseDto]( + ctx, + command, + ) + }) + + It("Should return no error", func() { + Expect(err).ToNot(HaveOccurred()) + }) + + It("Should return not nil result", func() { + Expect(result).ToNot(BeNil()) + }) + + It( + "Should publish ProductCreated event to the broker", + func() { + // ensuring message published to the rabbitmq broker + shouldPublish.Validate( + ctx, + "there is no published message", + time.Second*30, + ) + }, + ) + }, + ) + }) + }, + ) +}) diff --git a/internal/services/catalogwriteservice/test/integration/products/features/deletingproduct/v1/delete_product_test.go b/internal/services/catalogwriteservice/test/integration/products/features/deletingproduct/v1/delete_product_test.go new file mode 100644 index 00000000..759b8a23 --- /dev/null +++ b/internal/services/catalogwriteservice/test/integration/products/features/deletingproduct/v1/delete_product_test.go @@ -0,0 +1,206 @@ +//go:build integration +// +build integration + +package v1 + +import ( + "context" + "fmt" + "testing" + "time" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1" + integrationEvents "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1/events/integrationevents" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" + + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var integrationFixture *integration.IntegrationTestSharedFixture + +func TestDeleteProduct(t *testing.T) { + RegisterFailHandler(Fail) + integrationFixture = integration.NewIntegrationTestSharedFixture(t) + RunSpecs(t, "Delete Product Integration Tests") +} + +// https://specflow.org/learn/gherkin/#learn-gherkin +// scenario +var _ = Describe("Delete Product Feature", func() { + var ( + ctx context.Context + err error + command *v1.DeleteProduct + result *mediatr.Unit + id uuid.UUID + notExistsId uuid.UUID + shouldPublish hypothesis.Hypothesis[*integrationEvents.ProductDeletedV1] + ) + + _ = BeforeEach(func() { + By("Seeding the required data") + // call base SetupTest hook before running child hook + integrationFixture.SetupTest() + + // child hook codes should be here + id = integrationFixture.Items[0].Id + }) + + _ = AfterEach(func() { + By("Cleanup test data") + // call base TearDownTest hook before running child hook + integrationFixture.TearDownTest() + + // child hook codes should be here + }) + + _ = BeforeSuite(func() { + ctx = context.Background() + + // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually + err = integrationFixture.Bus.Start(context.Background()) + Expect(err).ShouldNot(HaveOccurred()) + + // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) + time.Sleep(1 * time.Second) + }) + + _ = AfterSuite(func() { + integrationFixture.Log.Info("TearDownSuite started") + err := integrationFixture.Bus.Stop() + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(1 * time.Second) + }) + + // "Scenario" step for testing deleting an existing product + Describe("Deleting an existing product from the database", func() { + Context("Given product already exists in the system", func() { + BeforeEach(func() { + shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductDeletedV1]( + ctx, + integrationFixture.Bus, + nil, + ) + command, err = v1.NewDeleteProduct(id) + Expect(err).ShouldNot(HaveOccurred()) + }) + + When( + "the DeleteProduct command is executed for existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.DeleteProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + It("Should not return an error", func() { + Expect(err).NotTo(HaveOccurred()) + }) + + It("Should delete the product from the database", func() { + deletedProduct, err := integrationFixture.CatalogsDBContext.FindProductByID( + ctx, + id, + ) + Expect(err).To(HaveOccurred()) + Expect( + err, + ).To(MatchError(ContainSubstring(fmt.Sprintf("product with id `%s` not found in the database", command.ProductID.String())))) + Expect(deletedProduct).To(BeNil()) + }) + }, + ) + }) + }) + + // "Scenario" step for testing deleting a non-existing product + Describe("Deleting a non-existing product from the database", func() { + Context("Given product does not exists in the system", func() { + BeforeEach(func() { + notExistsId = uuid.NewV4() + command, err = v1.NewDeleteProduct(notExistsId) + Expect(err).ShouldNot(HaveOccurred()) + }) + + When( + "the DeleteProduct command is executed for non-existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.DeleteProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + It("Should return an error", func() { + Expect(err).To(HaveOccurred()) + }) + + It("Should return a NotFound error", func() { + Expect( + err, + ).To(MatchError(ContainSubstring(fmt.Sprintf("product with id `%s` not found in the database", command.ProductID.String())))) + }) + + It("Should return a custom NotFound error", func() { + Expect(customErrors.IsNotFoundError(err)).To(BeTrue()) + }) + + It("Should not return a result", func() { + Expect(result).To(BeNil()) + }) + }, + ) + }) + }) + + Describe( + "Publishing ProductDeleted event when product deleted successfully", + func() { + Context("Given product already exists in the system", func() { + BeforeEach(func() { + shouldPublish = messaging.ShouldProduced[*integrationEvents.ProductDeletedV1]( + ctx, + integrationFixture.Bus, + nil, + ) + command, err = v1.NewDeleteProduct(id) + Expect(err).ShouldNot(HaveOccurred()) + }) + + When( + "the DeleteProduct command is executed for existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.DeleteProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + It( + "Should publish ProductDeleted event to the broker", + func() { + // ensuring message published to the rabbitmq broker + shouldPublish.Validate( + ctx, + "there is no published message", + time.Second*30, + ) + }, + ) + }, + ) + }) + }, + ) +}) diff --git a/internal/services/catalogwriteservice/test/integration/products/features/gettingproductbyid/v1/get_product_by_id_test.go b/internal/services/catalogwriteservice/test/integration/products/features/gettingproductbyid/v1/get_product_by_id_test.go new file mode 100644 index 00000000..daeb872e --- /dev/null +++ b/internal/services/catalogwriteservice/test/integration/products/features/gettingproductbyid/v1/get_product_by_id_test.go @@ -0,0 +1,166 @@ +//go:build integration +// +build integration + +package v1 + +import ( + "context" + "fmt" + "testing" + "time" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + getProductByIdQuery "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" + + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var integrationFixture *integration.IntegrationTestSharedFixture + +func TestGetProductById(t *testing.T) { + RegisterFailHandler(Fail) + integrationFixture = integration.NewIntegrationTestSharedFixture(t) + RunSpecs(t, "Get Product By ID Integration Tests") +} + +var _ = Describe("Get Product by ID Feature", func() { + var ( + ctx context.Context + id uuid.UUID + query *getProductByIdQuery.GetProductById + result *dtos.GetProductByIdResponseDto + err error + ) + + _ = BeforeEach(func() { + By("Seeding the required data") + // call base SetupTest hook before running child hook + integrationFixture.SetupTest() + + // child hook codes should be here + id = integrationFixture.Items[0].Id + }) + + _ = AfterEach(func() { + By("Cleanup test data") + // call base TearDownTest hook before running child hook + integrationFixture.TearDownTest() + + // child hook codes should be here + }) + + _ = BeforeSuite(func() { + ctx = context.Background() + + // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually + err = integrationFixture.Bus.Start(context.Background()) + Expect(err).ShouldNot(HaveOccurred()) + + // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) + time.Sleep(1 * time.Second) + }) + + _ = AfterSuite(func() { + integrationFixture.Log.Info("TearDownSuite started") + err := integrationFixture.Bus.Stop() + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(1 * time.Second) + }) + + // "Scenario" step for testing returning an existing product with correct properties + Describe( + "Returning an existing product with valid Id from the database with correct properties", + func() { + Context("Given products exists in the database", func() { + BeforeEach(func() { + query, err = getProductByIdQuery.NewGetProductById(id) + }) + + // "When" step + When( + "the GteProductById query is executed for existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*getProductByIdQuery.GetProductById, *dtos.GetProductByIdResponseDto]( + ctx, + query, + ) + }) + + // "Then" step + It("Should not return an error", func() { + Expect(err).To(BeNil()) + }) + + It("Should return a non-nil result", func() { + Expect(result).NotTo(BeNil()) + }) + + It( + "Should return a product with the correct ID", + func() { + Expect(result.Product).NotTo(BeNil()) + Expect(result.Product.Id).To(Equal(id)) + }, + ) + }, + ) + }) + }, + ) + + // "Scenario" step for testing returning a NotFound error when record does not exist + Describe( + "Returning a NotFound error when product with specific id does not exist", + func() { + Context("Given products does not exists in the database", func() { + BeforeEach(func() { + // Generate a random UUID that does not exist in the database + id = uuid.NewV4() + query, err = getProductByIdQuery.NewGetProductById(id) + Expect(err).To(BeNil()) + }) + + // "When" step + When( + "the GteProductById query is executed for non-existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*getProductByIdQuery.GetProductById, *dtos.GetProductByIdResponseDto]( + ctx, + query, + ) + }) + + // "Then" step + It("Should return an error", func() { + Expect(err).To(HaveOccurred()) + }) + + It("Should return a NotFound error", func() { + Expect( + err, + ).To(MatchError(ContainSubstring(fmt.Sprintf("product with id `%s` not found in the database", query.ProductID.String())))) + }) + + It("Should return a custom NotFound error", func() { + Expect( + customErrors.IsNotFoundError(err), + ).To(BeTrue()) + }) + + It("Should not return a result", func() { + Expect(result).To(BeNil()) + }) + }, + ) + }) + }, + ) +}) diff --git a/internal/services/catalog_write_service/test/integration/products/features/getting_products/v1/get_products_test.go b/internal/services/catalogwriteservice/test/integration/products/features/gettingproducts/v1/get_products_test.go similarity index 55% rename from internal/services/catalog_write_service/test/integration/products/features/getting_products/v1/get_products_test.go rename to internal/services/catalogwriteservice/test/integration/products/features/gettingproducts/v1/get_products_test.go index 64ce9eda..c5e2193c 100644 --- a/internal/services/catalog_write_service/test/integration/products/features/getting_products/v1/get_products_test.go +++ b/internal/services/catalogwriteservice/test/integration/products/features/gettingproducts/v1/get_products_test.go @@ -9,9 +9,9 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/dtos" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_products/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/integration" + gettingproductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" "github.com/mehdihadeli/go-mediatr" @@ -31,19 +31,25 @@ var _ = Describe("Get All Products Feature", func() { // Define variables to hold query and result data var ( ctx context.Context - query *queries.GetProducts + query *gettingproductsv1.GetProducts queryResult *dtos.GetProductsResponseDto err error ) _ = BeforeEach(func() { By("Seeding the required data") - integrationFixture.InitializeTest() + // call base SetupTest hook before running child hook + integrationFixture.SetupTest() + + // child hook codes should be here }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + // call base TearDownTest hook before running child hook + integrationFixture.TearDownTest() + + // child hook codes should be here }) _ = BeforeSuite(func() { @@ -69,35 +75,45 @@ var _ = Describe("Get All Products Feature", func() { Context("Given existing products in the database", func() { BeforeEach(func() { // Create a query to retrieve a list of products - query, err = queries.NewGetProducts(utils.NewListQuery(10, 1)) + query, err = gettingproductsv1.NewGetProducts( + utils.NewListQuery(10, 1), + ) Expect(err).To(BeNil()) }) // "When" step - When("the GteProducts query is executed for existing products", func() { - BeforeEach(func() { - queryResult, err = mediatr.Send[*queries.GetProducts, *dtos.GetProductsResponseDto](ctx, query) - }) - - // "Then" step - It("Should not return an error", func() { - Expect(err).To(BeNil()) - }) - - It("Should return a non-nil result", func() { - Expect(queryResult).NotTo(BeNil()) - }) - - It("Should return a list of products with items", func() { - Expect(queryResult.Products).NotTo(BeNil()) - Expect(queryResult.Products.Items).NotTo(BeEmpty()) - }) - - It("Should return the expected number of products", func() { - // Replace 'len(c.Items)' with the expected number of products - Expect(len(queryResult.Products.Items)).To(Equal(len(integrationFixture.Items))) - }) - }) + When( + "the GteProducts query is executed for existing products", + func() { + BeforeEach(func() { + queryResult, err = mediatr.Send[*gettingproductsv1.GetProducts, *dtos.GetProductsResponseDto]( + ctx, + query, + ) + }) + + // "Then" step + It("Should not return an error", func() { + Expect(err).To(BeNil()) + }) + + It("Should return a non-nil result", func() { + Expect(queryResult).NotTo(BeNil()) + }) + + It("Should return a list of products with items", func() { + Expect(queryResult.Products).NotTo(BeNil()) + Expect(queryResult.Products.Items).NotTo(BeEmpty()) + }) + + It("Should return the expected number of products", func() { + // Replace 'len(c.Products)' with the expected number of products + Expect( + len(queryResult.Products.Items), + ).To(Equal(len(integrationFixture.Items))) + }) + }, + ) }) }) }) diff --git a/internal/services/catalogwriteservice/test/integration/products/features/updatingproduct/v1/update_product_test.go b/internal/services/catalogwriteservice/test/integration/products/features/updatingproduct/v1/update_product_test.go new file mode 100644 index 00000000..e2b74baf --- /dev/null +++ b/internal/services/catalogwriteservice/test/integration/products/features/updatingproduct/v1/update_product_test.go @@ -0,0 +1,236 @@ +//go:build integration +// +build integration + +package v1 + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/hypothesis" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/test/messaging" + datamodel "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1/events/integrationevents" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/integration" + + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var integrationFixture *integration.IntegrationTestSharedFixture + +func TestUpdateProduct(t *testing.T) { + RegisterFailHandler(Fail) + integrationFixture = integration.NewIntegrationTestSharedFixture(t) + RunSpecs(t, "Updated Products Integration Tests") +} + +var _ = Describe("Update Product Feature", func() { + // Define variables to hold command and result data + var ( + ctx context.Context + existingProduct *datamodel.ProductDataModel + command *v1.UpdateProduct + result *mediatr.Unit + err error + id uuid.UUID + shouldPublish hypothesis.Hypothesis[*integrationevents.ProductUpdatedV1] + ) + + _ = BeforeEach(func() { + By("Seeding the required data") + integrationFixture.SetupTest() + + existingProduct = integrationFixture.Items[0] + }) + + _ = AfterEach(func() { + By("Cleanup test data") + integrationFixture.TearDownTest() + }) + + _ = BeforeSuite(func() { + ctx = context.Background() + + // in test mode we set rabbitmq `AutoStart=false` in configuration in rabbitmqOptions, so we should run rabbitmq bus manually + err = integrationFixture.Bus.Start(context.Background()) + Expect(err).ShouldNot(HaveOccurred()) + + // wait for consumers ready to consume before publishing messages, preparation background workers takes a bit time (for preventing messages lost) + time.Sleep(1 * time.Second) + }) + + _ = AfterSuite(func() { + integrationFixture.Log.Info("TearDownSuite started") + err := integrationFixture.Bus.Stop() + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(1 * time.Second) + }) + + // "Scenario" step for testing updating an existing product + Describe("Updating an existing product in the database", func() { + Context("Given product exists in the database", func() { + BeforeEach(func() { + command, err = v1.NewUpdateProduct( + existingProduct.Id, + "Updated Product ShortTypeName", + existingProduct.Description, + existingProduct.Price, + ) + Expect(err).NotTo(HaveOccurred()) + }) + + // "When" step + When("the UpdateProduct command is executed", func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.UpdateProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + // "Then" step + It("Should not return an error", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(result).NotTo(BeNil()) + }) + + It("Should return a non-nil result", func() { + Expect(result).NotTo(BeNil()) + }) + + It( + "Should update the existing product in the database", + func() { + updatedProduct, err := integrationFixture.CatalogsDBContext.FindProductByID( + ctx, + existingProduct.Id, + ) + Expect(err).To(BeNil()) + Expect(updatedProduct).NotTo(BeNil()) + Expect( + updatedProduct.Id, + ).To(Equal(existingProduct.Id)) + Expect( + updatedProduct.Price, + ).To(Equal(existingProduct.Price)) + Expect( + updatedProduct.Name, + ).NotTo(Equal(existingProduct.Name)) + }, + ) + }) + }) + }) + + // "Scenario" step for testing updating a non-existing product + Describe("Updating a non-existing product in the database", func() { + Context("Given product not exists in the database", func() { + BeforeEach(func() { + // Generate a random ID that does not exist in the database + id = uuid.NewV4() + command, err = v1.NewUpdateProduct( + id, + "Updated Product ShortTypeName", + "Updated Product Description", + 100, + ) + Expect(err).NotTo(HaveOccurred()) + }) + + // "When" step + When( + "the UpdateProduct command executed for non-existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.UpdateProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + // "Then" step + It("Should return an error", func() { + Expect(err).To(HaveOccurred()) + }) + It("Should not return a result", func() { + Expect(result).To(BeNil()) + }) + + It("Should return a NotFound error", func() { + Expect( + err, + ).To(MatchError(ContainSubstring(fmt.Sprintf("product with id `%s` not found", id.String())))) + }) + + It("Should return a custom NotFound error", func() { + Expect(customErrors.IsNotFoundError(err)).To(BeTrue()) + Expect( + customErrors.IsApplicationError( + err, + http.StatusNotFound, + ), + ).To(BeTrue()) + }) + }, + ) + }) + }) + + // "Scenario" step for testing updating an existing product + Describe( + "Publishing ProductUpdated when product updated successfully", + func() { + Context("Given product exists in the database", func() { + BeforeEach(func() { + command, err = v1.NewUpdateProduct( + existingProduct.Id, + "Updated Product ShortTypeName", + existingProduct.Description, + existingProduct.Price, + ) + Expect(err).NotTo(HaveOccurred()) + + shouldPublish = messaging.ShouldProduced[*integrationevents.ProductUpdatedV1]( + ctx, + integrationFixture.Bus, + nil, + ) + }) + + // "When" step + When( + "the UpdateProduct command is executed for existing product", + func() { + BeforeEach(func() { + result, err = mediatr.Send[*v1.UpdateProduct, *mediatr.Unit]( + ctx, + command, + ) + }) + + It( + "Should publish ProductUpdated event to the broker", + func() { + // ensuring message published to the rabbitmq broker + shouldPublish.Validate( + ctx, + "there is no published message", + time.Second*30, + ) + }, + ) + }, + ) + }) + }, + ) +}) diff --git a/internal/services/catalog_write_service/test/load/.openapi-generator-ignore b/internal/services/catalogwriteservice/test/load/.openapi-generator-ignore similarity index 100% rename from internal/services/catalog_write_service/test/load/.openapi-generator-ignore rename to internal/services/catalogwriteservice/test/load/.openapi-generator-ignore diff --git a/internal/services/catalog_write_service/test/load/.openapi-generator/FILES b/internal/services/catalogwriteservice/test/load/.openapi-generator/FILES similarity index 100% rename from internal/services/catalog_write_service/test/load/.openapi-generator/FILES rename to internal/services/catalogwriteservice/test/load/.openapi-generator/FILES diff --git a/internal/services/catalog_write_service/test/load/.openapi-generator/VERSION b/internal/services/catalogwriteservice/test/load/.openapi-generator/VERSION similarity index 100% rename from internal/services/catalog_write_service/test/load/.openapi-generator/VERSION rename to internal/services/catalogwriteservice/test/load/.openapi-generator/VERSION diff --git a/internal/services/catalog_write_service/test/load/README.md b/internal/services/catalogwriteservice/test/load/README.md similarity index 100% rename from internal/services/catalog_write_service/test/load/README.md rename to internal/services/catalogwriteservice/test/load/README.md diff --git a/internal/services/catalog_write_service/test/load/script.js b/internal/services/catalogwriteservice/test/load/script.js similarity index 100% rename from internal/services/catalog_write_service/test/load/script.js rename to internal/services/catalogwriteservice/test/load/script.js diff --git a/internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_handler_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_handler_unit_test.go new file mode 100644 index 00000000..7bdb8fe0 --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_handler_unit_test.go @@ -0,0 +1,170 @@ +//go:build unit +// +build unit + +package v1 + +import ( + "testing" + "time" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + datamodels "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + creatingproductv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1" + creatingproductdtosv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type createProductHandlerUnitTests struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*creatingproductv1.CreateProduct, *creatingproductdtosv1.CreateProductResponseDto] +} + +func TestCreateProductHandlerUnit(t *testing.T) { + suite.Run(t, &createProductHandlerUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, + ) +} + +func (c *createProductHandlerUnitTests) SetupTest() { + // call base SetupTest hook before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = creatingproductv1.NewCreateProductHandler( + fxparams.ProductHandlerParams{ + CatalogsDBContext: c.CatalogDBContext, + Tracer: c.Tracer, + RabbitmqProducer: c.Bus, + Log: c.Log, + }, + ) +} + +func (c *createProductHandlerUnitTests) TearDownTest() { + // call base TearDownTest hook before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *createProductHandlerUnitTests) Test_Handle_Should_Create_New_Product_With_Valid_Data() { + id := uuid.NewV4() + + createProduct := &creatingproductv1.CreateProduct{ + ProductID: id, + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.EmojiDescription(), + Price: gofakeit.Price(100, 1000), + } + + c.BeginTx() + _, err := c.handler.Handle(c.Ctx, createProduct) + c.CommitTx() + + c.Require().NoError(err) + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) + + res, err := gormdbcontext.FindModelByID[*datamodels.ProductDataModel, *models.Product]( + c.Ctx, + c.CatalogDBContext, + id, + ) + c.Require().NoError(err) + + c.Assert().Equal(res.Id, id) +} + +func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Duplicate_Item() { + id := uuid.NewV4() + + createProduct := &creatingproductv1.CreateProduct{ + ProductID: id, + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.EmojiDescription(), + Price: gofakeit.Price(100, 1000), + } + + c.BeginTx() + dto, err := c.handler.Handle(c.Ctx, createProduct) + c.Require().NoError(err) + c.Require().NotNil(dto) + c.CommitTx() + + c.BeginTx() + dto, err = c.handler.Handle(c.Ctx, createProduct) + c.CommitTx() + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) + c.True(customErrors.IsConflictError(err)) + c.ErrorContains(err, "product already exists") + c.Nil(dto) +} + +func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { + id := uuid.NewV4() + + createProduct := &creatingproductv1.CreateProduct{ + ProductID: id, + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.EmojiDescription(), + Price: gofakeit.Price(100, 1000), + } + + // override called mock + // https://github.com/stretchr/testify/issues/558 + c.Bus.Mock.ExpectedCalls = nil + c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). + Once(). + Return(errors.New("error in the publish message")) + + c.BeginTx() + + dto, err := c.handler.Handle(c.Ctx, createProduct) + + c.CommitTx() + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) + c.ErrorContains(err, "error in the publish message") + c.ErrorContains( + err, + "error in publishing ProductCreated integration_events event", + ) + c.Nil(dto) +} + +func (c *createProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Mapping() { + id := uuid.NewV4() + + createProduct := &creatingproductv1.CreateProduct{ + ProductID: id, + Name: gofakeit.Name(), + CreatedAt: time.Now(), + Description: gofakeit.EmojiDescription(), + Price: gofakeit.Price(100, 1000), + } + + mapper.ClearMappings() + + c.BeginTx() + + dto, err := c.handler.Handle(c.Ctx, createProduct) + + c.CommitTx() + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) + c.ErrorContains(err, "error in the mapping") + c.True(customErrors.IsInternalServerError(err)) + c.Nil(dto) +} diff --git a/internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_unit_test.go similarity index 55% rename from internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_unit_test.go rename to internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_unit_test.go index 22d67ced..5c591486 100644 --- a/internal/services/catalog_write_service/test/unit/products/features/creating_product/v1/commands/create_product_unit_test.go +++ b/internal/services/catalogwriteservice/test/unit/products/features/creatingproduct/v1/create_product_unit_test.go @@ -1,26 +1,30 @@ //go:build unit // +build unit -package commands +package v1 import ( + "fmt" "testing" - createProductCommand "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + createProductCommand "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/creatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" "github.com/brianvoe/gofakeit/v6" "github.com/stretchr/testify/suite" ) type createProductUnitTests struct { - *unit_test.UnitTestSharedFixture + *unittest.UnitTestSharedFixture } func TestCreateProductUnit(t *testing.T) { suite.Run( t, - &createProductUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, + &createProductUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, ) } @@ -29,17 +33,26 @@ func (c *createProductUnitTests) Test_New_Create_Product_Should_Return_No_Error_ description := gofakeit.EmojiDescription() price := gofakeit.Price(150, 6000) - updateProduct, err := createProductCommand.NewCreateProduct(name, description, price) + createProduct, err := createProductCommand.NewCreateProductWithValidation( + name, + description, + price, + ) + var g interface{} = createProduct + d, ok := g.(cqrs.Command) + if ok { + fmt.Println(d) + } - c.Assert().NotNil(updateProduct) - c.Assert().Equal(name, updateProduct.Name) - c.Assert().Equal(price, updateProduct.Price) + c.Assert().NotNil(createProduct) + c.Assert().Equal(name, createProduct.Name) + c.Assert().Equal(price, createProduct.Price) c.Require().NoError(err) } func (c *createProductUnitTests) Test_New_Create_Product_Should_Return_Error_For_Invalid_Price() { - command, err := createProductCommand.NewCreateProduct( + command, err := createProductCommand.NewCreateProductWithValidation( gofakeit.Name(), gofakeit.EmojiDescription(), 0, @@ -50,14 +63,22 @@ func (c *createProductUnitTests) Test_New_Create_Product_Should_Return_Error_For } func (c *createProductUnitTests) Test_New_Create_Product_Should_Return_Error_For_Empty_Name() { - command, err := createProductCommand.NewCreateProduct("", gofakeit.EmojiDescription(), 120) + command, err := createProductCommand.NewCreateProductWithValidation( + "", + gofakeit.EmojiDescription(), + 120, + ) c.Require().Error(err) c.Assert().Nil(command) } func (c *createProductUnitTests) Test_New_Create_Product_Should_Return_Error_For_Empty_Description() { - command, err := createProductCommand.NewCreateProduct(gofakeit.Name(), "", 120) + command, err := createProductCommand.NewCreateProductWithValidation( + gofakeit.Name(), + "", + 120, + ) c.Require().Error(err) c.Assert().Nil(command) diff --git a/internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_handler_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_handler_unit_test.go new file mode 100644 index 00000000..3d24aec7 --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_handler_unit_test.go @@ -0,0 +1,119 @@ +//go:build unit +// +build unit + +package v1 + +import ( + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + deletingproductv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + "emperror.dev/errors" + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type deleteProductHandlerUnitTests struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*deletingproductv1.DeleteProduct, *mediatr.Unit] +} + +func TestDeleteProductHandlerUnit(t *testing.T) { + suite.Run( + t, + &deleteProductHandlerUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, + ) +} + +func (c *deleteProductHandlerUnitTests) SetupTest() { + // call base SetupTest hook before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = deletingproductv1.NewDeleteProductHandler( + fxparams.ProductHandlerParams{ + Log: c.Log, + CatalogsDBContext: c.CatalogDBContext, + RabbitmqProducer: c.Bus, + Tracer: c.Tracer, + }, + ) +} + +func (c *deleteProductHandlerUnitTests) TearDownTest() { + // call base TearDownTest hook before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Delete_Product_With_Valid_Id() { + id := c.Products[0].Id + + deleteProduct := &deletingproductv1.DeleteProduct{ + ProductID: id, + } + + c.BeginTx() + _, err := c.handler.Handle(c.Ctx, deleteProduct) + c.CommitTx() + + c.Require().NoError(err) + + p, err := gormdbcontext.FindDataModelByID[*datamodels.ProductDataModel](c.Ctx, c.CatalogDBContext, id) + + c.Require().Nil(p) + c.Require().Error(err) + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) +} + +func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Return_NotFound_Error_When_Id_Is_Invalid() { + id := uuid.NewV4() + + deleteProduct := &deletingproductv1.DeleteProduct{ + ProductID: id, + } + + c.BeginTx() + res, err := c.handler.Handle(c.Ctx, deleteProduct) + c.CommitTx() + + c.Require().Error(err) + c.True(customErrors.IsNotFoundError(err)) + c.Nil(res) + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) +} + +func (c *deleteProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { + id := c.Products[0].Id + + deleteProduct := &deletingproductv1.DeleteProduct{ + ProductID: id, + } + + // override called mock + // https://github.com/stretchr/testify/issues/558 + c.Bus.Mock.ExpectedCalls = nil + c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). + Once(). + Return(errors.New("error in the publish message")) + + c.BeginTx() + dto, err := c.handler.Handle(c.Ctx, deleteProduct) + c.CommitTx() + + c.Nil(dto) + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) + c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) + c.ErrorContains(err, "error in publishing 'ProductDeleted' message") +} diff --git a/internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_unit_test.go similarity index 59% rename from internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_unit_test.go rename to internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_unit_test.go index 0ed12e9b..5ed6e23e 100644 --- a/internal/services/catalog_write_service/test/unit/products/features/deleting_product/v1/commands/delete_product_unit_test.go +++ b/internal/services/catalogwriteservice/test/unit/products/features/deletingproduct/v1/delete_product_unit_test.go @@ -1,33 +1,33 @@ //go:build unit // +build unit -package commands +package v1 import ( "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deleting_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/deletingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/suite" ) type deleteProductUnitTests struct { - *unit_test.UnitTestSharedFixture + *unittest.UnitTestSharedFixture } func TestDeleteProductByIdUnit(t *testing.T) { suite.Run( t, - &deleteProductUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, + &deleteProductUnitTests{UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t)}, ) } func (c *deleteProductUnitTests) Test_New_Delete_Product_Should_Return_No_Error_For_Valid_Input() { id := uuid.NewV4() - query, err := commands.NewDeleteProduct(id) + query, err := v1.NewDeleteProduct(id) c.Assert().NotNil(query) c.Assert().Equal(query.ProductID, id) @@ -35,7 +35,7 @@ func (c *deleteProductUnitTests) Test_New_Delete_Product_Should_Return_No_Error_ } func (c *deleteProductUnitTests) Test_New_Delete_Product_Should_Return_Error_For_Invalid_Id() { - query, err := commands.NewDeleteProduct(uuid.UUID{}) + query, err := v1.NewDeleteProduct(uuid.UUID{}) c.Assert().Nil(query) c.Require().Error(err) diff --git a/internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_Id_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_Id_unit_test.go similarity index 78% rename from internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_Id_unit_test.go rename to internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_Id_unit_test.go index f16e6d99..e1f146ef 100644 --- a/internal/services/catalog_write_service/test/unit/products/features/getting_product_by_id/v1/queries/get_product_by_Id_unit_test.go +++ b/internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_Id_unit_test.go @@ -1,26 +1,26 @@ //go:build unit // +build unit -package queries +package v1 import ( "testing" - getProductByIdQuery "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/getting_product_by_id/v1/queries" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" + getProductByIdQuery "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/suite" ) type getProductByIdUnitTests struct { - *unit_test.UnitTestSharedFixture + *unittest.UnitTestSharedFixture } func TestGetProductByIdUnit(t *testing.T) { suite.Run( t, - &getProductByIdUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, + &getProductByIdUnitTests{UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t)}, ) } diff --git a/internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_id_handler_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_id_handler_unit_test.go new file mode 100644 index 00000000..0add2d03 --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/gettingproductbyid/v1/get_product_by_id_handler_unit_test.go @@ -0,0 +1,97 @@ +//go:build unit +// +build unit + +package v1 + +import ( + "fmt" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + gettingproductbyidv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproductbyid/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/suite" +) + +type getProductByIdHandlerTest struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*gettingproductbyidv1.GetProductById, *dtos.GetProductByIdResponseDto] +} + +func TestGetProductByIdHandlerUnit(t *testing.T) { + suite.Run(t, &getProductByIdHandlerTest{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }) +} + +func (c *getProductByIdHandlerTest) SetupTest() { + // call base SetupTest hook before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = gettingproductbyidv1.NewGetProductByIDHandler( + fxparams.ProductHandlerParams{ + CatalogsDBContext: c.CatalogDBContext, + Tracer: c.Tracer, + RabbitmqProducer: c.Bus, + Log: c.Log, + }) +} + +func (c *getProductByIdHandlerTest) TearDownTest() { + // call base TearDownTest hook before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *getProductByIdHandlerTest) Test_Handle_Should_Return_Correct_Product_By_ID() { + product := c.Products[0] + + query, err := gettingproductbyidv1.NewGetProductById(product.Id) + c.Require().NoError(err) + + dto, err := c.handler.Handle(c.Ctx, query) + c.Require().NoError(err) + c.Assert().NotNil(dto) + c.Assert().NotNil(dto.Product) + c.Assert().Equal(dto.Product.Id, product.Id) + c.Assert().Equal(dto.Product.Name, product.Name) +} + +func (c *getProductByIdHandlerTest) Test_Handle_Should_Return_NotFound_Error_For_NotFound_Item() { + id := uuid.NewV4() + + query, err := gettingproductbyidv1.NewGetProductById(id) + c.Require().NoError(err) + + dto, err := c.handler.Handle(c.Ctx, query) + c.Require().Error(err) + c.True(customErrors.IsNotFoundError(err)) + c.ErrorContains( + err, + fmt.Sprintf( + "product_data_model with id `%s` not found in the database", + id.String(), + ), + ) + c.Nil(dto) +} + +func (c *getProductByIdHandlerTest) Test_Handle_Should_Return_Error_For_Error_In_Mapping() { + mapper.ClearMappings() + + product := c.Products[0] + + query, err := gettingproductbyidv1.NewGetProductById(product.Id) + c.Require().NoError(err) + + dto, err := c.handler.Handle(c.Ctx, query) + + c.Nil(dto) + c.Require().Error(err) + c.True(customErrors.IsInternalServerError(err)) + c.ErrorContains(err, "error in the mapping product") +} diff --git a/internal/services/catalogwriteservice/test/unit/products/features/gettingproducts/v1/get_products_handler_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/gettingproducts/v1/get_products_handler_unit_test.go new file mode 100644 index 00000000..4efc434e --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/gettingproducts/v1/get_products_handler_unit_test.go @@ -0,0 +1,74 @@ +//go:build unit +// +build unit + +package v1 + +import ( + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + gettingproductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/gettingproducts/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + "github.com/stretchr/testify/suite" +) + +type getProductsHandlerUnitTests struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*gettingproductsv1.GetProducts, *dtos.GetProductsResponseDto] +} + +func TestGetProductsUnit(t *testing.T) { + suite.Run( + t, + &getProductsHandlerUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, + ) +} + +func (c *getProductsHandlerUnitTests) SetupTest() { + // call base SetupTest hook before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = gettingproductsv1.NewGetProductsHandler( + fxparams.ProductHandlerParams{ + CatalogsDBContext: c.CatalogDBContext, + Tracer: c.Tracer, + RabbitmqProducer: c.Bus, + Log: c.Log, + }) +} + +func (c *getProductsHandlerUnitTests) TearDownTest() { + // call base TearDownTest hook before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *getProductsHandlerUnitTests) Test_Handle_Should_Return_Products_Successfully() { + query, err := gettingproductsv1.NewGetProducts(utils.NewListQuery(10, 1)) + c.Require().NoError(err) + + res, err := c.handler.Handle(c.Ctx, query) + c.Require().NoError(err) + c.NotNil(res) + c.NotEmpty(res.Products) + c.Equal(len(c.Products), len(res.Products.Items)) +} + +func (c *getProductsHandlerUnitTests) Test_Handle_Should_Return_Error_For_Mapping_List_Result() { + query, err := gettingproductsv1.NewGetProducts(utils.NewListQuery(10, 1)) + c.Require().NoError(err) + + mapper.ClearMappings() + + res, err := c.handler.Handle(c.Ctx, query) + c.Require().Error(err) + c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) + c.Nil(res) +} diff --git a/internal/services/catalogwriteservice/test/unit/products/features/searchingproducts/v1/search_products_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/searchingproducts/v1/search_products_unit_test.go new file mode 100644 index 00000000..ea5db3ea --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/searchingproducts/v1/search_products_unit_test.go @@ -0,0 +1,77 @@ +package v1 + +import ( + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + searchingproductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/searchingproduct/v1/dtos" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + "github.com/stretchr/testify/suite" +) + +type searchProductsHandlerUnitTests struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*searchingproductsv1.SearchProducts, *dtos.SearchProductsResponseDto] +} + +func TestSearchProductsUnit(t *testing.T) { + suite.Run( + t, + &searchProductsHandlerUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, + ) +} + +func (c *searchProductsHandlerUnitTests) SetupTest() { + // call base SetupTest hook before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = searchingproductsv1.NewSearchProductsHandler( + fxparams.ProductHandlerParams{ + CatalogsDBContext: c.CatalogDBContext, + Tracer: c.Tracer, + RabbitmqProducer: c.Bus, + Log: c.Log, + }) +} + +func (c *searchProductsHandlerUnitTests) TearDownTest() { + // call base TearDownTest hook before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *searchProductsHandlerUnitTests) Test_Handle_Should_Return_Products_Successfully() { + query, err := searchingproductsv1.NewSearchProducts( + c.Products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + res, err := c.handler.Handle(c.Ctx, query) + c.Require().NoError(err) + c.NotNil(res) + c.NotEmpty(res.Products) + c.Equal(len(res.Products.Items), 1) +} + +func (c *searchProductsHandlerUnitTests) Test_Handle_Should_Return_Error_For_Mapping_List_Result() { + query, err := searchingproductsv1.NewSearchProducts( + c.Products[0].Name, + utils.NewListQuery(10, 1), + ) + c.Require().NoError(err) + + mapper.ClearMappings() + + res, err := c.handler.Handle(c.Ctx, query) + c.Require().Error(err) + c.True(customErrors.IsApplicationError(err, http.StatusInternalServerError)) + c.Nil(res) +} diff --git a/internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_handler_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_handler_unit_test.go new file mode 100644 index 00000000..a2535fdc --- /dev/null +++ b/internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_handler_unit_test.go @@ -0,0 +1,136 @@ +//go:build unit +// +build unit + +package v1 + +import ( + "fmt" + "net/http" + "testing" + + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/cqrs" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/postgresgorm/gormdbcontext" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/data/datamodels" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1/fxparams" + updatingoroductsv1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" + + "emperror.dev/errors" + "github.com/brianvoe/gofakeit/v6" + "github.com/mehdihadeli/go-mediatr" + uuid "github.com/satori/go.uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +type updateProductHandlerUnitTests struct { + *unittest.UnitTestSharedFixture + handler cqrs.RequestHandlerWithRegisterer[*updatingoroductsv1.UpdateProduct, *mediatr.Unit] +} + +func TestUpdateProductHandlerUnit(t *testing.T) { + suite.Run( + t, + &updateProductHandlerUnitTests{ + UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t), + }, + ) +} + +func (c *updateProductHandlerUnitTests) SetupTest() { + // call base `SetupTest hook` before running child hook + c.UnitTestSharedFixture.SetupTest() + c.handler = updatingoroductsv1.NewUpdateProductHandler( + fxparams.ProductHandlerParams{ + CatalogsDBContext: c.CatalogDBContext, + Tracer: c.Tracer, + RabbitmqProducer: c.Bus, + Log: c.Log, + }, + ) +} + +func (c *updateProductHandlerUnitTests) TearDownTest() { + // call base `TearDownTest hook` before running child hook + c.UnitTestSharedFixture.TearDownTest() +} + +func (c *updateProductHandlerUnitTests) Test_Handle_Should_Update_Product_With_Valid_Data() { + existing := c.Products[0] + + updateProductCommand, err := updatingoroductsv1.NewUpdateProduct( + existing.Id, + gofakeit.Name(), + gofakeit.EmojiDescription(), + existing.Price, + ) + c.Require().NoError(err) + + c.BeginTx() + _, err = c.handler.Handle(c.Ctx, updateProductCommand) + c.CommitTx() + + c.Require().NoError(err) + + updatedProduct, err := gormdbcontext.FindDataModelByID[*datamodels.ProductDataModel]( + c.Ctx, + c.CatalogDBContext, + updateProductCommand.ProductID, + ) + c.Require().NoError(err) + + c.Assert().Equal(updatedProduct.Id, updateProductCommand.ProductID) + c.Assert().Equal(updatedProduct.Name, updateProductCommand.Name) + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) +} + +func (c *updateProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_NotFound_Item() { + id := uuid.NewV4() + + command, err := updatingoroductsv1.NewUpdateProduct( + id, + gofakeit.Name(), + gofakeit.EmojiDescription(), + gofakeit.Price(150, 6000), + ) + c.Require().NoError(err) + + c.BeginTx() + _, err = c.handler.Handle(c.Ctx, command) + c.CommitTx() + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 0) + c.True(customErrors.IsApplicationError(err, http.StatusNotFound)) + c.ErrorContains( + err, + fmt.Sprintf("product with id `%s` not found", id.String()), + ) +} + +func (c *updateProductHandlerUnitTests) Test_Handle_Should_Return_Error_For_Error_In_Bus() { + existing := c.Products[0] + + updateProductCommand, err := updatingoroductsv1.NewUpdateProduct( + existing.Id, + gofakeit.Name(), + gofakeit.EmojiDescription(), + existing.Price, + ) + c.Require().NoError(err) + + // override called mock + // https://github.com/stretchr/testify/issues/558 + c.Bus.Mock.ExpectedCalls = nil + c.Bus.On("PublishMessage", mock.Anything, mock.Anything, mock.Anything). + Once(). + Return(errors.New("error in the publish message")) + + c.BeginTx() + _, err = c.handler.Handle(c.Ctx, updateProductCommand) + c.CommitTx() + + c.Bus.AssertNumberOfCalls(c.T(), "PublishMessage", 1) + c.ErrorContains(err, "error in the publish message") + c.ErrorContains(err, "error in publishing 'ProductUpdated' message") +} diff --git a/internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_unit_test.go b/internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_unit_test.go similarity index 66% rename from internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_unit_test.go rename to internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_unit_test.go index 5041a308..c82b4c60 100644 --- a/internal/services/catalog_write_service/test/unit/products/features/updating_product/v1/commands/update_product_unit_test.go +++ b/internal/services/catalogwriteservice/test/unit/products/features/updatingproduct/v1/update_product_unit_test.go @@ -1,13 +1,13 @@ //go:build unit // +build unit -package commands +package v1 import ( "testing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updating_product/v1/commands" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" + v1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/features/updatingproduct/v1" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -15,13 +15,13 @@ import ( ) type updateProductUnitTests struct { - *unit_test.UnitTestSharedFixture + *unittest.UnitTestSharedFixture } func TestUpdateProductUnit(t *testing.T) { suite.Run( t, - &updateProductUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, + &updateProductUnitTests{UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t)}, ) } @@ -31,7 +31,7 @@ func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_No_Error_ description := gofakeit.EmojiDescription() price := gofakeit.Price(150, 6000) - updateProduct, err := commands.NewUpdateProduct(id, name, description, price) + updateProduct, err := v1.NewUpdateProduct(id, name, description, price) c.Assert().NotNil(updateProduct) c.Assert().Equal(id, updateProduct.ProductID) @@ -42,7 +42,7 @@ func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_No_Error_ } func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_Error_For_Invalid_Price() { - command, err := commands.NewUpdateProduct( + command, err := v1.NewUpdateProduct( uuid.NewV4(), gofakeit.Name(), gofakeit.EmojiDescription(), @@ -54,14 +54,14 @@ func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_Error_For } func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_Error_For_Empty_Name() { - command, err := commands.NewUpdateProduct(uuid.NewV4(), "", gofakeit.EmojiDescription(), 120) + command, err := v1.NewUpdateProduct(uuid.NewV4(), "", gofakeit.EmojiDescription(), 120) c.Require().Error(err) c.Assert().Nil(command) } func (c *updateProductUnitTests) Test_New_Update_Product_Should_Return_Error_For_Empty_Description() { - command, err := commands.NewUpdateProduct(uuid.NewV4(), gofakeit.Name(), "", 120) + command, err := v1.NewUpdateProduct(uuid.NewV4(), gofakeit.Name(), "", 120) c.Require().Error(err) c.Assert().Nil(command) diff --git a/internal/services/catalog_write_service/test/unit/products/mappings_profile_test.go b/internal/services/catalogwriteservice/test/unit/products/mappings_profile_test.go similarity index 81% rename from internal/services/catalog_write_service/test/unit/products/mappings_profile_test.go rename to internal/services/catalogwriteservice/test/unit/products/mappings_profile_test.go index ad2def27..651ee4a8 100644 --- a/internal/services/catalog_write_service/test/unit/products/mappings_profile_test.go +++ b/internal/services/catalogwriteservice/test/unit/products/mappings_profile_test.go @@ -8,9 +8,9 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dto/v1" + dtoV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/dtos/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/products/models" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/test_fixtures/unit_test" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/catalogwriteservice/internal/shared/testfixtures/unittest" "github.com/brianvoe/gofakeit/v6" uuid "github.com/satori/go.uuid" @@ -18,19 +18,19 @@ import ( ) type mappingProfileUnitTests struct { - *unit_test.UnitTestSharedFixture + *unittest.UnitTestSharedFixture } func TestMappingProfileUnit(t *testing.T) { suite.Run( t, - &mappingProfileUnitTests{UnitTestSharedFixture: unit_test.NewUnitTestSharedFixture(t)}, + &mappingProfileUnitTests{UnitTestSharedFixture: unittest.NewUnitTestSharedFixture(t)}, ) } func (m *mappingProfileUnitTests) Test_Mappings() { productModel := &models.Product{ - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Name: gofakeit.Name(), CreatedAt: time.Now(), Description: gofakeit.EmojiDescription(), @@ -38,7 +38,7 @@ func (m *mappingProfileUnitTests) Test_Mappings() { } productDto := &dtoV1.ProductDto{ - ProductId: uuid.NewV4(), + Id: uuid.NewV4(), Name: gofakeit.Name(), CreatedAt: time.Now(), Description: gofakeit.EmojiDescription(), @@ -48,7 +48,7 @@ func (m *mappingProfileUnitTests) Test_Mappings() { m.Run("Should_Map_Product_To_ProductDto", func() { d, err := mapper.Map[*dtoV1.ProductDto](productModel) m.Require().NoError(err) - m.Equal(productModel.ProductId, d.ProductId) + m.Equal(productModel.Id, d.Id) m.Equal(productModel.Name, d.Name) }) @@ -61,7 +61,7 @@ func (m *mappingProfileUnitTests) Test_Mappings() { m.Run("Should_Map_ProductDto_To_Product", func() { d, err := mapper.Map[*models.Product](productDto) m.Require().NoError(err) - m.Equal(productDto.ProductId, d.ProductId) + m.Equal(productDto.Id, d.Id) m.Equal(productDto.Name, d.Name) }) diff --git a/internal/services/order_service/.env b/internal/services/order_service/.env deleted file mode 100644 index d883e79f..00000000 --- a/internal/services/order_service/.env +++ /dev/null @@ -1 +0,0 @@ -PROJECT_NAME=order_service diff --git a/internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go b/internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go deleted file mode 100644 index 6bf878de..00000000 --- a/internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go +++ /dev/null @@ -1,21 +0,0 @@ -package infrastructure - -import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" -) - -type InfrastructureConfigurator struct { - contracts.Application -} - -func NewInfrastructureConfigurator(app contracts.Application) *InfrastructureConfigurator { - return &InfrastructureConfigurator{ - Application: app, - } -} - -func (ic *InfrastructureConfigurator) ConfigInfrastructures() { - ic.ResolveFunc(func() error { - return nil - }) -} diff --git a/internal/services/order_service/.air.toml b/internal/services/orderservice/.air.toml similarity index 100% rename from internal/services/order_service/.air.toml rename to internal/services/orderservice/.air.toml diff --git a/internal/services/order_service/.dockerignore b/internal/services/orderservice/.dockerignore similarity index 100% rename from internal/services/order_service/.dockerignore rename to internal/services/orderservice/.dockerignore diff --git a/internal/services/orderservice/.env b/internal/services/orderservice/.env new file mode 100644 index 00000000..0cedb4bf --- /dev/null +++ b/internal/services/orderservice/.env @@ -0,0 +1 @@ +PROJECT_NAME=orderservice diff --git a/internal/services/order_service/.golangci.yml b/internal/services/orderservice/.golangci.yml similarity index 100% rename from internal/services/order_service/.golangci.yml rename to internal/services/orderservice/.golangci.yml diff --git a/internal/services/order_service/Dockerfile b/internal/services/orderservice/Dockerfile similarity index 100% rename from internal/services/order_service/Dockerfile rename to internal/services/orderservice/Dockerfile diff --git a/internal/services/order_service/Makefile b/internal/services/orderservice/Makefile similarity index 100% rename from internal/services/order_service/Makefile rename to internal/services/orderservice/Makefile diff --git a/internal/services/order_service/arch-go.yml b/internal/services/orderservice/arch-go.yml similarity index 100% rename from internal/services/order_service/arch-go.yml rename to internal/services/orderservice/arch-go.yml diff --git a/internal/services/order_service/client.http b/internal/services/orderservice/client.http similarity index 100% rename from internal/services/order_service/client.http rename to internal/services/orderservice/client.http diff --git a/internal/services/order_service/cmd/app/main.go b/internal/services/orderservice/cmd/app/main.go similarity index 100% rename from internal/services/order_service/cmd/app/main.go rename to internal/services/orderservice/cmd/app/main.go diff --git a/internal/services/orderservice/cmd/migration/.gitkeep b/internal/services/orderservice/cmd/migration/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/internal/services/order_service/config/config.development.json b/internal/services/orderservice/config/config.development.json similarity index 59% rename from internal/services/order_service/config/config.development.json rename to internal/services/orderservice/config/config.development.json index 51199dc2..6e8c1275 100644 --- a/internal/services/order_service/config/config.development.json +++ b/internal/services/orderservice/config/config.development.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "order_service", + "serviceName": "orderservice", "deliveryType": "http" }, "grpcOptions": { - "name": "order_service", + "name": "orderservice", "port": ":6005", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "order_service", + "name": "orderservice", "port": ":8000", "development": true, "timeout": 30, @@ -48,24 +48,43 @@ "httpPort": 15672 } }, - "OpenTelemetryOptions": { + "tracingOptions": { "enable": true, "serviceName": "orders-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice", - "id": 3, + "instrumentationName": "io.opentelemetry.traces.orders-service", + "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4320", + "enabled": true }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" }, - "otelMetricsOptions": { - "port": ":2003", - "metricsRoutePath": "/metrics", - "name": "orders-service" - } + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": { + "uptrace-dsn": "https://@uptrace.dev/" + } + }, + { + "name": "elastic-apm", + "enabled": false, + "otlpEndpoint": "host.docker.internal:4317", + "otlpHeaders": { + "Authorization": "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + } + } + ] + }, + "metricsOptions": { + "metricsRoutePath": "/metrics", + "serviceName": "orders-service", + "instrumentationName": "io.opentelemetry.metrics.orders-service" }, "eventStoreDbOptions": { "host": "localhost", diff --git a/internal/services/order_service/config/config.go b/internal/services/orderservice/config/config.go similarity index 88% rename from internal/services/order_service/config/config.go rename to internal/services/orderservice/config/config.go index 9f60cc2e..5348a876 100644 --- a/internal/services/order_service/config/config.go +++ b/internal/services/orderservice/config/config.go @@ -4,14 +4,14 @@ import ( "strings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" ) type Config struct { AppOptions AppOptions `mapstructure:"appOptions"` } -func NewConfig(environment environemnt.Environment) (*Config, error) { +func NewConfig(environment environment.Environment) (*Config, error) { cfg, err := config.BindConfig[*Config](environment) if err != nil { return nil, err diff --git a/internal/services/order_service/config/config.test.json b/internal/services/orderservice/config/config.test.json similarity index 59% rename from internal/services/order_service/config/config.test.json rename to internal/services/orderservice/config/config.test.json index e6402e88..a416ed3a 100644 --- a/internal/services/order_service/config/config.test.json +++ b/internal/services/orderservice/config/config.test.json @@ -1,16 +1,16 @@ { "appOptions": { - "serviceName": "order_service", + "serviceName": "orderservice", "deliveryType": "http" }, "grpcOptions": { - "name": "order_service", + "name": "orderservice", "port": ":3302", "host": "localhost", "development": true }, "echoHttpOptions": { - "name": "order_service", + "name": "orderservice", "port": ":6002", "development": true, "timeout": 30, @@ -46,24 +46,43 @@ "httpPort": 15672 } }, - "OpenTelemetryOptions": { + "tracingOptions": { "enable": true, "serviceName": "orders-service", - "instrumentationName": "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice", - "id": 3, + "instrumentationName": "io.opentelemetry.traces.orders-service", + "id": 1, + "useStdout": false, "alwaysOnSampler": true, "jaegerExporterOptions": { - "agentHost": "localhost", - "agentPort": "6831" + "otlpEndpoint": "localhost:4320", + "enabled": true }, "zipkinExporterOptions": { "url": "http://localhost:9411/api/v2/spans" }, - "otelMetricsOptions": { - "port": ":3003", - "metricsRoutePath": "/metrics", - "name": "orders-service" - } + "otlpProviders": [ + { + "name": "uptrace", + "enabled": false, + "otlpEndpoint": "otlp.uptrace.dev:4317", + "otlpHeaders": { + "uptrace-dsn": "https://@uptrace.dev/" + } + }, + { + "name": "elastic-apm", + "enabled": false, + "otlpEndpoint": "host.docker.internal:4317", + "otlpHeaders": { + "Authorization": "Bearer ${ELASTIC_APM_SECRET_TOKEN}" + } + } + ] + }, + "metricsOptions": { + "metricsRoutePath": "/metrics", + "serviceName": "orders-service", + "instrumentationName": "io.opentelemetry.metrics.orders-service" }, "eventStoreDbOptions": { "host": "localhost", diff --git a/internal/services/order_service/config/config_fx.go b/internal/services/orderservice/config/config_fx.go similarity index 100% rename from internal/services/order_service/config/config_fx.go rename to internal/services/orderservice/config/config_fx.go diff --git a/internal/services/order_service/docs/docs.go b/internal/services/orderservice/docs/docs.go similarity index 100% rename from internal/services/order_service/docs/docs.go rename to internal/services/orderservice/docs/docs.go diff --git a/internal/services/order_service/docs/swagger.json b/internal/services/orderservice/docs/swagger.json similarity index 100% rename from internal/services/order_service/docs/swagger.json rename to internal/services/orderservice/docs/swagger.json diff --git a/internal/services/order_service/docs/swagger.yaml b/internal/services/orderservice/docs/swagger.yaml similarity index 100% rename from internal/services/order_service/docs/swagger.yaml rename to internal/services/orderservice/docs/swagger.yaml diff --git a/internal/services/order_service/go.mod b/internal/services/orderservice/go.mod similarity index 70% rename from internal/services/order_service/go.mod rename to internal/services/orderservice/go.mod index 776edaf6..21e775e4 100644 --- a/internal/services/order_service/go.mod +++ b/internal/services/orderservice/go.mod @@ -8,18 +8,18 @@ replace github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg => ../../ require ( emperror.dev/errors v0.8.1 github.com/EventStore/EventStore-Client-Go v1.0.2 - github.com/brianvoe/gofakeit/v6 v6.23.2 - github.com/elastic/go-elasticsearch/v8 v8.9.0 + github.com/brianvoe/gofakeit/v6 v6.25.0 + github.com/elastic/go-elasticsearch/v8 v8.10.0 github.com/gavv/httpexpect/v2 v2.15.0 github.com/go-ozzo/ozzo-validation v3.6.0+incompatible github.com/go-playground/validator v9.31.0+incompatible github.com/goccy/go-json v0.10.2 github.com/labstack/echo/v4 v4.11.1 github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg v0.0.0-20230831075934-be8df319f588 - github.com/mehdihadeli/go-mediatr v1.1.10 + github.com/mehdihadeli/go-mediatr v1.3.0 github.com/michaelklishin/rabbit-hole v1.5.0 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/pterm/pterm v0.12.69 github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.7.0 @@ -27,11 +27,11 @@ require ( github.com/swaggo/echo-swagger v1.4.1 github.com/swaggo/swag v1.16.2 go.mongodb.org/mongo-driver v1.12.1 - go.opentelemetry.io/otel v1.17.0 - go.opentelemetry.io/otel/metric v1.17.0 - go.opentelemetry.io/otel/trace v1.17.0 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/metric v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 go.uber.org/fx v1.20.0 - google.golang.org/grpc v1.57.0 + google.golang.org/grpc v1.58.2 google.golang.org/protobuf v1.31.0 ) @@ -44,6 +44,7 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect github.com/ahmetb/go-linq/v3 v3.2.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -56,22 +57,26 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/console v1.0.3 // indirect - github.com/containerd/containerd v1.7.5 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/doug-martin/goqu/v9 v9.18.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/glebarez/sqlite v1.10.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.20.9 // indirect @@ -93,6 +98,7 @@ require ( github.com/gookit/color v1.5.4 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/imkira/go-interpol v1.1.0 // indirect @@ -112,12 +118,13 @@ require ( github.com/joho/godotenv v1.5.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kamva/mgm/v3 v3.5.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -135,42 +142,51 @@ require ( github.com/nolleh/caption_json_formatter v0.2.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.9 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rabbitmq/amqp091-go v1.8.1 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect - github.com/redis/go-redis/v9 v9.0.5 // indirect + github.com/redis/go-redis/v9 v9.2.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/samber/lo v1.38.1 // indirect github.com/sanity-io/litter v1.5.5 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect - github.com/streadway/amqp v1.1.0 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect - github.com/testcontainers/testcontainers-go v0.23.0 // indirect + github.com/testcontainers/testcontainers-go v0.25.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect - github.com/uptrace/bun v1.1.14 // indirect - github.com/uptrace/bun/driver/pgdriver v1.1.14 // indirect + github.com/ulule/limiter/v3 v3.11.2 // indirect + github.com/uptrace/bun v1.1.16 // indirect + github.com/uptrace/bun/driver/pgdriver v1.1.16 // indirect + github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 // indirect + github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.34.0 // indirect + github.com/valyala/fasthttp v1.47.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -183,18 +199,25 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yudai/gojsondiff v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 // indirect - go.opentelemetry.io/contrib/propagators/ot v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.40.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.17.0 // indirect - go.opentelemetry.io/otel/sdk v1.17.0 // indirect - go.opentelemetry.io/otel/sdk/metric v0.40.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/host v0.45.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.19.0 // indirect + go.opentelemetry.io/otel/sdk v1.19.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.25.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/mod v0.12.0 // indirect @@ -205,13 +228,19 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/postgres v1.5.2 // indirect - gorm.io/gorm v1.25.4 // indirect + gorm.io/gorm v1.25.5 // indirect + gorm.io/plugin/opentelemetry v0.1.4 // indirect mellium.im/sasl v0.3.1 // indirect + modernc.org/libc v1.24.1 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.6.0 // indirect + modernc.org/sqlite v1.25.0 // indirect moul.io/http2curl/v2 v2.3.0 // indirect ) diff --git a/internal/services/order_service/go.sum b/internal/services/orderservice/go.sum similarity index 87% rename from internal/services/order_service/go.sum rename to internal/services/orderservice/go.sum index 3d8dbc97..909258ac 100644 --- a/internal/services/order_service/go.sum +++ b/internal/services/orderservice/go.sum @@ -33,8 +33,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= +cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -83,8 +83,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= @@ -93,7 +93,6 @@ github.com/ahmetb/go-linq/v3 v3.2.0 h1:BEuMfp+b59io8g5wYzNoFe9pWPalRklhlhbiU3hYZ github.com/ahmetb/go-linq/v3 v3.2.0/go.mod h1:haQ3JfOeWK8HpVxMtHHEMPVgBKiYyQ+f1/kLZh/cj9U= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= @@ -108,12 +107,14 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.23.2 h1:lVde18uhad5wII/f5RMVFLtdQNE0HaGFuBUXmYKk8i8= -github.com/brianvoe/gofakeit/v6 v6.23.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/brianvoe/gofakeit/v6 v6.25.0 h1:ZpFjktOpLZUeF8q223o0rUuXtA+m5qW5srjvVi+JkXk= +github.com/brianvoe/gofakeit/v6 v6.25.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= @@ -138,8 +139,8 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.5 h1:i9T9XpAWMe11BHMN7pu1BZqOGjXaKTPyz2v+KYOZgkY= -github.com/containerd/containerd v1.7.5/go.mod h1:ieJNCSzASw2shSGYLHx8NAE7WsZ/gEigo5fQ78W5Zvw= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= @@ -163,12 +164,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= -github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -177,19 +178,21 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elastic/elastic-transport-go/v8 v8.0.0-20230329154755-1a3c63de0db6/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= github.com/elastic/elastic-transport-go/v8 v8.3.0 h1:DJGxovyQLXGr62e9nDMPSxRyWION0Bh6d9eCFBriiHo= github.com/elastic/elastic-transport-go/v8 v8.3.0/go.mod h1:87Tcz8IVNe6rVSLdBux1o/PEItLtyabHU3naC7IoqKI= -github.com/elastic/go-elasticsearch/v8 v8.9.0 h1:8xtmYjUkqtahl50E0Bg/wjKI7K63krJrrLipbNj/fCU= -github.com/elastic/go-elasticsearch/v8 v8.9.0/go.mod h1:NGmpvohKiRHXI0Sw4fuUGn6hYOmAXlyCphKpzVBiqDE= +github.com/elastic/go-elasticsearch/v8 v8.10.0 h1:ALg3DMxSrx07YmeMNcfPf7cFh1Ep2+Qa19EOXTbwr2k= +github.com/elastic/go-elasticsearch/v8 v8.10.0/go.mod h1:NGmpvohKiRHXI0Sw4fuUGn6hYOmAXlyCphKpzVBiqDE= 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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -205,6 +208,10 @@ github.com/gavv/httpexpect/v2 v2.15.0 h1:CCnFk9of4l4ijUhnMxyoEpJsIIBKcuWIFLMwwGT github.com/gavv/httpexpect/v2 v2.15.0/go.mod h1:7myOP3A3VyS4+qnA4cm8DAad8zMN+7zxDB80W9f8yIc= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= +github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= +github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -215,6 +222,9 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= @@ -262,6 +272,8 @@ github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8 github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 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/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -306,6 +318,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -323,8 +336,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -344,6 +357,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/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/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -433,9 +448,8 @@ github.com/kamva/mgm/v3 v3.5.0/go.mod h1:F4J1hZnXQMkqL3DZgR7Z7BOuiTqQG/JTic3Yzli 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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -474,6 +488,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -498,12 +515,14 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= -github.com/mehdihadeli/go-mediatr v1.1.10 h1:NAzg4065c90lgYeb+Vzbd2WKH0tUFpxzL0mpx6hkU/A= -github.com/mehdihadeli/go-mediatr v1.1.10/go.mod h1:lwgZl7qVL/RKomObBblhG3uEte/r4nJDV95Vd+nGrMw= +github.com/mehdihadeli/go-mediatr v1.3.0 h1:hrb5Scp/nsiR3Y62mjZ0Tc5UX/dRJl4nDFkINBEIESA= +github.com/mehdihadeli/go-mediatr v1.3.0/go.mod h1:lsG+hyH+pEOhmZiZl0KPO72BcZiEReF03CBk4GVJB0k= github.com/michaelklishin/rabbit-hole v1.5.0 h1:Bex27BiFDsijCM9D0ezSHqyy0kehpYHuNKaPqq/a4RM= github.com/michaelklishin/rabbit-hole v1.5.0/go.mod h1:vvI1uOitYZi0O5HEGXhaWC1XT80Gy+HvFheJ+5Krlhk= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -535,19 +554,19 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= +github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= @@ -571,15 +590,18 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= @@ -595,8 +617,11 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -620,6 +645,12 @@ github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvW github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= +github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +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/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= @@ -631,8 +662,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -678,28 +709,39 @@ github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= +github.com/testcontainers/testcontainers-go v0.25.0 h1:erH6cQjsaJrH+rJDU9qIf89KFdhK0Bft0aEZHlYC3Vs= +github.com/testcontainers/testcontainers-go v0.25.0/go.mod h1:4sC9SiJyzD1XFi59q8umTQYWxnkweEc5OjVtTUlJzqQ= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= -github.com/uptrace/bun v1.1.14 h1:S5vvNnjEynJ0CvnrBOD7MIRW7q/WbtvFXrdfy0lddAM= -github.com/uptrace/bun v1.1.14/go.mod h1:RHk6DrIisO62dv10pUOJCz5MphXThuOTpVNYEYv7NI8= -github.com/uptrace/bun/driver/pgdriver v1.1.14 h1:V2Etm7mLGS3mhx8ddxZcUnwZLX02Jmq9JTlo0sNVDhA= -github.com/uptrace/bun/driver/pgdriver v1.1.14/go.mod h1:D4FjWV9arDYct6sjMJhFoyU71SpllZRHXFRRP2Kd0Kw= +github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= +github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= +github.com/uptrace/bun v1.1.16 h1:cn9cgEMFwcyYRsQLfxCRMUxyK1WaHwOVrR3TvzEFZ/A= +github.com/uptrace/bun v1.1.16/go.mod h1:7HnsMRRvpLFUcquJxp22JO8PsWKpFQO/gNXqqsuGWg8= +github.com/uptrace/bun/driver/pgdriver v1.1.16 h1:b/NiSXk6Ldw7KLfMLbOqIkm4odHd7QiNOCPLqPFJjK4= +github.com/uptrace/bun/driver/pgdriver v1.1.16/go.mod h1:Rmfbc+7lx1z/umjMyAxkOHK81LgnGj71XC5YpA6k1vU= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3 h1:m5eNyOhch/7tyK6aN6eRRpNoD1vM8PNh64dA05X22Js= +github.com/uptrace/opentelemetry-go-extra/otellogrus v0.2.3/go.mod h1:APPUXm9BbpH7NFkfpbw04raZSitzl19/3NOCu0rbI4E= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3 h1:LyGS9cIZV0YVhE81zwfMhIE2l2flcj3wn5IoK4VkbWA= +github.com/uptrace/opentelemetry-go-extra/otelutil v0.2.3/go.mod h1:RvCYhPchLhvQ9l9C9goblbgO7BaKt597kBMf5mgKyo0= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3 h1:2na5W81H38Z4qXCQCuzlcdSMiTWgPJ6XeZIArq6VIJE= +github.com/uptrace/opentelemetry-go-extra/otelzap v0.2.3/go.mod h1:9IVEh9mPv3NwFf99dVLX15FqVgdpZJ8RMDo/Cr0vK74= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4= -github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608= +github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= @@ -738,6 +780,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= @@ -748,32 +792,42 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0 h1:pcWR7mkuO5XMK3f0KeGpr70OTR9/ikPk0D1hHEd5dp4= -go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.43.0/go.mod h1:azTPpa9PvRDSNU/lQbe2CRDQoTcau5moOjS4EzhpyAY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0 h1:7XZai4VhA473clBrOqqHdjHBImGfyEtv0qW4nnn/kAo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.43.0/go.mod h1:1WpsUwjQrUJSNugfMlPn0rPRJ9Do7wwBgTBPK7MLiS4= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0 h1:hhSlPVi9AQwOmbMmptPNLfRZOLgENdRM2kb7z9LFe1A= -go.opentelemetry.io/contrib/propagators/b3 v1.18.0/go.mod h1:qtt+pEu23D7UVP+j33G4i7LopmVu8/6/IwGu3hEm100= -go.opentelemetry.io/contrib/propagators/ot v1.18.0 h1:VmzxO7BjUU6oo0ChcKuGdKaSR0vchPxwahHZl64zVUM= -go.opentelemetry.io/contrib/propagators/ot v1.18.0/go.mod h1:5VwcOJ7OjS0uPxaxuwKHwJtkt+EAC+cgjXleXMe51z4= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= -go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0 h1:9h6lCssr1j5aYVvWT6oc+ERB6R034zmsHjBRLyxrAR8= -go.opentelemetry.io/otel/exporters/prometheus v0.40.0/go.mod h1:5USWZ0ovyQB5CIM3IO3bGRSoDPMXiT3t+15gu8Zo9HQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0 h1:oi5+xMN3pflqWSd4EX6FiO+Cn3KbFBBzeQmD5LMIf0c= -go.opentelemetry.io/otel/exporters/zipkin v1.17.0/go.mod h1:pNir+S6/f0HFGfbXhobXLTFu60KtAzw8aGSUpt9A6VU= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/sdk/metric v0.40.0 h1:qOM29YaGcxipWjL5FzpyZDpCYrDREvX0mVlmXdOjCHU= -go.opentelemetry.io/otel/sdk/metric v0.40.0/go.mod h1:dWxHtdzdJvg+ciJUKLTKwrMe5P6Dv3FyDbh8UkfgkVs= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0 h1:bldpPC7XAv7f7LKTwNfRkNdzRhjtXaWybZFFa16dAb8= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.45.0/go.mod h1:xhkNpJG3D+kmuaciNTco7cdK27Fb77J9Iqcq5CMe4Y8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0 h1:1uzNKJDqZ6y6F5J6aKWgJjRREpKiGhBvKHlWon/bqB4= +go.opentelemetry.io/contrib/instrumentation/host v0.45.0/go.mod h1:vlqPvzDsmB4+jlERxBRXsdLCD6Q0LoBzxHqNXp3qvG4= +go.opentelemetry.io/contrib/propagators/ot v1.20.0 h1:duH7mgL6VGQH7e7QEAVOFkCQXWpCb4PjTtrhdrYrJRQ= +go.opentelemetry.io/contrib/propagators/ot v1.20.0/go.mod h1:gijQzxOq0JLj9lyZhTvqjDddGV/zaNagpPIn+2r8CEI= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0 h1:EGY0h5mGliP9o/nIkVuLI0vRiQqmsYOcbwCuotksO1o= +go.opentelemetry.io/otel/exporters/zipkin v1.19.0/go.mod h1:JQgTGJP11yi3o4GHzIWYodhPisxANdqxF1eHwDSnJrI= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= +go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -799,8 +853,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -817,7 +871,6 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= @@ -900,7 +953,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= @@ -914,8 +966,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -948,6 +1000,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -975,6 +1028,7 @@ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -992,16 +1046,17 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1166,8 +1221,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= +google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1184,8 +1243,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 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.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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= @@ -1233,8 +1292,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0= gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8= -gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= -gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/plugin/opentelemetry v0.1.4 h1:7p0ocWELjSSRI7NCKPW2mVe6h43YPini99sNJcbsTuc= +gorm.io/plugin/opentelemetry v0.1.4/go.mod h1:tndJHOdvPT0pyGhOb8E2209eXJCUxhC5UpKw7bGVWeI= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= @@ -1247,6 +1310,14 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= +modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= +modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= +modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA= +modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/services/order_service/internal/orders/configurations/mappings/mappings_profile.go b/internal/services/orderservice/internal/orders/configurations/mappings/mappings_profile.go similarity index 100% rename from internal/services/order_service/internal/orders/configurations/mappings/mappings_profile.go rename to internal/services/orderservice/internal/orders/configurations/mappings/mappings_profile.go diff --git a/internal/services/order_service/internal/orders/configurations/mediatr/mediator_configurations.go b/internal/services/orderservice/internal/orders/configurations/mediatr/mediator_configurations.go similarity index 100% rename from internal/services/order_service/internal/orders/configurations/mediatr/mediator_configurations.go rename to internal/services/orderservice/internal/orders/configurations/mediatr/mediator_configurations.go diff --git a/internal/services/order_service/internal/orders/configurations/orders_module_configurator.go b/internal/services/orderservice/internal/orders/configurations/orders_module_configurator.go similarity index 94% rename from internal/services/order_service/internal/orders/configurations/orders_module_configurator.go rename to internal/services/orderservice/internal/orders/configurations/orders_module_configurator.go index 91452983..b5ccc37d 100644 --- a/internal/services/order_service/internal/orders/configurations/orders_module_configurator.go +++ b/internal/services/orderservice/internal/orders/configurations/orders_module_configurator.go @@ -1,13 +1,13 @@ package configurations import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts/store" contracts2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" grpcServer "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/configurations/mappings" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/configurations/mediatr" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/repositories" @@ -40,13 +40,13 @@ func (c *OrdersModuleConfigurator) ConfigureOrdersModule() { orderAggregateStore store.AggregateStore[*aggregate.Order], tracer tracing.AppTracer, ) error { - // Config Orders Mappings + // config Orders Mappings err := mappings.ConfigureOrdersMappings() if err != nil { return err } - // Config Orders Mediators + // config Orders Mediators err = mediatr.ConfigOrdersMediator(logger, orderRepository, orderAggregateStore, tracer) if err != nil { return err @@ -58,7 +58,7 @@ func (c *OrdersModuleConfigurator) ConfigureOrdersModule() { } func (c *OrdersModuleConfigurator) MapOrdersEndpoints() { - // Config Orders Http Endpoints + // config Orders Http Endpoints c.ResolveFuncWithParamTag(func(endpoints []route.Endpoint) { for _, endpoint := range endpoints { endpoint.MapEndpoint() @@ -66,7 +66,7 @@ func (c *OrdersModuleConfigurator) MapOrdersEndpoints() { }, `group:"order-routes"`, ) - // Config Orders Grpc Endpoints + // config Orders Grpc Endpoints c.ResolveFunc( func(ordersGrpcServer grpcServer.GrpcServer, ordersMetrics *contracts.OrdersMetrics, logger logger.Logger, validator *validator.Validate) error { orderGrpcService := grpc.NewOrderGrpcService(logger, validator, ordersMetrics) diff --git a/internal/services/order_service/internal/orders/configurations/rabbitmq/rabbitmq_configurations.go b/internal/services/orderservice/internal/orders/configurations/rabbitmq/rabbitmq_configurations.go similarity index 100% rename from internal/services/order_service/internal/orders/configurations/rabbitmq/rabbitmq_configurations.go rename to internal/services/orderservice/internal/orders/configurations/rabbitmq/rabbitmq_configurations.go diff --git a/internal/services/order_service/internal/orders/contracts/params/order_projection_params.go b/internal/services/orderservice/internal/orders/contracts/params/order_projection_params.go similarity index 100% rename from internal/services/order_service/internal/orders/contracts/params/order_projection_params.go rename to internal/services/orderservice/internal/orders/contracts/params/order_projection_params.go diff --git a/internal/services/order_service/internal/orders/contracts/params/order_route_params.go b/internal/services/orderservice/internal/orders/contracts/params/order_route_params.go similarity index 100% rename from internal/services/order_service/internal/orders/contracts/params/order_route_params.go rename to internal/services/orderservice/internal/orders/contracts/params/order_route_params.go diff --git a/internal/services/order_service/internal/orders/contracts/repositories/order_repository.go b/internal/services/orderservice/internal/orders/contracts/repositories/order_repository.go similarity index 100% rename from internal/services/order_service/internal/orders/contracts/repositories/order_repository.go rename to internal/services/orderservice/internal/orders/contracts/repositories/order_repository.go diff --git a/internal/services/order_service/internal/orders/data/repositories/elastic_order_read_repository.go b/internal/services/orderservice/internal/orders/data/repositories/elastic_order_read_repository.go similarity index 100% rename from internal/services/order_service/internal/orders/data/repositories/elastic_order_read_repository.go rename to internal/services/orderservice/internal/orders/data/repositories/elastic_order_read_repository.go diff --git a/internal/services/order_service/internal/orders/data/repositories/mongo_order_read_repository.go b/internal/services/orderservice/internal/orders/data/repositories/mongo_order_read_repository.go similarity index 95% rename from internal/services/order_service/internal/orders/data/repositories/mongo_order_read_repository.go rename to internal/services/orderservice/internal/orders/data/repositories/mongo_order_read_repository.go index 7215d647..8407c0da 100644 --- a/internal/services/order_service/internal/orders/data/repositories/mongo_order_read_repository.go +++ b/internal/services/orderservice/internal/orders/data/repositories/mongo_order_read_repository.go @@ -8,6 +8,7 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + utils2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/repositories" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/models/orders/read_models" @@ -57,7 +58,7 @@ func (m mongoOrderReadRepository) GetAllOrders( result, err := mongodb.Paginate[*read_models.OrderReadModel](ctx, listQuery, collection, nil) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -98,7 +99,7 @@ func (m mongoOrderReadRepository) SearchOrders( result, err := mongodb.Paginate[*read_models.OrderReadModel](ctx, listQuery, collection, filter) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -135,7 +136,7 @@ func (m mongoOrderReadRepository) GetOrderById( if err == mongo.ErrNoDocuments { return nil, nil } - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -172,7 +173,7 @@ func (m mongoOrderReadRepository) GetOrderByOrderId( if err == mongo.ErrNoDocuments { return nil, nil } - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -206,7 +207,7 @@ func (m mongoOrderReadRepository) CreateOrder( collection := m.mongoClient.Database(m.mongoOptions.Database).Collection(orderCollection) _, err := collection.InsertOne(ctx, order, &options.InsertOneOptions{}) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -242,7 +243,7 @@ func (m mongoOrderReadRepository) UpdateOrder( var updated read_models.OrderReadModel if err := collection.FindOneAndUpdate(ctx, bson.M{"_id": order.OrderId}, bson.M{"$set": order}, ops).Decode(&updated); err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, errors.WrapIf( err, @@ -274,7 +275,7 @@ func (m mongoOrderReadRepository) DeleteOrderByID(ctx context.Context, uuid uuid collection := m.mongoClient.Database(m.mongoOptions.Database).Collection(orderCollection) if err := collection.FindOneAndDelete(ctx, bson.M{"_id": uuid.String()}).Err(); err != nil { - return tracing.TraceErrFromSpan(span, errors.WrapIf(err, fmt.Sprintf( + return utils2.TraceErrFromSpan(span, errors.WrapIf(err, fmt.Sprintf( "[mongoOrderReadRepository_DeleteOrderByID.FindOneAndDelete] error in deleting order with id %d from the database.", uuid, ))) diff --git a/internal/services/order_service/internal/orders/dtos/v1/order_dto.go b/internal/services/orderservice/internal/orders/dtos/v1/order_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/dtos/v1/order_dto.go rename to internal/services/orderservice/internal/orders/dtos/v1/order_dto.go diff --git a/internal/services/order_service/internal/orders/dtos/v1/order_read_dto.go b/internal/services/orderservice/internal/orders/dtos/v1/order_read_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/dtos/v1/order_read_dto.go rename to internal/services/orderservice/internal/orders/dtos/v1/order_read_dto.go diff --git a/internal/services/order_service/internal/orders/dtos/v1/shopItem_dto.go b/internal/services/orderservice/internal/orders/dtos/v1/shopItem_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/dtos/v1/shopItem_dto.go rename to internal/services/orderservice/internal/orders/dtos/v1/shopItem_dto.go diff --git a/internal/services/order_service/internal/orders/dtos/v1/shopItem_read_dto.go b/internal/services/orderservice/internal/orders/dtos/v1/shopItem_read_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/dtos/v1/shopItem_read_dto.go rename to internal/services/orderservice/internal/orders/dtos/v1/shopItem_read_dto.go diff --git a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go similarity index 77% rename from internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go rename to internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go index b49d02ca..a420de8f 100644 --- a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go +++ b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_delivery_address_error.go @@ -1,7 +1,7 @@ package domainExceptions import ( - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) @@ -23,10 +23,14 @@ func NewInvalidDeliveryAddressError(message string) error { return errors.WithStackIf(br) } +func (i *invalidDeliveryAddressError) isInvalidAddress() bool { + return true +} + func IsInvalidDeliveryAddressError(err error) bool { - var ia InvalidDeliveryAddressError + var ia *invalidDeliveryAddressError if errors.As(err, &ia) { - return true + return ia.isInvalidAddress() } return false diff --git a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_email_error.go b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_email_error.go similarity index 75% rename from internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_email_error.go rename to internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_email_error.go index 4d8bc13c..b5a30ec2 100644 --- a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/invalid_email_error.go +++ b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/invalid_email_error.go @@ -1,7 +1,7 @@ package domainExceptions import ( - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) @@ -24,10 +24,15 @@ func NewInvalidEmailAddressError(message string) error { return errors.WithStackIf(br) } +func (i *invalidEmailAddressError) isInvalidEmailAddressError() bool { + return true +} + func IsInvalidEmailAddressError(err error) bool { - var ie InvalidEmailAddressError + var ie *invalidEmailAddressError + if errors.As(err, &ie) { - return true + return ie.isInvalidEmailAddressError() } return false diff --git a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go similarity index 63% rename from internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go rename to internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go index a81d2e5e..fc06c48b 100644 --- a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go +++ b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_domain_errors_test.go @@ -4,7 +4,8 @@ import ( "fmt" "testing" - errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/error_utils" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + errorUtils "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils/errorutils" "github.com/stretchr/testify/assert" ) @@ -22,13 +23,33 @@ func Test_Order_Not_Found_Error(t *testing.T) { } func Test_Invalid_Delivery_Address_Error(t *testing.T) { + t.Parallel() + err := NewInvalidDeliveryAddressError("address is not valid") assert.True(t, IsInvalidDeliveryAddressError(err)) fmt.Println(errorUtils.ErrorsWithStack(err)) } +func Test_Is_Not_Invalid_Delivery_Address_Error( + t *testing.T, +) { + t.Parallel() + + err := customErrors.NewBadRequestError("address is not valid") + assert.False(t, IsInvalidDeliveryAddressError(err)) +} + func Test_InvalidEmail_Address_Error(t *testing.T) { + t.Parallel() + err := NewInvalidEmailAddressError("email address is not valid") assert.True(t, IsInvalidEmailAddressError(err)) fmt.Println(errorUtils.ErrorsWithStack(err)) } + +func Test_Is_Not_InvalidEmail_Address_Error(t *testing.T) { + t.Parallel() + + err := customErrors.NewBadRequestError("email address is not valid") + assert.False(t, IsInvalidEmailAddressError(err)) +} diff --git a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go similarity index 75% rename from internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go rename to internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go index 148b51dd..ddbc4413 100644 --- a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go +++ b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_items_required_errors.go @@ -1,7 +1,7 @@ package domainExceptions import ( - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) @@ -24,10 +24,14 @@ func NewOrderShopItemsRequiredError(message string) error { return errors.WithStackIf(br) } +func (i *orderShopItemsRequiredError) isOrderShopItemsRequiredError() bool { + return true +} + func IsOrderShopItemsRequiredError(err error) bool { - var os OrderShopItemsRequiredError + var os *orderShopItemsRequiredError if errors.As(err, &os) { - return true + return os.isOrderShopItemsRequiredError() } return false diff --git a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_not_found_error.go b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_not_found_error.go similarity index 67% rename from internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_not_found_error.go rename to internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_not_found_error.go index 4346d2a1..b9d77100 100644 --- a/internal/services/order_service/internal/orders/exceptions/domain_exceptions/order_not_found_error.go +++ b/internal/services/orderservice/internal/orders/exceptions/domain_exceptions/order_not_found_error.go @@ -3,7 +3,7 @@ package domainExceptions import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "emperror.dev/errors" ) @@ -17,7 +17,9 @@ type OrderNotFoundError interface { } func NewOrderNotFoundError(id int) error { - notFound := customErrors.NewNotFoundError(fmt.Sprintf("order with id %d not found", id)) + notFound := customErrors.NewNotFoundError( + fmt.Sprintf("order with id %d not found", id), + ) customErr := customErrors.GetCustomError(notFound).(customErrors.NotFoundError) br := &orderNotFoundError{ NotFoundError: customErr, @@ -26,10 +28,14 @@ func NewOrderNotFoundError(id int) error { return errors.WithStackIf(br) } +func (i *orderNotFoundError) isorderNotFoundError() bool { + return true +} + func IsOrderNotFoundError(err error) bool { - var os OrderNotFoundError + var os *orderNotFoundError if errors.As(err, &os) { - return true + return os.isorderNotFoundError() } return false diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/commands/create_order.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/commands/create_order.go similarity index 100% rename from internal/services/order_service/internal/orders/features/creating_order/v1/commands/create_order.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/commands/create_order.go diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/commands/create_order_handler.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/commands/create_order_handler.go similarity index 92% rename from internal/services/order_service/internal/orders/features/creating_order/v1/commands/create_order_handler.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/commands/create_order_handler.go index d00ebcc4..fa3496f3 100644 --- a/internal/services/order_service/internal/orders/features/creating_order/v1/commands/create_order_handler.go +++ b/internal/services/orderservice/internal/orders/features/creating_order/v1/commands/create_order_handler.go @@ -5,11 +5,12 @@ import ( "fmt" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts/store" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" customAttribute "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/models/orders/aggregate" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/models/orders/value_objects" @@ -43,7 +44,7 @@ func (c *CreateOrderHandler) Handle( shopItems, err := mapper.Map[[]*value_objects.ShopItem](command.ShopItems) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -61,7 +62,7 @@ func (c *CreateOrderHandler) Handle( command.CreatedAt, ) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -72,7 +73,7 @@ func (c *CreateOrderHandler) Handle( _, err = c.aggregateStore.Store(order, nil, ctx) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go similarity index 95% rename from internal/services/order_service/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go index 6b63171c..22c26e63 100644 --- a/internal/services/order_service/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go +++ b/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos/create_order_request_dto.go @@ -1,7 +1,7 @@ package dtos import ( - customTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/custom_types" + customTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/customtypes" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" ) diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/dtos/create_order_response_dto.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos/create_order_response_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/features/creating_order/v1/dtos/create_order_response_dto.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/dtos/create_order_response_dto.go diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go similarity index 97% rename from internal/services/order_service/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go index ddf25504..100be0c7 100644 --- a/internal/services/order_service/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go +++ b/internal/services/orderservice/internal/orders/features/creating_order/v1/endpoints/create_order_endpoint.go @@ -5,9 +5,9 @@ import ( "net/http" "time" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/params" createOrderCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/commands" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos" diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/events/domain_events/order_created.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/events/domain_events/order_created.go similarity index 96% rename from internal/services/order_service/internal/orders/features/creating_order/v1/events/domain_events/order_created.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/events/domain_events/order_created.go index 4c9d2af0..c7de5061 100644 --- a/internal/services/order_service/internal/orders/features/creating_order/v1/events/domain_events/order_created.go +++ b/internal/services/orderservice/internal/orders/features/creating_order/v1/events/domain_events/order_created.go @@ -4,8 +4,8 @@ import ( "time" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" domainExceptions "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/exceptions/domain_exceptions" diff --git a/internal/services/order_service/internal/orders/features/creating_order/v1/events/integration_events/order_created.go b/internal/services/orderservice/internal/orders/features/creating_order/v1/events/integration_events/order_created.go similarity index 95% rename from internal/services/order_service/internal/orders/features/creating_order/v1/events/integration_events/order_created.go rename to internal/services/orderservice/internal/orders/features/creating_order/v1/events/integration_events/order_created.go index 2487ef87..1ad32d0b 100644 --- a/internal/services/order_service/internal/orders/features/creating_order/v1/events/integration_events/order_created.go +++ b/internal/services/orderservice/internal/orders/features/creating_order/v1/events/integration_events/order_created.go @@ -1,7 +1,7 @@ package integrationEvents import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/types" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/types" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" uuid "github.com/satori/go.uuid" diff --git a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_request_dto.go b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_request_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_request_dto.go rename to internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_request_dto.go diff --git a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_response_dto.go b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_response_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_response_dto.go rename to internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos/get_order_by_id_response_dto.go diff --git a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go similarity index 97% rename from internal/services/order_service/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go rename to internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go index c193c9ab..521c4031 100644 --- a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go +++ b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/endpoints/get_order_by_id_endpoint.go @@ -4,9 +4,9 @@ import ( "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/params" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries" diff --git a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id.go b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id.go rename to internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id.go diff --git a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go similarity index 91% rename from internal/services/order_service/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go rename to internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go index 2051893c..b18201f6 100644 --- a/internal/services/order_service/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go +++ b/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/queries/get_order_by_id_handler.go @@ -4,11 +4,12 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/repositories" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/dtos" @@ -46,7 +47,7 @@ func (q *GetOrderByIdHandler) Handle( // get order by order-read id order, err := q.orderMongoRepository.GetOrderById(ctx, query.Id) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -62,7 +63,7 @@ func (q *GetOrderByIdHandler) Handle( // get order by order-write id order, err = q.orderMongoRepository.GetOrderByOrderId(ctx, query.Id) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -77,7 +78,7 @@ func (q *GetOrderByIdHandler) Handle( orderDto, err := mapper.Map[*dtosV1.OrderReadDto](order) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, diff --git a/internal/services/order_service/internal/orders/features/getting_orders/v1/dtos/get_orders_request_dto.go b/internal/services/orderservice/internal/orders/features/getting_orders/v1/dtos/get_orders_request_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_orders/v1/dtos/get_orders_request_dto.go rename to internal/services/orderservice/internal/orders/features/getting_orders/v1/dtos/get_orders_request_dto.go diff --git a/internal/services/order_service/internal/orders/features/getting_orders/v1/dtos/get_orders_response_dto.go b/internal/services/orderservice/internal/orders/features/getting_orders/v1/dtos/get_orders_response_dto.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_orders/v1/dtos/get_orders_response_dto.go rename to internal/services/orderservice/internal/orders/features/getting_orders/v1/dtos/get_orders_response_dto.go diff --git a/internal/services/order_service/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go b/internal/services/orderservice/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go similarity index 97% rename from internal/services/order_service/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go rename to internal/services/orderservice/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go index d0c0ad3d..f6dda661 100644 --- a/internal/services/order_service/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go +++ b/internal/services/orderservice/internal/orders/features/getting_orders/v1/endpoints/get_orders_endpoint.go @@ -4,9 +4,9 @@ import ( "fmt" "net/http" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/params" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_orders/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_orders/v1/queries" diff --git a/internal/services/order_service/internal/orders/features/getting_orders/v1/queries/get_orders.go b/internal/services/orderservice/internal/orders/features/getting_orders/v1/queries/get_orders.go similarity index 100% rename from internal/services/order_service/internal/orders/features/getting_orders/v1/queries/get_orders.go rename to internal/services/orderservice/internal/orders/features/getting_orders/v1/queries/get_orders.go diff --git a/internal/services/order_service/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go b/internal/services/orderservice/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go similarity index 91% rename from internal/services/order_service/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go rename to internal/services/orderservice/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go index eeffb60d..5c49f43c 100644 --- a/internal/services/order_service/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go +++ b/internal/services/orderservice/internal/orders/features/getting_orders/v1/queries/get_orders_handler.go @@ -3,10 +3,11 @@ package queries import ( "context" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + utils2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/repositories" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" @@ -41,7 +42,7 @@ func (c *GetOrdersHandler) Handle( products, err := c.mongoOrderReadRepository.GetAllOrders(ctx, query.ListQuery) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -52,7 +53,7 @@ func (c *GetOrdersHandler) Handle( listResultDto, err := utils.ListResultToListResultDto[*dtosV1.OrderReadDto](products) if err != nil { - return nil, tracing.TraceErrFromSpan( + return nil, utils2.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, diff --git a/internal/services/order_service/internal/orders/features/submitting_order/v1/commands/submit_order.go b/internal/services/orderservice/internal/orders/features/submitting_order/v1/commands/submit_order.go similarity index 100% rename from internal/services/order_service/internal/orders/features/submitting_order/v1/commands/submit_order.go rename to internal/services/orderservice/internal/orders/features/submitting_order/v1/commands/submit_order.go diff --git a/internal/services/order_service/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go b/internal/services/orderservice/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go similarity index 91% rename from internal/services/order_service/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go rename to internal/services/orderservice/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go index 890e5baf..8ba117b2 100644 --- a/internal/services/order_service/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go +++ b/internal/services/orderservice/internal/orders/features/submitting_order/v1/events/domain_events/order_submitted.go @@ -3,7 +3,7 @@ package domainEvents import ( "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" uuid "github.com/satori/go.uuid" ) diff --git a/internal/services/order_service/internal/orders/features/updating_shopping_card/v1/commands/update_shopping_card.go b/internal/services/orderservice/internal/orders/features/updating_shopping_card/v1/commands/update_shopping_card.go similarity index 100% rename from internal/services/order_service/internal/orders/features/updating_shopping_card/v1/commands/update_shopping_card.go rename to internal/services/orderservice/internal/orders/features/updating_shopping_card/v1/commands/update_shopping_card.go diff --git a/internal/services/order_service/internal/orders/features/updating_shopping_card/v1/events/shopping_card_updated.go b/internal/services/orderservice/internal/orders/features/updating_shopping_card/v1/events/shopping_card_updated.go similarity index 100% rename from internal/services/order_service/internal/orders/features/updating_shopping_card/v1/events/shopping_card_updated.go rename to internal/services/orderservice/internal/orders/features/updating_shopping_card/v1/events/shopping_card_updated.go diff --git a/internal/services/order_service/internal/orders/models/orders/aggregate/order.go b/internal/services/orderservice/internal/orders/models/orders/aggregate/order.go similarity index 98% rename from internal/services/order_service/internal/orders/models/orders/aggregate/order.go rename to internal/services/orderservice/internal/orders/models/orders/aggregate/order.go index 19812c31..c228a46b 100644 --- a/internal/services/order_service/internal/orders/models/orders/aggregate/order.go +++ b/internal/services/orderservice/internal/orders/models/orders/aggregate/order.go @@ -8,9 +8,9 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/domain" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/errors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/type_mappper" + typeMapper "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/reflection/typemapper" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" domainExceptions "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/exceptions/domain_exceptions" createOrderDomainEventsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/events/domain_events" diff --git a/internal/services/order_service/internal/orders/models/orders/read_models/order_read.go b/internal/services/orderservice/internal/orders/models/orders/read_models/order_read.go similarity index 100% rename from internal/services/order_service/internal/orders/models/orders/read_models/order_read.go rename to internal/services/orderservice/internal/orders/models/orders/read_models/order_read.go diff --git a/internal/services/order_service/internal/orders/models/orders/read_models/shop_item_read.go b/internal/services/orderservice/internal/orders/models/orders/read_models/shop_item_read.go similarity index 100% rename from internal/services/order_service/internal/orders/models/orders/read_models/shop_item_read.go rename to internal/services/orderservice/internal/orders/models/orders/read_models/shop_item_read.go diff --git a/internal/services/order_service/internal/orders/models/orders/value_objects/shop_item.go b/internal/services/orderservice/internal/orders/models/orders/value_objects/shop_item.go similarity index 100% rename from internal/services/order_service/internal/orders/models/orders/value_objects/shop_item.go rename to internal/services/orderservice/internal/orders/models/orders/value_objects/shop_item.go diff --git a/internal/services/order_service/internal/orders/orders_fx.go b/internal/services/orderservice/internal/orders/orders_fx.go similarity index 97% rename from internal/services/order_service/internal/orders/orders_fx.go rename to internal/services/orderservice/internal/orders/orders_fx.go index ffe80cad..c1ea96aa 100644 --- a/internal/services/order_service/internal/orders/orders_fx.go +++ b/internal/services/orderservice/internal/orders/orders_fx.go @@ -1,10 +1,10 @@ package orders import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/web/route" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/web/route" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/data/repositories" createOrderV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/endpoints" getOrderByIdV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/getting_order_by_id/v1/endpoints" diff --git a/internal/services/order_service/internal/orders/projections/elastic_order_projection.go b/internal/services/orderservice/internal/orders/projections/elastic_order_projection.go similarity index 100% rename from internal/services/order_service/internal/orders/projections/elastic_order_projection.go rename to internal/services/orderservice/internal/orders/projections/elastic_order_projection.go diff --git a/internal/services/order_service/internal/orders/projections/mongo_order_projection.go b/internal/services/orderservice/internal/orders/projections/mongo_order_projection.go similarity index 94% rename from internal/services/order_service/internal/orders/projections/mongo_order_projection.go rename to internal/services/orderservice/internal/orders/projections/mongo_order_projection.go index e79ac59b..4679e8a1 100644 --- a/internal/services/order_service/internal/orders/projections/mongo_order_projection.go +++ b/internal/services/orderservice/internal/orders/projections/mongo_order_projection.go @@ -4,14 +4,15 @@ import ( "context" "fmt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/producer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts/projection" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/models" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/producer" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/contracts/repositories" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" createOrderDomainEventsV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/events/domain_events" @@ -82,7 +83,7 @@ func (m *mongoOrderProjection) onOrderCreated( ) _, err = m.mongoOrderRepository.CreateOrder(ctx, orderRead) if err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrFromSpan( span, errors.WrapIf( err, @@ -93,7 +94,7 @@ func (m *mongoOrderProjection) onOrderCreated( orderReadDto, err := mapper.Map[*dtosV1.OrderReadDto](orderRead) if err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, @@ -106,7 +107,7 @@ func (m *mongoOrderProjection) onOrderCreated( err = m.rabbitmqProducer.PublishMessage(ctx, orderCreatedEvent, nil) if err != nil { - return tracing.TraceErrFromSpan( + return utils.TraceErrFromSpan( span, customErrors.NewApplicationErrorWrap( err, diff --git a/internal/services/order_service/internal/shared/app/app.go b/internal/services/orderservice/internal/shared/app/app.go similarity index 100% rename from internal/services/order_service/internal/shared/app/app.go rename to internal/services/orderservice/internal/shared/app/app.go diff --git a/internal/services/order_service/internal/shared/app/application.go b/internal/services/orderservice/internal/shared/app/application.go similarity index 92% rename from internal/services/order_service/internal/shared/app/application.go rename to internal/services/orderservice/internal/shared/app/application.go index fdb4b135..fd57ba9c 100644 --- a/internal/services/order_service/internal/shared/app/application.go +++ b/internal/services/orderservice/internal/shared/app/application.go @@ -1,7 +1,7 @@ package app import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/shared/configurations/orders" @@ -18,7 +18,7 @@ func NewOrdersApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *OrdersApplication { app := fxapp.NewApplication(providers, decorates, options, logger, environment) return &OrdersApplication{ diff --git a/internal/services/order_service/internal/shared/app/application_builder.go b/internal/services/orderservice/internal/shared/app/application_builder.go similarity index 100% rename from internal/services/order_service/internal/shared/app/application_builder.go rename to internal/services/orderservice/internal/shared/app/application_builder.go diff --git a/internal/services/order_service/internal/shared/app/test/test_app.go b/internal/services/orderservice/internal/shared/app/test/test_app.go similarity index 99% rename from internal/services/order_service/internal/shared/app/test/test_app.go rename to internal/services/orderservice/internal/shared/app/test/test_app.go index 7849d0ed..1d1ae048 100644 --- a/internal/services/order_service/internal/shared/app/test/test_app.go +++ b/internal/services/orderservice/internal/shared/app/test/test_app.go @@ -10,7 +10,7 @@ import ( config4 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" - config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo/config" + config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/bus" diff --git a/internal/services/order_service/internal/shared/app/test/test_application.go b/internal/services/orderservice/internal/shared/app/test/test_application.go similarity index 94% rename from internal/services/order_service/internal/shared/app/test/test_application.go rename to internal/services/orderservice/internal/shared/app/test/test_application.go index 206f15a2..1f205ea2 100644 --- a/internal/services/order_service/internal/shared/app/test/test_application.go +++ b/internal/services/orderservice/internal/shared/app/test/test_application.go @@ -1,7 +1,7 @@ package test import ( - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environment" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/test" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/shared/app" @@ -22,7 +22,7 @@ func NewOrdersTestApplication( decorates []interface{}, options []fx.Option, logger logger.Logger, - environment environemnt.Environment, + environment environment.Environment, ) *OrdersTestApplication { testApp := test.NewTestApplication( tb, diff --git a/internal/services/order_service/internal/shared/app/test/test_application_builder.go b/internal/services/orderservice/internal/shared/app/test/test_application_builder.go similarity index 100% rename from internal/services/order_service/internal/shared/app/test/test_application_builder.go rename to internal/services/orderservice/internal/shared/app/test/test_application_builder.go diff --git a/internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go b/internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go new file mode 100644 index 00000000..c3dbb895 --- /dev/null +++ b/internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_configurator.go @@ -0,0 +1,44 @@ +package infrastructure + +import ( + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + metricspipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics/mediatr/pipelines" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" + tracingpipelines "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/mediatr/pipelines" + + "github.com/mehdihadeli/go-mediatr" +) + +type InfrastructureConfigurator struct { + contracts.Application +} + +func NewInfrastructureConfigurator( + app contracts.Application, +) *InfrastructureConfigurator { + return &InfrastructureConfigurator{ + Application: app, + } +} + +func (ic *InfrastructureConfigurator) ConfigInfrastructures() { + ic.ResolveFunc( + func(l logger.Logger, tracer tracing.AppTracer, metrics metrics.AppMetrics) error { + err := mediatr.RegisterRequestPipelineBehaviors( + loggingpipelines.NewMediatorLoggingPipeline(l), + tracingpipelines.NewMediatorTracingPipeline( + tracer, + tracingpipelines.WithLogger(l), + ), + metricspipelines.NewMediatorMetricsPipeline( + metrics, + metricspipelines.WithLogger(l), + ), + ) + + return err + }, + ) +} diff --git a/internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go b/internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go similarity index 91% rename from internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go rename to internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go index db042abb..d3a510a4 100644 --- a/internal/services/order_service/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go +++ b/internal/services/orderservice/internal/shared/configurations/orders/infrastructure/infrastructure_fx.go @@ -6,9 +6,10 @@ import ( "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/grpc" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/health" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/metrics" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/configurations" rabbitmq2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/configurations/rabbitmq" @@ -35,7 +36,6 @@ var Module = fx.Module( } }, ), - otel.Module, rabbitmq.ModuleFunc( func() configurations.RabbitMQConfigurationBuilderFuc { return func(builder configurations.RabbitMQConfigurationBuilder) { @@ -44,6 +44,8 @@ var Module = fx.Module( }, ), health.Module, + tracing.Module, + metrics.Module, // Other provides fx.Provide(validator.New), diff --git a/internal/services/order_service/internal/shared/configurations/orders/orders_configurator.go b/internal/services/orderservice/internal/shared/configurations/orders/orders_configurator.go similarity index 95% rename from internal/services/order_service/internal/shared/configurations/orders/orders_configurator.go rename to internal/services/orderservice/internal/shared/configurations/orders/orders_configurator.go index 3e47f567..2f435d1a 100644 --- a/internal/services/order_service/internal/shared/configurations/orders/orders_configurator.go +++ b/internal/services/orderservice/internal/shared/configurations/orders/orders_configurator.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/configurations" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/shared/configurations/orders/infrastructure" @@ -51,7 +51,7 @@ func (ic *OrdersServiceConfigurator) MapOrdersEndpoints() { func(ordersServer customEcho.EchoHttpServer, cfg *config.Config) error { ordersServer.SetupDefaultMiddlewares() - // Config orders root endpoint + // config orders root endpoint ordersServer.RouteBuilder(). RegisterRoutes(func(e *echo.Echo) { e.GET("", func(ec echo.Context) error { @@ -65,7 +65,7 @@ func (ic *OrdersServiceConfigurator) MapOrdersEndpoints() { }) }) - // Config orders swagger + // config orders swagger ic.configSwagger(ordersServer.RouteBuilder()) return nil diff --git a/internal/services/order_service/internal/shared/configurations/orders/orders_configurator_swagger.go b/internal/services/orderservice/internal/shared/configurations/orders/orders_configurator_swagger.go similarity index 93% rename from internal/services/order_service/internal/shared/configurations/orders/orders_configurator_swagger.go rename to internal/services/orderservice/internal/shared/configurations/orders/orders_configurator_swagger.go index ed1313ce..7f861a83 100644 --- a/internal/services/order_service/internal/shared/configurations/orders/orders_configurator_swagger.go +++ b/internal/services/orderservice/internal/shared/configurations/orders/orders_configurator_swagger.go @@ -1,7 +1,7 @@ package orders import ( - customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/custom_echo" + customEcho "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/customecho/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/docs" "github.com/labstack/echo/v4" diff --git a/internal/services/order_service/internal/shared/configurations/orders/orders_fx.go b/internal/services/orderservice/internal/shared/configurations/orders/orders_fx.go similarity index 100% rename from internal/services/order_service/internal/shared/configurations/orders/orders_fx.go rename to internal/services/orderservice/internal/shared/configurations/orders/orders_fx.go diff --git a/internal/services/order_service/internal/shared/contracts/orders_metrics.go b/internal/services/orderservice/internal/shared/contracts/orders_metrics.go similarity index 100% rename from internal/services/order_service/internal/shared/contracts/orders_metrics.go rename to internal/services/orderservice/internal/shared/contracts/orders_metrics.go diff --git a/internal/services/order_service/internal/shared/grpc/genproto/orders.pb.go b/internal/services/orderservice/internal/shared/grpc/genproto/orders.pb.go similarity index 99% rename from internal/services/order_service/internal/shared/grpc/genproto/orders.pb.go rename to internal/services/orderservice/internal/shared/grpc/genproto/orders.pb.go index abccd5c3..9800c343 100644 --- a/internal/services/order_service/internal/shared/grpc/genproto/orders.pb.go +++ b/internal/services/orderservice/internal/shared/grpc/genproto/orders.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.26.0 // protoc v4.23.4 -// source: order_service/orders.proto +// source: orderservice/orders.proto package orders_service diff --git a/internal/services/order_service/internal/shared/grpc/genproto/orders_grpc.pb.go b/internal/services/orderservice/internal/shared/grpc/genproto/orders_grpc.pb.go similarity index 99% rename from internal/services/order_service/internal/shared/grpc/genproto/orders_grpc.pb.go rename to internal/services/orderservice/internal/shared/grpc/genproto/orders_grpc.pb.go index 6f5be25d..ba46c936 100644 --- a/internal/services/order_service/internal/shared/grpc/genproto/orders_grpc.pb.go +++ b/internal/services/orderservice/internal/shared/grpc/genproto/orders_grpc.pb.go @@ -2,7 +2,7 @@ // versions: // - protoc-gen-go-grpc v1.3.0 // - protoc v4.23.4 -// source: order_service/orders.proto +// source: orderservice/orders.proto package orders_service @@ -251,5 +251,5 @@ var OrdersService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "order_service/orders.proto", + Metadata: "orderservice/orders.proto", } diff --git a/internal/services/order_service/internal/shared/grpc/order_grpc_service_server.go b/internal/services/orderservice/internal/shared/grpc/order_grpc_service_server.go similarity index 97% rename from internal/services/order_service/internal/shared/grpc/order_grpc_service_server.go rename to internal/services/orderservice/internal/shared/grpc/order_grpc_service_server.go index b779042d..3919e517 100644 --- a/internal/services/order_service/internal/shared/grpc/order_grpc_service_server.go +++ b/internal/services/orderservice/internal/shared/grpc/order_grpc_service_server.go @@ -4,11 +4,11 @@ import ( "context" "fmt" - customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/http_errors/custom_errors" + customErrors "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/http/httperrors/customerrors" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mapper" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing" attribute2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/attribute" + utils2 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/otel/tracing/utils" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" createOrderCommandV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/commands" @@ -166,7 +166,7 @@ func (o OrderGrpcServiceServer) GetOrderByID( err, "[OrderGrpcServiceServer_GetOrderByID.Map] error in mapping order", ) - return nil, tracing.TraceErrFromContext(ctx, err) + return nil, utils2.TraceStatusFromContext(ctx, err) } return &grpcOrderService.GetOrderByIDRes{Order: order}, nil diff --git a/internal/services/order_service/internal/shared/test_fixtures/integration/integration_test_fixture.go b/internal/services/orderservice/internal/shared/test_fixtures/integration/integration_test_fixture.go similarity index 97% rename from internal/services/order_service/internal/shared/test_fixtures/integration/integration_test_fixture.go rename to internal/services/orderservice/internal/shared/test_fixtures/integration/integration_test_fixture.go index 91032d12..c2d0f976 100644 --- a/internal/services/order_service/internal/shared/test_fixtures/integration/integration_test_fixture.go +++ b/internal/services/orderservice/internal/shared/test_fixtures/integration/integration_test_fixture.go @@ -4,11 +4,11 @@ import ( "context" "testing" + "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/messaging/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/es/contracts/store" config3 "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/eventstroredb/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger" - "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/messaging/bus" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/mongodb" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/rabbitmq/config" "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/utils" @@ -85,8 +85,8 @@ func NewIntegrationTestSharedFixture( return shared } -func (i *IntegrationTestSharedFixture) InitializeTest() { - i.Log.Info("InitializeTest started") +func (i *IntegrationTestSharedFixture) SetupTest() { + i.Log.Info("SetupTest started") // seed data in each test res, err := seedReadModelData(i.mongoClient, i.MongoDbOptions.Database) @@ -96,8 +96,8 @@ func (i *IntegrationTestSharedFixture) InitializeTest() { i.Items = res } -func (i *IntegrationTestSharedFixture) DisposeTest() { - i.Log.Info("DisposeTest started") +func (i *IntegrationTestSharedFixture) TearDownTest() { + i.Log.Info("TearDownTest started") // cleanup test containers with their hooks if err := i.cleanupRabbitmqData(); err != nil { diff --git a/internal/services/order_service/mocks/InvalidDeliveryAddressError.go b/internal/services/orderservice/mocks/InvalidDeliveryAddressError.go similarity index 100% rename from internal/services/order_service/mocks/InvalidDeliveryAddressError.go rename to internal/services/orderservice/mocks/InvalidDeliveryAddressError.go diff --git a/internal/services/order_service/mocks/InvalidEmailAddressError.go b/internal/services/orderservice/mocks/InvalidEmailAddressError.go similarity index 100% rename from internal/services/order_service/mocks/InvalidEmailAddressError.go rename to internal/services/orderservice/mocks/InvalidEmailAddressError.go diff --git a/internal/services/order_service/mocks/OrderElasticRepository.go b/internal/services/orderservice/mocks/OrderElasticRepository.go similarity index 100% rename from internal/services/order_service/mocks/OrderElasticRepository.go rename to internal/services/orderservice/mocks/OrderElasticRepository.go diff --git a/internal/services/order_service/mocks/OrderMongoRepository.go b/internal/services/orderservice/mocks/OrderMongoRepository.go similarity index 100% rename from internal/services/order_service/mocks/OrderMongoRepository.go rename to internal/services/orderservice/mocks/OrderMongoRepository.go diff --git a/internal/services/order_service/mocks/OrderNotFoundError.go b/internal/services/orderservice/mocks/OrderNotFoundError.go similarity index 100% rename from internal/services/order_service/mocks/OrderNotFoundError.go rename to internal/services/orderservice/mocks/OrderNotFoundError.go diff --git a/internal/services/order_service/mocks/OrderShopItemsRequiredError.go b/internal/services/orderservice/mocks/OrderShopItemsRequiredError.go similarity index 100% rename from internal/services/order_service/mocks/OrderShopItemsRequiredError.go rename to internal/services/orderservice/mocks/OrderShopItemsRequiredError.go diff --git a/internal/services/order_service/mocks/OrdersServiceClient.go b/internal/services/orderservice/mocks/OrdersServiceClient.go similarity index 100% rename from internal/services/order_service/mocks/OrdersServiceClient.go rename to internal/services/orderservice/mocks/OrdersServiceClient.go diff --git a/internal/services/order_service/mocks/OrdersServiceServer.go b/internal/services/orderservice/mocks/OrdersServiceServer.go similarity index 100% rename from internal/services/order_service/mocks/OrdersServiceServer.go rename to internal/services/orderservice/mocks/OrdersServiceServer.go diff --git a/internal/services/order_service/mocks/UnsafeOrdersServiceServer.go b/internal/services/orderservice/mocks/UnsafeOrdersServiceServer.go similarity index 100% rename from internal/services/order_service/mocks/UnsafeOrdersServiceServer.go rename to internal/services/orderservice/mocks/UnsafeOrdersServiceServer.go diff --git a/internal/services/order_service/mocks/orderReadRepository.go b/internal/services/orderservice/mocks/orderReadRepository.go similarity index 100% rename from internal/services/order_service/mocks/orderReadRepository.go rename to internal/services/orderservice/mocks/orderReadRepository.go diff --git a/internal/services/order_service/readme.md b/internal/services/orderservice/readme.md similarity index 100% rename from internal/services/order_service/readme.md rename to internal/services/orderservice/readme.md diff --git a/internal/services/order_service/revive-config.toml b/internal/services/orderservice/revive-config.toml similarity index 100% rename from internal/services/order_service/revive-config.toml rename to internal/services/orderservice/revive-config.toml diff --git a/internal/services/order_service/taskfile.yml b/internal/services/orderservice/taskfile.yml similarity index 100% rename from internal/services/order_service/taskfile.yml rename to internal/services/orderservice/taskfile.yml diff --git a/internal/services/order_service/taskfile_db.yml b/internal/services/orderservice/taskfile_db.yml similarity index 100% rename from internal/services/order_service/taskfile_db.yml rename to internal/services/orderservice/taskfile_db.yml diff --git a/internal/services/order_service/taskfile_test.yml b/internal/services/orderservice/taskfile_test.yml similarity index 100% rename from internal/services/order_service/taskfile_test.yml rename to internal/services/orderservice/taskfile_test.yml diff --git a/internal/services/order_service/test/end_to_end/orders/features/creating_order/v1/create_order_test.go b/internal/services/orderservice/test/end_to_end/orders/features/creating_order/v1/create_order_test.go similarity index 95% rename from internal/services/order_service/test/end_to_end/orders/features/creating_order/v1/create_order_test.go rename to internal/services/orderservice/test/end_to_end/orders/features/creating_order/v1/create_order_test.go index 378e93c8..f1a35a82 100644 --- a/internal/services/order_service/test/end_to_end/orders/features/creating_order/v1/create_order_test.go +++ b/internal/services/orderservice/test/end_to_end/orders/features/creating_order/v1/create_order_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - customTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/custom_types" + customTypes "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/core/customtypes" dtosV1 "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/dtos/v1" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/orders/features/creating_order/v1/dtos" "github.com/mehdihadeli/go-ecommerce-microservices/internal/services/orderservice/internal/shared/test_fixtures/integration" @@ -39,12 +39,12 @@ var _ = Describe("CreateOrder Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" for testing the creation of an order with valid input diff --git a/internal/services/order_service/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go b/internal/services/orderservice/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go similarity index 94% rename from internal/services/order_service/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go rename to internal/services/orderservice/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go index 89242231..19e81a9f 100644 --- a/internal/services/order_service/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go +++ b/internal/services/orderservice/test/end_to_end/orders/features/getting_order_by_id/v1/get_order_by_id_test.go @@ -34,14 +34,14 @@ var _ = Describe("GetOrderById Feature", func() { ctx = context.Background() By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() id = integrationFixture.Items[0].Id }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) // "Scenario" for testing the retrieval of an order by a valid ID diff --git a/internal/services/order_service/test/end_to_end/orders/grpc/order_grpc_service_server_test.go b/internal/services/orderservice/test/end_to_end/orders/grpc/order_grpc_service_server_test.go similarity index 98% rename from internal/services/order_service/test/end_to_end/orders/grpc/order_grpc_service_server_test.go rename to internal/services/orderservice/test/end_to_end/orders/grpc/order_grpc_service_server_test.go index 3ba8e917..6b0c752a 100644 --- a/internal/services/order_service/test/end_to_end/orders/grpc/order_grpc_service_server_test.go +++ b/internal/services/orderservice/test/end_to_end/orders/grpc/order_grpc_service_server_test.go @@ -62,7 +62,7 @@ package grpc // Quantity: uint64(gofakeit.Number(1, 10)), // Description: gofakeit.AdjectiveDescriptive(), // Price: gofakeit.Price(100, 10000), -// Title: gofakeit.Name(), +// Title: gofakeit.ShortTypeName(), // }, // }, // } diff --git a/internal/services/order_service/test/integration/orders/features/creating_order/v1/create_order_test.go b/internal/services/orderservice/test/integration/orders/features/creating_order/v1/create_order_test.go similarity index 98% rename from internal/services/order_service/test/integration/orders/features/creating_order/v1/create_order_test.go rename to internal/services/orderservice/test/integration/orders/features/creating_order/v1/create_order_test.go index 4238121a..78aefa5e 100644 --- a/internal/services/order_service/test/integration/orders/features/creating_order/v1/create_order_test.go +++ b/internal/services/orderservice/test/integration/orders/features/creating_order/v1/create_order_test.go @@ -46,14 +46,14 @@ var _ = Describe("Create Order Feature", func() { _ = BeforeEach(func() { By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() // id = integrationFixture.Items[0].OrderId }) _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) _ = BeforeSuite(func() { diff --git a/internal/services/order_service/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go b/internal/services/orderservice/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go similarity index 97% rename from internal/services/order_service/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go rename to internal/services/orderservice/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go index 90a9c30b..c305c7bc 100644 --- a/internal/services/order_service/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go +++ b/internal/services/orderservice/test/integration/orders/features/getting_order_by_id/v1/get_order_by_id_test.go @@ -38,7 +38,7 @@ var _ = Describe("Get Order By Id Feature", func() { _ = BeforeEach(func() { By("Seeding the required data") - integrationFixture.InitializeTest() + integrationFixture.SetupTest() idString := integrationFixture.Items[0].Id id, err = uuid.FromString(idString) @@ -47,7 +47,7 @@ var _ = Describe("Get Order By Id Feature", func() { _ = AfterEach(func() { By("Cleanup test data") - integrationFixture.DisposeTest() + integrationFixture.TearDownTest() }) _ = BeforeSuite(func() { diff --git a/scripts/format.sh b/scripts/format.sh index 49bd3dce..6e1ae880 100644 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -26,9 +26,10 @@ golines -m 120 -w --ignore-generated . # https://github.com/incu6us/goimports-reviser # https://github.com/incu6us/goimports-reviser/issues/118 # https://github.com/incu6us/goimports-reviser/issues/88 +# https://github.com/incu6us/goimports-reviser/issues/104 # will do `gofmt` internally if we use -format # -rm-unused, -set-alias have some errors ---> goimports-reviser -rm-unused -set-alias -format -recursive ./... -# goimports-reviser -company-prefixes "github.com/mehdihadeli" -project-name "github.com/mehdihadeli/go-ecommerce-microservices" -imports-order "std,general,company,project" -recursive ./... +# goimports-reviser -company-prefixes "github.com/mehdihadeli" -project-name "github.com/mehdihadeli/go-ecommerce-microservices" -rm-unused -set-alias -imports-order "std,general,company,project,blanked,dotted" -recursive ./... gci write --skip-generated -s standard -s "prefix(github.com/mehdihadeli/go-ecommerce-microservices)" -s default -s blank -s dot --custom-order . diff --git a/scripts/install-tools.sh b/scripts/install-tools.sh index e6f4c72d..4b60b113 100644 --- a/scripts/install-tools.sh +++ b/scripts/install-tools.sh @@ -7,6 +7,8 @@ set -e # `go install package@version` command works directly when we specified exact version, elsewhere it needs a `go.mod` and specifying corresponding version for each package +go install github.com/samlitowitz/goimportcycle/cmd/goimportcycle@latest + # https://github.com/incu6us/goimports-reviser go install -v github.com/incu6us/goimports-reviser/v3@latest diff --git a/scripts/service-reset.ps1 b/scripts/service-reset.ps1 new file mode 100644 index 00000000..954d36d0 --- /dev/null +++ b/scripts/service-reset.ps1 @@ -0,0 +1,2 @@ +net stop winnat +net start winnat diff --git a/scripts/update-dependencies.sh b/scripts/update-dependencies.sh new file mode 100644 index 00000000..924ceae7 --- /dev/null +++ b/scripts/update-dependencies.sh @@ -0,0 +1,13 @@ +# In a bash script, set -e is a command that enables the "exit immediately" option. When this option is set, the script will terminate immediately if any command within the script exits with a non-zero status (indicating an error). +set -e + +readonly service="$1" + +echo "start upgrading packages in $service" + +if [ "$service" = "pkg" ]; then + cd "./internal/pkg" && go get -u -t -d -v ./... && go mod tidy +# Check if input is not empty or null +elif [ -n "$service" ]; then + cd "./internal/services/$service" && go get -u -t -d -v ./... && go mod tidy +fi diff --git a/taskfile.yml b/taskfile.yml index fd4323d2..45146538 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -11,33 +11,33 @@ tasks: run-catalogs-write-service: desc: Run catalog write service cmds: - - sh ./scripts/run.sh catalog_write_service + - sh ./scripts/run.sh catalogwriteservice run-catalog-read-service: desc: Run catalog read service cmds: - - sh ./scripts/run.sh catalog_read_service + - sh ./scripts/run.sh catalogreadservice run-order-service: desc: Run order service cmds: - - sh ./scripts/run.sh order_service + - sh ./scripts/run.sh orderservice build: desc: Build project components cmds: - sh ./scripts/build.sh pkg - - sh ./scripts/build.sh catalog_write_service - - sh ./scripts/build.sh catalog_read_service - - sh ./scripts/build.sh order_service + - sh ./scripts/build.sh catalogwriteservice + - sh ./scripts/build.sh catalogreadservice + - sh ./scripts/build.sh orderservice install-dependencies: desc: Install project dependencies cmds: - sh ./scripts/install-dependencies.sh pkg - - sh ./scripts/install-dependencies.sh catalog_write_service - - sh ./scripts/install-dependencies.sh catalog_read_service - - sh ./scripts/install-dependencies.sh order_service + - sh ./scripts/install-dependencies.sh catalogwriteservice + - sh ./scripts/install-dependencies.sh catalogreadservice + - sh ./scripts/install-dependencies.sh orderservice docker-compose-infra-up: desc: Start infrastructure using docker-compose @@ -52,51 +52,51 @@ tasks: openapi: desc: Generate OpenAPI documentation cmds: - - sh ./scripts/openapi.sh catalog_write_service - - sh ./scripts/openapi.sh catalog_read_service - - sh ./scripts/openapi.sh order_service + - sh ./scripts/openapi.sh catalogwriteservice + - sh ./scripts/openapi.sh catalogreadservice + - sh ./scripts/openapi.sh orderservice proto: desc: Generate protobuf files cmds: - - sh ./scripts/proto.sh catalog_write_service - - sh ./scripts/proto.sh order_service + - sh ./scripts/proto.sh catalogwriteservice + - sh ./scripts/proto.sh orderservice unit-test: desc: Run unit tests cmds: - - sh ./scripts/test.sh catalog_write_service unit - - sh ./scripts/test.sh catalog_read_service unit - - sh ./scripts/test.sh order_service unit + - sh ./scripts/test.sh catalogwriteservice unit + - sh ./scripts/test.sh catalogreadservice unit + - sh ./scripts/test.sh orderservice unit integration-test: desc: Run integration tests cmds: - - sh ./scripts/test.sh catalog_write_service integration - - sh ./scripts/test.sh catalog_read_service integration - - sh ./scripts/test.sh order_service integration + - sh ./scripts/test.sh catalogwriteservice integration + - sh ./scripts/test.sh catalogreadservice integration + - sh ./scripts/test.sh orderservice integration e2e-test: desc: Run end-to-end tests cmds: - - sh ./scripts/test.sh catalog_write_service e2e - - sh ./scripts/test.sh catalog_read_service e2e - - sh ./scripts/test.sh order_service e2e + - sh ./scripts/test.sh catalogwriteservice e2e + - sh ./scripts/test.sh catalogreadservice e2e + - sh ./scripts/test.sh orderservice e2e format: desc: Format codebase cmds: - - sh ./scripts/format.sh catalog_write_service - - sh ./scripts/format.sh catalog_read_service - - sh ./scripts/format.sh order_service + - sh ./scripts/format.sh catalogwriteservice + - sh ./scripts/format.sh catalogreadservice + - sh ./scripts/format.sh orderservice - sh ./scripts/format.sh pkg lint: desc: Run linters cmds: - - sh ./scripts/lint.sh catalog_write_service - - sh ./scripts/lint.sh catalog_read_service - - sh ./scripts/lint.sh order_service + - sh ./scripts/lint.sh catalogwriteservice + - sh ./scripts/lint.sh catalogreadservice + - sh ./scripts/lint.sh orderservice - sh ./scripts/lint.sh pkg pkg-mocks: @@ -109,6 +109,6 @@ tasks: services-mocks: desc: Generate service mocks cmds: - - cd internal/services/catalog_write_service && mockery --output mocks --all - - cd internal/services/catalog_read_service && mockery --output mocks --all - - cd internal/services/order_service && mockery --output mocks --all + - cd internal/services/catalogwriteservice && mockery --output mocks --all + - cd internal/services/catalogreadservice && mockery --output mocks --all + - cd internal/services/orderservice && mockery --output mocks --all