You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(V2): enforce response contract and required-field validation for CSV batch
Return stagedCount/errorCount from batchUpload models. Controllers now
branch response: full success (200), partial success (200 with errors),
or total failure (400 with success:false) when zero rows are staged.
Add required-field validation for INSERT rows — checks allowNull:false
fields from the Sequelize model definition, excluding auto-managed
fields (PK, orgUid, timestamps) and derivable fields (unitSerialId).
UPDATE rows skip this check since missing fields merge from the DB.
Addresses review findings:
- FINDING 1: success:true when 0 rows staged (misleading contract)
- FINDING 2: missing required-field validation for CSV INSERT rows
Copy file name to clipboardExpand all lines: docs/cadt_rpc_api_v2.md
+45-10Lines changed: 45 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2422,7 +2422,7 @@ CSV headers may use either camelCase attribute names (e.g., `cadTrustProjectId`)
2422
2422
2423
2423
**Update behavior**: When a row includes a `cadTrustProjectId` that matches an existing project, the CSV row is **merged** with the existing DB record — fields present in the CSV overwrite the existing values; fields absent from the CSV retain their current values. This differs from the REST `PUT` endpoint, which requires all fields.
2424
2424
2425
-
**Validation**: Rows are validated for foreign key existence (`cadTrustProgramId` must reference an existing program or a staged programrecord). Rows that fail validation are skipped and their errors are returned in the response. The CSV validation is intentionally more lenient than the REST API — fields like `projectLink` and `projectStatusDate` that are required in the REST schema are optional in CSV batch upload.
2425
+
**Validation**: INSERT rows are validated for required fields (`projectRegistryName`, `projectId`, `projectName`) and foreign key existence (`cadTrustProgramId` must reference an existing or staged program). UPDATE rows skip required-field checks since missing fields are merged from the existing record. Rows that fail validation are skipped and their errors are returned in the response with row numbers. The CSV validation is intentionally more lenient than the REST API — fields like `projectLink` and `projectStatusDate` that are required in the REST schema are optional in CSV batch upload. If **all** rows fail validation, the response returns HTTP 400 with `success: false`.
2426
2426
2427
2427
**Ownership**: UPDATE rows are verified to belong to the home organization. Attempting to update a project owned by another organization will produce an error for that row.
2428
2428
@@ -2444,20 +2444,38 @@ Request
2444
2444
curl --location --request POST 'http://localhost:31310/v2/project/batch' --form 'csv=@"./createProject.csv"'
2445
2445
```
2446
2446
2447
-
Response (success, no row-level errors)
2447
+
Response (full success — all rows staged)
2448
2448
```json
2449
2449
{
2450
2450
"message": "CSV processing complete, your records have been added to the staging table.",
2451
-
"success": true
2451
+
"success": true,
2452
+
"stagedCount": 3,
2453
+
"errorCount": 0
2452
2454
}
2453
2455
```
2454
2456
2455
-
Response (success with row-level errors — valid rows are still staged)
2457
+
Response (partial success — some rows staged, some failed)
2456
2458
```json
2457
2459
{
2458
-
"message": "CSV processing complete, your records have been added to the staging table.",
2460
+
"message": "CSV processing complete. 2 row(s) staged, 1 row(s) skipped due to errors.",
2459
2461
"success": true,
2462
+
"stagedCount": 2,
2463
+
"errorCount": 1,
2464
+
"errors": [
2465
+
{ "row": 3, "error": "cadTrustProgramId 'invalid-id' does not exist" }
2466
+
]
2467
+
}
2468
+
```
2469
+
2470
+
Response (failure — no rows staged, HTTP 400)
2471
+
```json
2472
+
{
2473
+
"message": "No rows were staged. All rows failed validation.",
{ "row": 3, "error": "cadTrustProgramId 'invalid-id' does not exist" }
2462
2480
]
2463
2481
}
@@ -3718,7 +3736,7 @@ CSV headers may use either camelCase attribute names (e.g., `cadTrustUnitId`) or
3718
3736
3719
3737
**Serial ID derivation**: If `unitSerialId` is not provided but `unitStartBlock` and `unitEndBlock` are, the serial ID is automatically derived as `<unitStartBlock>-<unitEndBlock>`.
3720
3738
3721
-
**Validation**: Rows are validated for foreign key existence (`cadTrustIssuanceId` must reference an existing issuance or a staged issuance record). Rows that fail validation are skipped and their errors are returned in the response.
3739
+
**Validation**: INSERT rows are validated for required fields (`unitStartBlock`, `unitEndBlock`, `unitVintageYear`, `cadTrustIssuanceId`) and foreign key existence (`cadTrustIssuanceId` must reference an existing or staged issuance). `unitSerialId` is not required if `unitStartBlock` and `unitEndBlock` are provided (it is derived automatically). UPDATE rows skip required-field checks since missing fields are merged from the existing record. Rows that fail validation are skipped and their errors are returned in the response with row numbers. If **all** rows fail validation, the response returns HTTP 400 with `success: false`.
3722
3740
3723
3741
**Ownership**: UPDATE rows are verified to belong to the home organization. Attempting to update a unit owned by another organization will produce an error for that row.
3724
3742
@@ -3727,25 +3745,42 @@ Request
3727
3745
curl --location --request POST 'http://localhost:31310/v2/unit/batch' --form 'csv=@"./createUnit.csv"'
3728
3746
```
3729
3747
3730
-
Response (success, no row-level errors)
3748
+
Response (full success — all rows staged)
3731
3749
```json
3732
3750
{
3733
3751
"message": "CSV processing complete, your records have been added to the staging table.",
3734
-
"success": true
3752
+
"success": true,
3753
+
"stagedCount": 3,
3754
+
"errorCount": 0
3735
3755
}
3736
3756
```
3737
3757
3738
-
Response (success with row-level errors — valid rows are still staged)
3758
+
Response (partial success — some rows staged, some failed)
3739
3759
```json
3740
3760
{
3741
-
"message": "CSV processing complete, your records have been added to the staging table.",
3761
+
"message": "CSV processing complete. 2 row(s) staged, 1 row(s) skipped due to errors.",
3742
3762
"success": true,
3763
+
"stagedCount": 2,
3764
+
"errorCount": 1,
3743
3765
"errors": [
3744
3766
{ "row": 4, "error": "cadTrustIssuanceId 'invalid-id' does not exist" }
3745
3767
]
3746
3768
}
3747
3769
```
3748
3770
3771
+
Response (failure — no rows staged, HTTP 400)
3772
+
```json
3773
+
{
3774
+
"message": "No rows were staged. All rows failed validation.",
0 commit comments