Skip to content

Conversation

@lukaszgryglicki
Copy link
Member

@lukaszgryglicki lukaszgryglicki commented Jan 7, 2026

This adds logging of API calls to a new DynamoDB table cla-{stage}-api-log.

This table (and its GSI index allowing date range search) were added on both dev and prod but for lambdas to use them (add entries on API call) a policy update is needed. We need to list this additional table and its GSI index in 2 policies per environment (dev/prod):

cla-backend-{stage}-us-east-1-lambdaRole
cla-backend-go-{stage}-us-east-2-lambdaRole

I've added them in {stage}=dev policy, but I can't do this for prod because I don't have permissions. So we can deploy this to dev and test but for prod we need to update the following policies:

cla-backend-prod-us-east-1-lambdaRole
cla-backend-go-prod-us-east-2-lambdaRole

but adding this diff to both policies (marked with + at the line beginning:

(...)
            "Resource": [
+                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-dev-api-log",
                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-prod-ccla-whitelist-requests",
                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-prod-cla-manager-requests",
                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-prod-companies",

(...)
        {
            "Action": [
                "dynamodb:Query"
            ],
            "Resource": [
+                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-dev-api-log/index/bucket-dt-index",
                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-prod-ccla-whitelist-requests/index/company-id-project-id-index",
                "arn:aws:dynamodb:us-east-1:716487311010:table/cla-prod-ccla-whitelist-requests/index/ccla-approval-list-request-project-id-index",
(...)

I'm getting:

STAGE=prod ./utils/aws_edit_table_access.sh 

An error occurred (AccessDenied) when calling the PutRolePolicy operation: User: arn:aws:sts::716487311010:assumed-role/product-contractors-role/botocore-session-1767786994 is not authorized to perform: iam:PutRolePolicy on resource: role cla-backend-prod-us-east-1-lambdaRole because no identity-based policy allows the iam:PutRolePolicy action

cc @mlehotskylf @jarias-lfx
Zrzut ekranu 2026-01-7 o 12 58 29

This should (?) also be replicated to Snowflake (automatically? Or do I need to update something?) so we will be able to generate SQL reports based on this as well.

Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Walkthrough

Adds API request logging across services: new APILog models and repository in Go, DB-backed middleware wiring, Python ORM/model and middleware hooks to emit three time-bucketed DynamoDB entries per request, plus infra and utility scripts to provision, back up, and query the DynamoDB table.

Changes

Cohort / File(s) Summary
Go API Log Models & Repo
cla-backend-go/api_logs/models.go, cla-backend-go/api_logs/repository.go
Introduces APILog type (URL, DT, Bucket) with NewAPILog and String(). Adds Repository interface and DynamoDB implementation (NewRepository) with LogAPIRequest(ctx, url) writing three bucketed items to cla-%s-api-log.
Go Server Middleware Integration
cla-backend-go/cmd/server.go
Adds apiPathLoggerWithDB(apiLogsRepo) middleware factory, instantiates api_logs.NewRepository(...), and wires asynchronous, panic-protected DB logging into request pipeline.
Python DynamoDB Models & ORM
cla-backend/cla/models/dynamo_models.py
Adds APILogModel table, APILogBucketDTIndex GSI, and APILog ORM wrapper with CRUD and log_api_request classmethod that writes three bucketed entries; included in DB create/delete lifecycle.
Python Model Interface
cla-backend/cla/models/model_interfaces.py
Adds APILog interface methods (to_dict, save, delete, load, get_url, get_dt, get_bucket) and @classmethod log_api_request(url).
Python Route Middleware Integration
cla-backend/cla/routes.py
Adds lazy importer _get_apilog_cls() with caching and error capture; replaces middleware with process_data_api_logs and conditionally calls apilog_cls.log_api_request(request.path) when available.
Infra: Backups & Provisioning
infra/backup-dynamodb-tables.sh, utils/aws_create_api_logs_table.sh
Backup script now includes cla-${env}-api-log. New create script provisions table (url HASH, dt RANGE) and adds bucket-dt-index GSI, polling until ACTIVE.
Utils: IAM & Examples
utils/aws_edit_table_access.sh, utils/aws_example_api_logs_usage.sh, utils/example_scan_api_log.sh
New IAM policy edit helper and example CLI scripts demonstrating PutItem/Query/Scan against cla-*-api-log (usage and scan examples).
Serverless IAM Updates
cla-backend-go/serverless.yml, cla-backend/serverless.yml
Adds IAM resource ARNs for cla-${stage}-api-log table and bucket-dt-index to allow Query/Describe/ListStreams as needed.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant GoMiddleware as Go Middleware
    participant GoRepo as Go Repository
    participant PyMiddleware as Python Middleware
    participant PyModel as Python APILog Model
    participant DynamoDB

    Client->>GoMiddleware: HTTP request (path)
    alt Go service with DB logger
        GoMiddleware->>GoRepo: LogAPIRequest(ctx, url) [async]
        GoRepo->>GoRepo: build 3 APILog items (ALL, daily, monthly)
        GoRepo->>DynamoDB: PutItem ×3
        DynamoDB-->>GoRepo: OK / Error
    end
    Client->>PyMiddleware: HTTP request (path)
    alt Python service with APILog
        PyMiddleware->>PyModel: log_api_request(path)
        PyModel->>PyModel: build & save 3 entries (ALL, daily, monthly)
        PyModel->>DynamoDB: PutItem ×3
        DynamoDB-->>PyModel: OK / Error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Unicron add api logs in dynamodb' directly and specifically describes the main change: adding API logging functionality to DynamoDB.
Description check ✅ Passed The description is detailed and directly related to the changeset, explaining what API logs were added to DynamoDB, the table structure, required IAM policy updates, and deployment constraints.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch unicron-add-api-logs-in-dynamodb

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @cla-backend-go/cmd/server.go:
- Around line 171-176: Increase the timeout and make the DynamoDB log call
asynchronous: replace the 300ms context.WithTimeout with a 1-2 second timeout
(e.g., 1s or 2s) when creating ctx/cancel for the apiLogsRepo.LogAPIRequest
call, then invoke LogAPIRequest inside a new goroutine so it doesn't block the
handler (create the ctx for that goroutine and call defer cancel inside it).
Ensure the goroutine body is wrapped with a full panic recovery (recover) that
covers the entire goroutine function, and reference the existing
apiLogsRepo.LogAPIRequest and r.URL.Path when moving the call to the goroutine
so the log still records the path.

In @utils/aws_edit_table_access.sh:
- Around line 14-26: The script currently writes IAM policy JSON to predictable
/tmp paths (e.g., /tmp/cla-backend-${STAGE}-${AWS_REGION}-lambda.json), which is
insecure; change it to create secure temporary files with mktemp and restrictive
permissions and store their paths in variables BACKEND_POLICY and
BACKEND_GO_POLICY (e.g., BACKEND_POLICY=$(mktemp) && chmod 600
"$BACKEND_POLICY"), then direct the aws --query PolicyDocument output into those
variables and update all subsequent references to use ${BACKEND_POLICY} and
${BACKEND_GO_POLICY} instead of hard-coded /tmp filenames so the files are
non-predictable and readable only by the current user.
- Around line 28-40: The script currently runs iam put-role-policy immediately
for cla-backend-${STAGE}-${AWS_REGION}-lambda and cla-backend-go-${STAGE}-lambda
using /tmp/cla-backend-...-lambda.json files; add pre-flight validation and
confirmation: validate each temp JSON with a JSON parser (e.g., jq -e) and exit
on parse errors, create backups of the existing policies using aws iam
get-role-policy --role-name <role> --policy-name <policy> and save to /tmp
before applying changes, display a summary of the target role/policy and the
file path and prompt the operator to confirm (Y/N) before invoking iam
put-role-policy, and optionally offer a dry-run by running aws iam
simulate-custom-policy or printing the parsed JSON for review; ensure the
changes reference the exact role names
(cla-backend-${STAGE}-${AWS_REGION}-lambdaRole, cla-backend-${STAGE}-lambda,
cla-backend-go-${STAGE}-us-east-2-lambdaRole, cla-backend-go-${STAGE}-lambda)
and abort without making changes if validation or confirmation fails.

In @utils/aws_example_api_logs_usage.sh:
- Around line 1-63: The script mixes executable aws CLI commands (e.g., the aws
--profile lfproduct-dev dynamodb query/put-item commands) with raw JSON example
outputs, which will break shell execution; separate them by either commenting
out the example JSON blocks, wrapping the example outputs in a heredoc (e.g.,
cat <<'EOF' ... EOF) so they are treated as plain text, or move the examples to
a Markdown documentation file; update the sections currently containing the JSON
examples (the query response blocks following the aws query commands)
accordingly so the script only contains runnable commands or properly
quoted/documented output.
🧹 Nitpick comments (3)
utils/aws_edit_table_access.sh (1)

1-40: Add error handling to prevent partial policy updates.

The script lacks error checking for AWS CLI commands, which could result in partial updates if one command fails while another succeeds.

⚡ Proposed error handling
 #!/bin/bash
+set -euo pipefail
 
 if [ -z "$AWS_REGION" ]; then
   echo "AWS_REGION is not set. Please set it before running the script."
   exit 1
 fi
 if [ -z "$STAGE" ]; then
   echo "STAGE is not set. Please set it before running the script."
   exit 1
 fi
 
+# Check AWS CLI is available
+if ! command -v aws &> /dev/null; then
+  echo "Error: aws CLI not found"
+  exit 1
+fi
+
 # ... rest of script with error checking built in via set -e

The -e flag will cause the script to exit immediately if any AWS command fails, preventing partial updates.

utils/aws_create_api_logs_table.sh (1)

65-65: Add quotes around variable expansions for robustness.

While unlikely in this context, profile names containing spaces or special characters could cause issues.

Recommended fix
-aws --profile ${PROFILE} dynamodb describe-table --table-name cla-${STAGE}-api-log --region ${AWS_REGION}
+aws --profile "${PROFILE}" dynamodb describe-table --table-name "cla-${STAGE}-api-log" --region "${AWS_REGION}"
cla-backend-go/api_logs/repository.go (1)

46-48: Consider: Nil context handling masks caller bugs.

While the "200% fail-safe" approach prevents panics, replacing nil context with Background() masks caller bugs. In Go, passing nil context typically indicates a programming error that should be caught early.

Consider returning an error immediately if ctx is nil:

Alternative nil context handling
 func (r *repository) LogAPIRequest(ctx context.Context, url string) error {
-	// 200% fail-safe: never panic on nil ctx/client
 	if ctx == nil {
-		ctx = context.Background()
+		return fmt.Errorf("context cannot be nil")
 	}
 	if r == nil || r.dynamoDBClient == nil {
 		return fmt.Errorf("dynamodb client is nil")
 	}

However, if the defensive approach is intentional for this logging use case (where failures should be extremely rare), the current implementation is acceptable.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7f01c5f and 0d75d4b.

📒 Files selected for processing (11)
  • cla-backend-go/api_logs/models.go
  • cla-backend-go/api_logs/repository.go
  • cla-backend-go/cmd/server.go
  • cla-backend/cla/models/dynamo_models.py
  • cla-backend/cla/models/model_interfaces.py
  • cla-backend/cla/routes.py
  • infra/backup-dynamodb-tables.sh
  • utils/aws_create_api_logs_table.sh
  • utils/aws_edit_table_access.sh
  • utils/aws_example_api_logs_usage.sh
  • utils/example_scan_api_log.sh
🧰 Additional context used
🧬 Code graph analysis (6)
cla-backend-go/api_logs/models.go (2)
cla-backend/cla/models/dynamo_models.py (1)
  • APILog (5429-5516)
cla-backend/cla/models/model_interfaces.py (1)
  • APILog (2388-2462)
utils/aws_create_api_logs_table.sh (1)
tests/py2go/dynamo.go (1)
  • STAGE (16-16)
cla-backend/cla/routes.py (2)
cla-backend/cla/models/dynamo_models.py (2)
  • APILog (5429-5516)
  • log_api_request (5480-5516)
cla-backend/cla/models/model_interfaces.py (2)
  • APILog (2388-2462)
  • log_api_request (2454-2462)
cla-backend-go/cmd/server.go (1)
cla-backend-go/api_logs/repository.go (2)
  • Repository (23-25)
  • NewRepository (34-39)
cla-backend/cla/models/dynamo_models.py (2)
cla-backend/cla/models/model_interfaces.py (65)
  • APILog (2388-2462)
  • to_dict (14-21)
  • to_dict (302-309)
  • to_dict (609-616)
  • to_dict (787-794)
  • to_dict (1206-1213)
  • to_dict (1467-1474)
  • to_dict (1722-1729)
  • to_dict (1863-1870)
  • to_dict (1909-1916)
  • to_dict (2011-2018)
  • to_dict (2057-2064)
  • to_dict (2103-2110)
  • to_dict (2155-2156)
  • to_dict (2173-2180)
  • to_dict (2300-2307)
  • to_dict (2347-2354)
  • save (23-29)
  • save (311-315)
  • save (618-622)
  • save (796-800)
  • save (1215-1219)
  • save (1872-1876)
  • save (1918-1922)
  • save (2020-2024)
  • save (2066-2070)
  • save (2112-2116)
  • save (2158-2159)
  • save (2182-2186)
  • save (2309-2313)
  • save (2356-2360)
  • save (2402-2406)
  • load (31-39)
  • load (317-325)
  • load (624-632)
  • load (802-810)
  • load (1221-1229)
  • load (1878-1886)
  • load (1924-1932)
  • load (2026-2034)
  • load (2072-2080)
  • load (2118-2126)
  • load (2161-2162)
  • load (2188-2196)
  • load (2321-2329)
  • load (2368-2376)
  • load (2414-2424)
  • delete (41-47)
  • delete (327-331)
  • delete (634-638)
  • delete (812-816)
  • delete (1231-1235)
  • delete (1888-1892)
  • delete (1934-1938)
  • delete (2036-2040)
  • delete (2082-2086)
  • delete (2128-2132)
  • delete (2164-2165)
  • delete (2315-2319)
  • delete (2362-2366)
  • delete (2408-2412)
  • get_url (2426-2433)
  • get_dt (2435-2442)
  • get_bucket (2444-2451)
  • log_api_request (2454-2462)
cla-backend-go/api_logs/models.go (1)
  • APILog (12-16)
cla-backend-go/api_logs/repository.go (1)
cla-backend-go/api_logs/models.go (1)
  • APILog (12-16)
🪛 Ruff (0.14.10)
cla-backend/cla/routes.py

57-57: Consider moving this statement to an else block

(TRY300)


58-58: Do not catch blind exception: Exception

(BLE001)


68-68: Unused function argument: response

(ARG001)


81-81: Do not catch blind exception: Exception

(BLE001)

cla-backend/cla/models/dynamo_models.py

5455-5455: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


5455-5455: Avoid specifying long messages outside the exception class

(TRY003)


5507-5507: Do not catch blind exception: Exception

(BLE001)


5514-5514: Do not catch blind exception: Exception

(BLE001)

🪛 Shellcheck (0.11.0)
utils/aws_example_api_logs_usage.sh

[error] 16-16: Couldn't parse this brace group. Fix to allow more checks.

(SC1073)


[warning] 19-19: This { is literal. Check expression (missing ;/\n?) or quote it.

(SC1083)


[error] 21-21: Expected a '}'. If you have one, try a ; or \n in front of it.

(SC1056)


[error] 21-21: Missing '}'. Fix any mentioned problems and try again.

(SC1072)


[error] 21-21: Unexpected tokens after compound command. Bad redirection or missing ;/&&/||/|?

(SC1141)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: CodeQL analysis (go)
  • GitHub Check: Agent
  • GitHub Check: build-test-lint
  • GitHub Check: cypress-functional
🔇 Additional comments (16)
cla-backend-go/api_logs/models.go (2)

11-16: LGTM - Clean data model definition.

The struct definition is well-designed with appropriate types and tags for DynamoDB marshaling and JSON serialization.


18-21: LGTM - Standard String() implementation.

The method provides a clear representation for debugging and logging purposes.

utils/example_scan_api_log.sh (1)

1-2: LGTM - Clear example script.

The script provides a straightforward example for scanning the dev API log table. The hardcoded values are appropriate for an example/documentation script.

infra/backup-dynamodb-tables.sh (1)

63-64: LGTM - API log table added to backups.

The new API log table is correctly included in the backup list with consistent naming and formatting.

cla-backend/cla/models/model_interfaces.py (1)

2388-2462: LGTM! Well-documented interface.

The new APILog interface is comprehensive and follows the existing pattern in the codebase. The documentation clearly describes the expected behavior, including the three-bucket logging strategy (ALL, daily, monthly).

cla-backend/cla/routes.py (2)

44-61: Verify the broad exception handling won't mask import-time issues.

The lazy import catches all exceptions, which is appropriate for a fallback mechanism. However, ensure that import failures are intentional (e.g., when the model isn't deployed yet) rather than masking actual errors like missing dependencies or syntax errors.

Consider adding more specific logging to distinguish between expected vs. unexpected import failures:

     try:
         from cla.models.dynamo_models import APILog as _APILog
         _APILOG_CLS = _APILog
+        cla.log.info("LG:api-log-model-loaded successfully")
         return _APILOG_CLS
     except Exception as e:
         _APILOG_IMPORT_ERROR = e
-        cla.log.info(f"LG:api-log-import-failed err={e}")
+        cla.log.warning(f"LG:api-log-import-failed err={e} type={type(e).__name__}")
         return None

If the model should always be available in your deployment, consider whether this should log at ERROR level or even fail fast during startup.


76-82: Error handling is appropriate for best-effort logging.

The broad exception catch on Line 81 ensures API logging failures never break the request flow, which is the correct behavior for observability features. The static analysis warning can be safely ignored in this context.

cla-backend/cla/models/dynamo_models.py (5)

61-61: LGTM: API log model properly integrated into database lifecycle.

The APILogModel is correctly added to both create_database and delete_database table lists, ensuring the API log table is managed consistently with other tables.

Also applies to: 87-87


5389-5407: LGTM: GSI properly configured for time-range queries.

The APILogBucketDTIndex correctly defines a global secondary index with bucket as hash key and dt as range key, enabling efficient queries for log entries within specific time buckets (ALL, daily, monthly).


5409-5427: LGTM: Table model correctly defines composite key.

The APILogModel properly uses a composite key (url as hash key, dt as range key) which allows multiple log entries for the same URL with different timestamps, while the GSI enables time-range queries.


5447-5449: Verify: Commented out date_modified tracking.

The save() method has date_modified update commented out, which differs from the pattern used in other models that inherit from BaseModel. If API logs don't require modification tracking for performance reasons, this is acceptable. However, if date_modified should be tracked, uncomment line 5448.

Is the omission of date_modified tracking intentional for API logs?


5479-5517: LGTM: Robust API request logging with defensive error handling.

The log_api_request implementation correctly:

  • Generates millisecond timestamps and creates three log entries (ALL, daily, monthly buckets)
  • Shifts timestamps by -1/0/+1 ms to avoid overwrites on the composite key (url, dt)
  • Uses broad exception catching (lines 5507, 5514) which is intentionally designed to ensure logging failures never disrupt the API request flow
  • Logs errors with "LG:" prefix for centralized monitoring without raising exceptions

The static analysis warnings about broad exception catching can be safely ignored as this defensive approach is appropriate for non-critical logging operations.

cla-backend-go/api_logs/repository.go (4)

17-25: LGTM: Clear interface and naming convention.

The repository interface and table name constant are well-defined. The table name format matches the Python implementation.


33-39: LGTM: Constructor properly initializes repository.

The NewRepository constructor correctly initializes the repository with stage and DynamoDB client, returning the interface type for better testability.


53-64: LGTM: Timestamp and bucket logic consistent with Python implementation.

The timestamp generation (UnixMilli()) and bucket creation correctly mirror the Python implementation:

  • Uses millisecond precision timestamps
  • Creates three entries with -1/0/+1 ms shifts to avoid overwrites
  • Formats daily (YYYY-MM-DD) and monthly (YYYY-MM) buckets consistently

67-93: LGTM: Error aggregation enables partial success.

The error handling strategy is well-designed:

  • Collects errors per bucket but continues processing remaining entries
  • Allows partial success (if "ALL" bucket fails, daily/monthly may still succeed)
  • Returns aggregated error string for middleware logging
  • Consistent with Python implementation's error handling philosophy

This approach is appropriate for non-critical logging where partial success is preferable to complete failure.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds API request logging functionality to DynamoDB for the EasyCLA backend services. The implementation creates a new table cla-{stage}-api-log that records every API call with timestamp and bucketing information for efficient querying.

  • Adds DynamoDB table structure with url/dt primary key and bucket/dt GSI for time-range queries
  • Implements middleware in both Python and Go backends to log all API requests
  • Provides utility scripts for table creation, policy updates, and data querying
  • Creates three log entries per request (ALL, daily, monthly buckets) with microsecond timestamp offsets to avoid key conflicts

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
utils/example_scan_api_log.sh Simple script to scan and display API log entries for development/testing
utils/aws_example_api_logs_usage.sh Example AWS CLI commands demonstrating table operations and GSI queries
utils/aws_edit_table_access.sh Script to update IAM policies to grant Lambda roles access to the new API log table
utils/aws_create_api_logs_table.sh Table creation script with primary key and GSI setup
infra/backup-dynamodb-tables.sh Adds the new API log table to the backup table list
cla-backend/cla/routes.py Adds API logging to request middleware with lazy import to avoid circular dependencies
cla-backend/cla/models/model_interfaces.py Defines the APILog interface with standard CRUD operations and log_api_request class method
cla-backend/cla/models/dynamo_models.py Implements APILogModel and APILog with bucket-dt GSI and three-entry logging logic
cla-backend-go/cmd/server.go Integrates API logging middleware into the Go backend server with 300ms timeout
cla-backend-go/api_logs/repository.go Go implementation of the API logging repository with context-aware DynamoDB writes
cla-backend-go/api_logs/models.go Defines the APILog struct with DynamoDB attribute mappings

Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cla-backend/cla/routes.py (1)

44-61: LGTM: Lazy import pattern is sound for best-effort logging.

The caching mechanism prevents repeated imports while gracefully handling failures. The broad exception catch is intentional—API logging should never break the request flow.

There's a minor race condition where multiple threads could simultaneously attempt the initial import, but the impact is negligible (wasteful but not harmful, and only occurs during startup). After the first successful import, all subsequent requests use the cached value.

🔒 Optional: Add threading.Lock for perfect thread-safety

If you want to eliminate the startup race condition entirely:

+import threading
+
+_APILOG_CLS = None
+_APILOG_IMPORT_ERROR = None
+_apilog_lock = threading.Lock()

 def _get_apilog_cls():
     """
     Lazy, cached import to avoid per-request imports while staying safe
     against circular-import/startup ordering issues.
     """
     global _APILOG_CLS, _APILOG_IMPORT_ERROR
     if _APILOG_CLS is not None:
         return _APILOG_CLS
     if _APILOG_IMPORT_ERROR is not None:
         return None
+    with _apilog_lock:
+        # Double-check after acquiring lock
+        if _APILOG_CLS is not None:
+            return _APILOG_CLS
+        if _APILOG_IMPORT_ERROR is not None:
+            return None
         try:
             from cla.models.dynamo_models import APILog as _APILog
             _APILOG_CLS = _APILog
             return _APILOG_CLS
         except Exception as e:
             _APILOG_IMPORT_ERROR = e
             cla.log.info(f"LG:api-log-import-failed err={e}")
             return None
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0d75d4b and d2d6901.

📒 Files selected for processing (2)
  • cla-backend-go/cmd/server.go
  • cla-backend/cla/routes.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • cla-backend-go/cmd/server.go
🧰 Additional context used
🧬 Code graph analysis (1)
cla-backend/cla/routes.py (2)
cla-backend/cla/models/model_interfaces.py (2)
  • APILog (2388-2462)
  • log_api_request (2454-2462)
cla-backend/cla/models/dynamo_models.py (2)
  • APILog (5429-5516)
  • log_api_request (5480-5516)
🪛 Ruff (0.14.10)
cla-backend/cla/routes.py

57-57: Consider moving this statement to an else block

(TRY300)


58-58: Do not catch blind exception: Exception

(BLE001)


68-68: Unused function argument: response

(ARG001)


84-84: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-test-lint
  • GitHub Check: cypress-functional
🔇 Additional comments (2)
cla-backend/cla/routes.py (2)

41-42: LGTM: Clean caching globals for lazy import pattern.

The module-level variables are well-named with the private convention and serve their caching purpose clearly.


68-86: LGTM: Middleware correctly integrates API logging as a best-effort operation.

The implementation properly:

  • Uses the lazy-import pattern to avoid per-request overhead
  • Catches and logs failures to prevent API logging errors from breaking request flow
  • Preserves existing GitHub activity stream behavior
  • Documents the dual purpose in the docstring

The static analysis warnings are false positives:

  • The response parameter is required by the hug framework's request middleware signature
  • The broad exception catch is intentional—this ensures logging never disrupts normal request handling

Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cla-backend-go/api_logs/repository.go (1)

60-87: Consider using BatchWriteItem for better performance.

The current implementation makes 3 separate PutItem calls per API request (ALL, daily, and monthly buckets). This approach:

  • Triples the number of DynamoDB write operations
  • Increases write latency (3 sequential network calls)
  • Increases DynamoDB write costs

Consider refactoring to use BatchWriteItemWithContext to write all three entries in a single call, which would reduce both latency and cost.

♻️ Proposed optimization using BatchWriteItem
-	var errs []string
-	for _, logEntry := range entries {
-		// Convert to DynamoDB attribute value
-		av, err := dynamodbattribute.MarshalMap(logEntry)
-		if err != nil {
-			errs = append(errs, fmt.Sprintf("bucket=%s marshal=%v", logEntry.Bucket, err))
-			continue
-		}
-
-		// Put item to DynamoDB
-		input := &dynamodb.PutItemInput{
-			TableName: aws.String(tableName),
-			Item:      av,
-		}
-
-		_, err = r.dynamoDBClient.PutItemWithContext(ctx, input)
-		if err != nil {
-			errs = append(errs, fmt.Sprintf("bucket=%s put=%v", logEntry.Bucket, err))
-			continue
-		}
-	}
+	// Prepare batch write request
+	writeRequests := make([]*dynamodb.WriteRequest, 0, len(entries))
+	for _, logEntry := range entries {
+		av, err := dynamodbattribute.MarshalMap(logEntry)
+		if err != nil {
+			return fmt.Errorf("marshal bucket=%s: %v", logEntry.Bucket, err)
+		}
+		writeRequests = append(writeRequests, &dynamodb.WriteRequest{
+			PutRequest: &dynamodb.PutRequest{Item: av},
+		})
+	}
+
+	// Single batch write for all entries
+	input := &dynamodb.BatchWriteItemInput{
+		RequestItems: map[string][]*dynamodb.WriteRequest{
+			tableName: writeRequests,
+		},
+	}
+	
+	_, err := r.dynamoDBClient.BatchWriteItemWithContext(ctx, input)
+	if err != nil {
+		return fmt.Errorf("batch write failed: %v", err)
+	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d2d6901 and 44edeaa.

📒 Files selected for processing (2)
  • cla-backend-go/api_logs/repository.go
  • cla-backend-go/cmd/server.go
🧰 Additional context used
🧬 Code graph analysis (2)
cla-backend-go/cmd/server.go (1)
cla-backend-go/api_logs/repository.go (2)
  • Repository (23-25)
  • NewRepository (34-39)
cla-backend-go/api_logs/repository.go (1)
cla-backend-go/api_logs/models.go (1)
  • APILog (12-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cypress-functional
  • GitHub Check: build-test-lint
🔇 Additional comments (5)
cla-backend-go/cmd/server.go (3)

18-18: LGTM: Required imports added.

The time and api_logs imports are necessary for the new DynamoDB logging middleware.

Also applies to: 82-82


307-307: LGTM: Repository initialization.

The API logs repository is correctly initialized with the stage and DynamoDB client.


433-433: LGTM: Middleware wiring.

The middleware is correctly wired into the handler chain with the API logs repository.

cla-backend-go/api_logs/repository.go (2)

1-20: LGTM: Package setup and table name constant.

The imports and table name constant are correctly defined for DynamoDB operations.


22-39: LGTM: Interface and constructor design.

Clean separation of interface and implementation with proper encapsulation.

Signed-off-by: Lukasz Gryglicki <[email protected]>

Assisted by [OpenAI](https://platform.openai.com/)

Assisted by [GitHub Copilot](https://github.com/features/copilot)
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @cla-backend-go/cmd/server.go:
- Line 308: The code instantiates apiLogsRepo via api_logs.NewRepository(stage,
dynamodb.New(awsSession)) but the serverless IAM policy is missing permissions
for the DynamoDB table cla-${opt:stage}-api-log; add the table ARN
"arn:aws:dynamodb:${self:custom.dynamodb.region}:${aws:accountId}:table/cla-${opt:stage}-api-log"
to the IAM policy in serverless.yml and ensure the cla-dev-api-log and
cla-prod-api-log tables are created in your infra with the required schema
(partition key: url, sort key: dt) so LogAPIRequest and apiLogsRepo writes
succeed at runtime.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 44edeaa and b95cc2d.

📒 Files selected for processing (1)
  • cla-backend-go/cmd/server.go
🧰 Additional context used
🧬 Code graph analysis (1)
cla-backend-go/cmd/server.go (1)
cla-backend-go/api_logs/repository.go (2)
  • Repository (23-25)
  • NewRepository (34-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-test-lint
  • GitHub Check: cypress-functional
🔇 Additional comments (3)
cla-backend-go/cmd/server.go (3)

18-18: LGTM: Import additions are appropriate.

The new imports (time and api_logs) are necessary for the DynamoDB logging middleware functionality.

Also applies to: 82-82


149-177: Excellent implementation: Previous review feedback fully addressed.

The async DynamoDB logging middleware correctly implements the recommendations from the previous review:

Timeout increased: Now 3 seconds (line 165), exceeding the suggested 1-2s minimum
Async execution: Runs in a goroutine (line 158) to avoid blocking the request
Panic recovery: Properly wraps the goroutine body (lines 159-163)
Correct context: Uses context.Background() instead of request context (line 165)
Closure safety: Path variable captured before goroutine (line 157)

The fire-and-forget pattern is appropriate for logging—errors are logged but never fail the request.


434-434: LGTM: Middleware wiring is correct.

The new DB-backed logger is properly integrated as the outermost middleware in the chain, ensuring all API requests are logged before any other processing occurs. The repository is correctly passed to the factory function.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
cla-backend-go/serverless.yml (1)

125-125: Consider scoping permissions to logging requirements.

The API log table inherits broad DynamoDB permissions including UpdateItem and DeleteItem. If the table is write-only for logging purposes, consider creating a separate, narrower policy statement that grants only PutItem and Query to reduce the attack surface.

cla-backend/serverless.yml (1)

210-210: Consider scoping permissions to logging requirements.

Similar to the Go backend, the API log table inherits broad DynamoDB permissions including UpdateItem and DeleteItem. If the table is primarily for write-only logging, consider a separate policy statement with narrower permissions (PutItem and Query only).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b95cc2d and c08fbf0.

📒 Files selected for processing (2)
  • cla-backend-go/serverless.yml
  • cla-backend/serverless.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-test-lint
  • GitHub Check: cypress-functional
🔇 Additional comments (2)
cla-backend-go/serverless.yml (1)

148-148: LGTM!

The GSI ARN is correctly added to the Query-only permission block, which aligns with DynamoDB best practices since indexes don't support direct write operations.

cla-backend/serverless.yml (1)

233-233: LGTM!

The GSI ARN is correctly placed in the Query-only permission block, consistent with DynamoDB index access patterns and matching the Go backend configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants