Repository navigation
Implement cart, order, and AI services with product integration - #1
Merged
Merged
Conversation
- Untrack root-level `api-gateway`, which was a 27MB compiled ELF binary accidentally committed rather than the "legacy artefact" source the README implied. Gitignore it going forward. - gofmt the tree so the (separately staged) CI gate doesn't fail on pre-existing formatting drift (product-service, tools/create_service.go). - Fix a real go vet failure in shared/util.GetRandomAvatar: %d format verb was used with a uuid.UUID string argument. - Update README's Roadmap & Known Gaps to reflect actual per-service maturity. - Corrected the auth-service assessment in the AskShop revamp plan: it's an empty stub like order/ai-service, not a duplicate in-memory auth implementation as the file listing initially suggested.
Closes the biggest gap identified in the revamp plan: product, cart,
and order services now form a real, working checkout path instead of
scaffolding with hardcoded placeholders.
- product-service: implement the Postgres repository and REST handler
(both were stubs), add price/stock to the domain model and gRPC
contract (shared/proto/product.proto), extend the service/repository
interfaces with Create/Update/Delete, and wire it all into main.go
with a graceful in-memory fallback when Postgres is unreachable.
Seed data now carries real prices and stock levels.
- cart-service: was previously unwired (empty main.go) despite solid
domain/service logic. Add a Postgres CartRepository, rewrite the
REST handler to match the actual CartService interface (the old one
referenced a CartInterface nothing implemented), and replace the
hardcoded $29.99 placeholder in AddItem with a live gRPC lookup
against product-service for name/SKU/price/stock. Implement the
ValidateCartForCheckout TODO (price-change warnings, stock checks).
- order-service: build it from scratch (previously `func main() {}`).
Validates the cart via cart-service, snapshots it into an Order,
marks the cart converted, and publishes order.event.placed.
- shared/events: thin RabbitMQ publisher/consumer over a topic
exchange, keyed by the routing keys already declared in
shared/contracts/amqp.go. Degrades to a logged no-op if
RABBITMQ_URL is unset/unreachable rather than failing startup.
order-service runs a demonstration consumer proving the round trip.
- api-gateway: proxy routes for /products, /cart, /orders, forwarding
caller identity via X-User-ID until JWT verification is wired
through the gateway.
Verified locally end-to-end against real Postgres and RabbitMQ
instances: register/browse/cart/checkout all the way through order
creation, cart conversion, and event publish+consume, both service-
to-service and routed entirely through the gateway.
Builds ai-service into a real service (previously an empty stub) using the official Anthropic Go SDK, grounded in the live catalog rather than letting the model invent products. - ai-service: conversational shopping assistant (POST /api/v1/ai/chat) using a manual tool-use loop where Claude calls a search_products tool backed by product-service before answering; product explanations (POST /api/v1/ai/products/:id/explain), which also publish ai.cmd.explain_product; and a cart-abandonment nudge batch endpoint (POST /api/v1/ai/cart-nudges) that generates re-engagement messages from cart-service's abandoned carts. Degrades cleanly to 503 on every AI route when ANTHROPIC_API_KEY is unset, rather than failing to start or crashing on first request. Model configurable via AI_MODEL (default claude-opus-5). - product-service: wire up real keyword search (?q=) using the existing but previously unused domain.ScopeSearch, both in the Postgres repository and the in-memory fallback. This is what the chat assistant's search_products tool calls. - cart-service: add an internal GET /api/v1/cart/admin/abandoned endpoint (not proxied through the gateway) so ai-service can read abandoned carts for the nudge feature. - api-gateway: proxy /api/v1/ai/chat and /api/v1/ai/products/:id/explain. Verified locally: product search returns real seeded data; all three AI endpoints correctly return 503 with a clear message when no Anthropic API key is configured (no key was available in this environment to exercise the live model call, so that path is unverified beyond compiling against the documented SDK patterns).
Replaces the Anthropic Go SDK with the official Google Gen AI Go SDK (google.golang.org/genai). Same shape as before, different provider: - internal/llm/client.go: wraps *genai.Client instead of the Anthropic client, reading GEMINI_API_KEY (falling back to GOOGLE_API_KEY) via the Gemini API backend. Default model is now gemini-3.6-flash (override with AI_MODEL, unchanged env var). - internal/service/chat_service.go: the shopping assistant's tool-use loop is rewritten against Gemini's function-calling API (FunctionDeclaration/Schema, resp.FunctionCalls(), resp.Text(), genai.NewContentFromFunctionResponse) in place of Anthropic's ToolParam/ToolUseBlock. Same behavior: search_products grounds every answer in the real catalog, product explanations and cart nudges are single-shot generations. - Same graceful-degradation contract as before: every AI route returns 503 with a clear message when no key is configured, instead of failing to start. - README and startup log messages updated to reference GEMINI_API_KEY/GOOGLE_API_KEY instead of ANTHROPIC_API_KEY. Verified: builds and vets clean; ai-service starts and correctly returns 503 without a key (no Gemini key was available in this environment to exercise a live model call).
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
- Unit tests for the pure domain logic that had none: cart-service's CartItemBuilder/CartManager (item validation, totals, cart validation, merge/abandonment detection) and Cart/CartItem methods; product-service's slugify and BeforeCreate hooks, plus the clampPagination pagination helper; and shared/contracts.JoinPaths and shared/util.GetRandomAvatar. go test ./... no longer reports "no test files" across the board. - shared/health: a small Register(router, name) helper wired into every service (api-gateway, user, product, cart, order, ai) so each exposes a consistent GET /healthz. It's a liveness check only (no DB/broker ping yet). - docker-compose.yml: a lighter local-dev path than Tilt/Kubernetes — Postgres + RabbitMQ + every Go service via `go run` against the mounted source tree, no image build step. Validated with `docker compose config` (no daemon was available in this environment to run it end-to-end). - ARCHITECTURE.md: service map, checkout request flow, RabbitMQ event flow, and a documented list of known architectural gaps (no gateway JWT verification yet, the cart unique-user-id-index vs. converted-cart interaction, no distributed tracing). - README: rewrote the now-stale "Architecture at a Glance" section (still described product/cart/order/ai as scaffolding), added the docker-compose instructions and a Testing section, and documented a real, verified bug found while researching this phase: the container ports in the Tilt/K8s dev manifests disagree with each service's actual HTTP_ADDR default and with each other. Left unfixed since it needs a real cluster to verify against, which wasn't available here — flagged instead of guessed at. Verified: go build/vet/test all clean; docker compose config parses without error; manually curled /healthz on a running service.
Aligns every k8s deployment's containerPort/Service port, and every Tiltfile port_forward, with each service's actual HTTP_ADDR default in code: - product-service: containerPort/Service port 8083 -> 8082 (matches its real default), and adds a grpc containerPort/Service port 9090 that was previously missing entirely — without it, cart-service's in-cluster gRPC pricing lookups against product-service would have had nothing to connect to. - cart-service: 8086 -> 8083. - ai-service: 8087 -> 8086. - order-service and user-service's k8s manifests were already correct (8085/8084); only their Tilt port_forwards were wrong (mismatched with each other, not just with the code) and are now fixed. - api-gateway-deployment.yaml/app-config.yaml: the configmap key was GATEWAY_HTTP_ADDR, but services/api-gateway/main.go reads HTTP_ADDR — renamed so the configmap actually controls the gateway's port instead of silently no-opping. - product-seeder-job.yaml: bumped golang:1.21 -> golang:1.24-bookworm to match go.mod's `go 1.24.2` requirement (the job would have failed to build the seeder otherwise). Verified: every edited YAML file parses (python yaml.safe_load_all), Tiltfile brackets balance, go build/vet/test still clean. Not verified against a live cluster — none was available in this environment; this is a static consistency fix aligning declared config with code, not something exercised end-to-end. README and ARCHITECTURE.md updated accordingly.
Live end-to-end QA against real Postgres, RabbitMQ, and a working Gemini key surfaced a critical authorization bug plus a systemic status-code bug, both confirmed and fixed: P0 — cart item IDOR: RemoveItem/UpdateItemQuantity/SaveForLater took a userID parameter but never used it — cart-service operated on bare item IDs with no check that the item belonged to the caller's cart. Live-verified: User B could delete or overwrite User A's cart item quantity just by knowing the item's UUID, and the response even echoed A's full cart (including identity) back to B. Fixed with assertItemOwnedByUser, called before every item mutation; returns the same ErrItemNotFound in both the "not yours" and "doesn't exist" cases so the fix doesn't add an existence oracle. P1 — anonymous cart writes collided on one shared identity: omitting X-User-ID let cart-service create/reuse a cart with userId "", so every caller who forgot the header shared the same cart. cart-service write/checkout routes now require it (400 if absent), matching what order-service already enforced. P1 — api-gateway collapsed every downstream error to 500: cart-service correctly returns 400/409/422 for bad input, insufficient stock, and failed checkout validation, but every proxy handler I added in Phase 1/2 (cart, orders, ai) hardcoded 500 in its err != nil branch. rest.ServiceClient now returns a typed *ServiceError carrying the real status code, exposed via rest.StatusAndMessage(err), and every proxy handler forwards it. P2 — GET /api/v1/products/:id returned 500 for a malformed UUID instead of 400 (Update/Delete already validated; Get didn't). Fixed to match. P2 — ai-service's own error mapping only special-cased LLM unavailability; everything else was 500 regardless of cause. Extended its product client with the same typed-status-error pattern so 404/400 from product-service now propagate correctly. Verified via a full live QA pass (services/api-gateway/rest/client.go's new StatusAndMessage helper, cart-service's ownership check, etc.) — register/login, browse/search, cart, checkout, order placement, and the AI chat/explain endpoints against a real Gemini key all re-verified working after the fixes, with no regressions to the legitimate-owner paths. go build/vet/test clean.
Dubjay18
force-pushed
the
feat/revamp-askshop
branch
from
September 4, 2026 10:49
01e6238 to
5cab0f8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR completes the implementation of the cart, order, and AI services, adds comprehensive HTTP handlers and domain logic, integrates inter-service communication via gRPC and HTTP clients, and introduces event publishing over RabbitMQ. It also adds a docker-compose development environment and updates the API gateway to proxy all service endpoints.
Key Changes
Cart Service
CartManagerandCartItemBuilderwith full validation logic (quantity limits, price validation, discount application)PGCartRepositoryfor Postgres-backed cart persistence with user/session ID lookupsCartServiceImplwith product availability checks via gRPC clientCartHandler) with routes for CRUD operations and item managementOrder Service
OrderServiceImplcoordinating with cart-service over HTTP to validate and snapshot cartsPGOrderRepositoryfor order persistenceOrderHandler) with order placement, listing, and retrievalcartclient) for inter-service communication with cart-serviceAI Service
AIServicewith conversational shopping assistant using Google Gemini APIAIHandler) with/chat,/products/:id/explain, and/cart-nudgesendpointsGEMINI_API_KEYis unsetProduct Service
PGProductRepositorywith full CRUD, pagination, and keyword search (name/slug/SKU)InMemoryProductRepositoryas fallback when Postgres is unavailableProductHandler) with GET/POST/PUT/DELETE endpoints and paginationInfrastructure & Integration
docker-compose.ymlfor lightweight local development (Postgres, RabbitMQ, all services viago run)shared/eventswith graceful degradationshared/health) for container orchestrationTesting & CI
Documentation & Configuration
ARCHITECTURE.mdwith service map and communication patternsREADME.mdwith architecture overview and links to detailed docsNotable Implementation Details