Add customer order API sample for db persist feature - #118
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 51 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a new customer-order API sample that uses Ballerina persist with a PostgreSQL datastore. Included are project manifests (Ballerina.toml, Dependencies.toml), configuration and connection files, persist model and generated client, SQL init and seed scripts, a Docker Compose for Postgres, an HTTP service exposing customer/product/order endpoints, and a README describing setup and example requests. Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as Ballerina Service
participant DB as PostgreSQL Database
Client->>Service: POST /api/v1/orders (NewOrder)
Service->>Service: Validate payload (items present)
loop for each item
Service->>DB: SELECT product by id
DB-->>Service: product (price, existence)
Service->>Service: compute line total
end
Service->>DB: INSERT order (customerId, status, total, createdAt)
DB-->>Service: orderId
loop for each item
Service->>DB: INSERT order_item (orderId, productId, quantity, unitPrice)
end
DB-->>Service: order items inserted
Service-->>Client: 201 Created (order with items)
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (6)
integrator-default-profile/samples/customer-order-api/persist/db/model.bal (1)
16-18: Field naming:orderitemsvs.OrderItem.Reverse-navigation fields use lowercase
orderitems(also onProductat line 62). ConsiderorderItemsfor consistency with Ballerina camelCase conventions and the rest of the record fields (customerId,unitPrice,createdAt).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/persist/db/model.bal` around lines 16 - 18, Change the reverse-navigation field name from orderitems to camelCase orderItems in the record where OrderItem[] orderitems is declared (and likewise update the similar field on Product); update all references/usages to the field (e.g., any code accessing .orderitems) to .orderItems and ensure any related annotations or SQL relations still reference the same key symbols (OrderItem, Product, customerId) so naming is consistent with Ballerina camelCase conventions.integrator-default-profile/samples/customer-order-api/docker-compose.yml (1)
6-11: Clarify that these are sample-only defaults.The credentials and exposed port are fine for a local sample, but the README should explicitly call out that the defaults in this compose file are for local development only and must not be reused in any shared or non-local environment. Consider also supporting environment variable overrides (e.g.,
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}) so users can override without editing the file.Proposed change
environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: db_persist + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-db_persist}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/docker-compose.yml` around lines 6 - 11, The compose file exposes insecure sample defaults (POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB and the "5432:5432" port mapping) that should be documented as local-development only and made overrideable; update the README to state these are sample-only defaults and must not be reused in shared/non-local environments, and change the docker-compose environment entries (POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB) to use environment variable interpolation with sensible fallbacks (e.g., ${POSTGRES_PASSWORD:-postgres}) so users can override without editing the file and consider making the ports mapping optional or documented as for local use only.integrator-default-profile/samples/customer-order-api/db/init/01_schema.sql (1)
24-30: Consider indexingorder_items.productId.Joins/filters on
productId(e.g., sales-by-product queries) will do sequential scans. An index similar to the other FK-column indexes would be consistent and helpful.Proposed addition
CREATE INDEX IF NOT EXISTS idx_orders_customer_id ON orders("customerId"); CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items("orderId"); +CREATE INDEX IF NOT EXISTS idx_order_items_product_id ON order_items("productId");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/db/init/01_schema.sql` around lines 24 - 30, Add a non-unique index on order_items.productId to speed joins and filters; locate the order_items table definition and add an index creation for the "productId" column (e.g., create an index named like order_items_productid_idx) so queries filtering or joining on order_items."productId" avoid sequential scans.integrator-default-profile/samples/customer-order-api/connections.bal (1)
1-3: Positional arguments ondbpersist:Client new.Passing five strings/ints positionally (
dbHost, dbPort, dbUser, dbPassword, dbDatabase) is easy to get wrong if the generated client's parameter order ever changes. Consider using named arguments for readability and resilience to regeneration:Proposed change
-final dbpersist:Client customerDb = check new (dbHost, dbPort, dbUser, dbPassword, dbDatabase); +final dbpersist:Client customerDb = check new ( + host = dbHost, + port = dbPort, + user = dbUser, + password = dbPassword, + database = dbDatabase +);Please verify the actual parameter names exposed by the generated
dbpersist:Client(they depend on the persist tool version) and adjust accordingly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/connections.bal` around lines 1 - 3, The client is being constructed with positional args which is fragile; update the instantiation of dbpersist:Client (the `new` call that assigns `customerDb`) to use named arguments instead of positional values—lookup the generated parameter names for dbHost, dbPort, dbUser, dbPassword, dbDatabase in the generated `dbpersist:Client` API and pass them as name: value pairs in the `new` expression so the call becomes resilient to parameter-order changes and more readable.integrator-default-profile/samples/customer-order-api/main.bal (2)
86-99: Order and its items are inserted non-atomically.The order row is inserted first (lines 86–93) and the line items afterward (line 99). If the
orderitemsinsert fails, the parentOrderremains in the database withtotalset but no items, leaving partial data. The README already lists wrapping this flow in apersisttransaction as an extension idea — given this is a reference sample, consider demonstrating that pattern directly so readers see the recommended shape from the start.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/main.bal` around lines 86 - 99, The current flow inserts the order via customerDb->/orders.post into orderIds and then inserts items via customerDb->/orderitems.post separately, which can leave a partial order if the second insert fails; update the code to perform both inserts inside a single persist transaction (wrap the orders.post and orderitems.post calls in a persist block or equivalent transactional API), set itemInserts[i].orderId = orderId after obtaining orderId within that transaction, and ensure the transaction rolls back on any error so the order and items are committed atomically; target the customerDb->/orders.post, customerDb->/orderitems.post calls and the orderIds/orderId/itemInserts variables when making this change.
70-76: Inconsistent error mapping for product lookup failures.When the product lookup returns a generic
persist:Error(line 75), it is returned as-is and surfaces as a 500, while aNotFoundErroris mapped tohttp:BadRequest. That's reasonable, butcheckwould be more idiomatic here and equivalent:Proposed simplification
- dbpersist:Product|persist:Error product = customerDb->/products/[item.productId].get(); - if product is persist:NotFoundError { - return <http:BadRequest>{body: string `product ${item.productId} not found`}; - } - if product is persist:Error { - return product; - } + dbpersist:Product|persist:Error productResult = customerDb->/products/[item.productId].get(); + if productResult is persist:NotFoundError { + return <http:BadRequest>{body: string `product ${item.productId} not found`}; + } + dbpersist:Product product = check productResult;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@integrator-default-profile/samples/customer-order-api/main.bal` around lines 70 - 76, The product lookup currently returns a persist:NotFoundError mapped to http:BadRequest but returns other persist:Error values directly; instead, first capture the lookup result from customerDb->/products/[item.productId].get() into a var, handle persist:NotFoundError by returning http:BadRequest, and then use check to propagate any other persist:Error while assigning the dbpersist:Product (e.g., var res = customerDb->/products/[item.productId].get(); if res is persist:NotFoundError return <http:BadRequest>{...}; dbpersist:Product product = check res;). This keeps the NotFound mapping and uses check for idiomatic error propagation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@integrator-default-profile/samples/customer-order-api/Ballerina.toml`:
- Around line 1-6: The package metadata uses a personal org value and a second
conflicting org entry; update the package [package] section so the org key is
set to "wso2" (replacing "danniles") and remove the duplicate/conflicting org
entry elsewhere in the file so there is a single, consistent org value; verify
the keys in the package block (org, name, version, distribution, title) remain
intact and valid after the change.
In `@integrator-default-profile/samples/customer-order-api/db/init/02_seed.sql`:
- Around line 19-24: The seed for order_items inserts productId=1 with
unitPrice=24.99 which mismatches the products seed price for product 1 (Wireless
Mouse) at 25.99; either add a comment explaining this is an intentional
historical/promotional price or change the unitPrice in the order_items row for
productId=1 to 25.99 and then update the corresponding order total in the orders
seed (order with id 3) to reflect the corrected sum (39.95 + 25.99). Ensure you
update the rows that reference order_items, productId=1, the products seed for
product 1, and the orders row for order id 3 (orders.total) consistently.
In `@integrator-default-profile/samples/customer-order-api/main.bal`:
- Around line 61-99: Validate each NewOrderItem.quantity before using it: inside
the foreach NewOrderItem item in payload.items loop in the resource function
post orders, check that item.quantity is > 0 and if not immediately return an
http:BadRequest with a clear message (e.g., include item.productId and offending
quantity). Do this validation before calling
customerDb->/products/[item.productId].get() and before adding to total or
pushing into itemInserts so negative or zero quantities never affect total or DB
inserts.
In `@integrator-default-profile/samples/customer-order-api/persist/db/model.bal`:
- Around line 5-19: The model fields that have DB defaults should be made
optional so inserts can omit them; update the persist record types: in Order
change status to string? , total to decimal? and createdAt to time:Utc? ; in
Product change stock to int? ; in Customer change createdAt to time:Utc? ;
locate these fields in the Order, Product, and Customer record type declarations
(e.g., the Order record in model.bal) and adjust the type annotations to
nullable/optional so the generated client will not force callers to supply
DB-defaulted values.
In `@integrator-default-profile/samples/customer-order-api/README.md`:
- Around line 36-39: The README config example uses a hardcoded org header
([danniles.customer_order_api]); change it to a generic placeholder like
[<org>.customer_order_api] throughout the sample so readers know to substitute
their own org name, and update any adjacent references in the same README or
Ballerina.toml examples to match the placeholder convention (look for
occurrences of "danniles.customer_order_api" and the TOML section shown).
---
Nitpick comments:
In `@integrator-default-profile/samples/customer-order-api/connections.bal`:
- Around line 1-3: The client is being constructed with positional args which is
fragile; update the instantiation of dbpersist:Client (the `new` call that
assigns `customerDb`) to use named arguments instead of positional values—lookup
the generated parameter names for dbHost, dbPort, dbUser, dbPassword, dbDatabase
in the generated `dbpersist:Client` API and pass them as name: value pairs in
the `new` expression so the call becomes resilient to parameter-order changes
and more readable.
In `@integrator-default-profile/samples/customer-order-api/db/init/01_schema.sql`:
- Around line 24-30: Add a non-unique index on order_items.productId to speed
joins and filters; locate the order_items table definition and add an index
creation for the "productId" column (e.g., create an index named like
order_items_productid_idx) so queries filtering or joining on
order_items."productId" avoid sequential scans.
In `@integrator-default-profile/samples/customer-order-api/docker-compose.yml`:
- Around line 6-11: The compose file exposes insecure sample defaults
(POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB and the "5432:5432" port mapping)
that should be documented as local-development only and made overrideable;
update the README to state these are sample-only defaults and must not be reused
in shared/non-local environments, and change the docker-compose environment
entries (POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB) to use environment
variable interpolation with sensible fallbacks (e.g.,
${POSTGRES_PASSWORD:-postgres}) so users can override without editing the file
and consider making the ports mapping optional or documented as for local use
only.
In `@integrator-default-profile/samples/customer-order-api/main.bal`:
- Around line 86-99: The current flow inserts the order via
customerDb->/orders.post into orderIds and then inserts items via
customerDb->/orderitems.post separately, which can leave a partial order if the
second insert fails; update the code to perform both inserts inside a single
persist transaction (wrap the orders.post and orderitems.post calls in a persist
block or equivalent transactional API), set itemInserts[i].orderId = orderId
after obtaining orderId within that transaction, and ensure the transaction
rolls back on any error so the order and items are committed atomically; target
the customerDb->/orders.post, customerDb->/orderitems.post calls and the
orderIds/orderId/itemInserts variables when making this change.
- Around line 70-76: The product lookup currently returns a
persist:NotFoundError mapped to http:BadRequest but returns other persist:Error
values directly; instead, first capture the lookup result from
customerDb->/products/[item.productId].get() into a var, handle
persist:NotFoundError by returning http:BadRequest, and then use check to
propagate any other persist:Error while assigning the dbpersist:Product (e.g.,
var res = customerDb->/products/[item.productId].get(); if res is
persist:NotFoundError return <http:BadRequest>{...}; dbpersist:Product product =
check res;). This keeps the NotFound mapping and uses check for idiomatic error
propagation.
In `@integrator-default-profile/samples/customer-order-api/persist/db/model.bal`:
- Around line 16-18: Change the reverse-navigation field name from orderitems to
camelCase orderItems in the record where OrderItem[] orderitems is declared (and
likewise update the similar field on Product); update all references/usages to
the field (e.g., any code accessing .orderitems) to .orderItems and ensure any
related annotations or SQL relations still reference the same key symbols
(OrderItem, Product, customerId) so naming is consistent with Ballerina
camelCase conventions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 30420b8e-a8e5-41c2-8682-998dcdd8f95a
📒 Files selected for processing (10)
integrator-default-profile/samples/customer-order-api/Ballerina.tomlintegrator-default-profile/samples/customer-order-api/Dependencies.tomlintegrator-default-profile/samples/customer-order-api/README.mdintegrator-default-profile/samples/customer-order-api/config.balintegrator-default-profile/samples/customer-order-api/connections.balintegrator-default-profile/samples/customer-order-api/db/init/01_schema.sqlintegrator-default-profile/samples/customer-order-api/db/init/02_seed.sqlintegrator-default-profile/samples/customer-order-api/docker-compose.ymlintegrator-default-profile/samples/customer-order-api/main.balintegrator-default-profile/samples/customer-order-api/persist/db/model.bal
There was a problem hiding this comment.
Pull request overview
Adds a new standalone Ballerina integration sample (customer-order-api) that demonstrates using a generated Persist SQL client (PostgreSQL) from an HTTP REST service, backed by a small e-commerce schema and runnable via Docker Compose.
Changes:
- Introduces a Persist entity model (customers/products/orders/order_items) and Persist tool configuration for client generation.
- Adds a Ballerina HTTP service exposing basic customer/product reads and customer CRUD plus order creation.
- Adds local Postgres setup (docker-compose + schema + seed data) and end-user README instructions.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| integrator-default-profile/samples/customer-order-api/README.md | Documents how to run the sample and call the REST API endpoints. |
| integrator-default-profile/samples/customer-order-api/persist/db/model.bal | Defines Persist entities and relations for the e-commerce schema. |
| integrator-default-profile/samples/customer-order-api/main.bal | Implements the REST API and uses the generated Persist client for CRUD and inserts. |
| integrator-default-profile/samples/customer-order-api/docker-compose.yml | Provides local Postgres container configuration for the sample. |
| integrator-default-profile/samples/customer-order-api/Dependencies.toml | Locks dependency/tool versions for reproducible builds. |
| integrator-default-profile/samples/customer-order-api/db/init/01_schema.sql | Creates the Postgres schema used by the sample. |
| integrator-default-profile/samples/customer-order-api/db/init/02_seed.sql | Seeds initial customers/products/orders/order_items data. |
| integrator-default-profile/samples/customer-order-api/connections.bal | Instantiates the generated Persist client using configurable DB params. |
| integrator-default-profile/samples/customer-order-api/config.bal | Declares configurable DB connection parameters for the sample. |
| integrator-default-profile/samples/customer-order-api/Ballerina.toml | Declares the package and Persist tool configuration for client generation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Purpose
Adds an integration sample demonstrating the db persist feature.