APIs & services / asset-service

Save one or more endorsements (approve/reject). Sent online or through the offline queue.

Endpointtoday's app
Call
POST /gateway/asset-service/api/v1/endorsement/bulk/save-all
Service
asset-service
Features
VO / SHG / Farmer endorsement, Farmer endorsement details
When
User action (online bulk endorse) or queued sync
Auth
X-SESSIONID header (AuthInterceptor adds it; re-auth on 401/400); queued path: X-SESSIONID stamped when queued (CoreAPI dooWithRetry); X-REQUESTID idempotency key
In the App Service

Use case

Saves endorsement decisions (endorse, flag or send for review) on two paths. (1) Offline queue: endorsing one VO, SHG or farmer writes endorsement_entity plus an ENDORSEMENT POST sync-ledger entry. EndorsementWorker builds EndorsementEntity.post() (a one-item list) and queues it with dooWithRetry. On success EndorsementPostRH stores the returned id, closes the ledger, and sends an FCM data message to /topics/urvi_endorsement_status (see the FCM entry). (2) Online bulk endorse from the farmer list (EndorsementViewModel.endorseBulkFarmers) posts the whole selection directly and, on success, updates the local rows. Note: this path never posts a success Resource, so the UI only ever sees errors. Offline-path errors stay in the queue and retry later.

Headers

HeaderValueNotes
X-SESSIONID<session id>Stamped by CoreAPI when the queued operation runs (CoreAPIDefaultImpl.doOperation). On a 401 the token is invalidated and the call retried. BaseResponseHandler also refreshes the session after a 500 on a gateway URL (first 5 attempts).
X-REQUESTID<uuid v4>Idempotency key added by the offline queue (OperationManagerImpl). A 409 replaces it with a new UUID. It is removed for multipart requests.
Content-Typeapplication/jsonSet by the HTTP client for JSON bodies.

Request

Example request body (synthetic data)
[
  {
    "id": 0,
    "uuid": "e0000000-0000-4000-8000-000000003101",
    "entityType": "ENTITY.TYPE.SHG",
    "entityId": "440012",
    "entityCode": "SHG-0012",
    "endorsedBy": "Cadre A",
    "endorsedUserId": 1001,
    "name": "SHG A",
    "status": "ENDORSED",
    "endorsementType": "SHG",
    "districtName": "District A",
    "unitName": "Unit A",
    "mandalCode": "M-01",
    "mandalName": "Mandal A",
    "voCode": "VO-01",
    "voName": "VO A",
    "shgCode": "SHG-0012",
    "shgName": "SHG A",
    "reviewdBy": null,
    "reviewByUserId": null,
    "reviewedTimestamp": null,
    "timestamp": 1748772000000,
    "createdAt": 1748772000000,
    "hasNpmShop": false,
    "abstractFile": null
  }
]
FieldTypeRequiredNotes
[].idintNoServer id. 0 for a new endorsement. Updated from the POST response.
[].uuidstringYesClient-generated primary key.
[].entityTypestringYesENTITY.TYPE.VO, ENTITY.TYPE.SHG or ENTITY.TYPE.FARMER.
[].entityId / entityCodestringYesVO, SHG or farmer server id and code. Used to update IE flat endorsement fields.
[].statusstringYesPENDING, ENDORSED, FLAGGED or FOR_REVIEW (VoConstants). Comes from the action the user picks on EndorsementFragment.
[].endorsedBy / endorsedUserIdstring / longNoPII: name and user id of the endorsing cadre.
[].reviewdBy / reviewByUserId / reviewedTimestampstring / long / longNoReviewer (unit in-charge). The misspelling "reviewdBy" is the wire name.
[].namestringNoPII when entityType is FARMER (farmer name).
[].districtName, unitName, mandalCode, mandalName, voCode, voName, shgCode, shgNamestringNoGeography and group context shown in lists.
[].endorsementTypestringNoVO, SHG or FARMER tab type.
[].hasNpmShopbooleanNoNPM shop flag on VO endorsements.
[].abstractFilestring|nullNoLocal file path of the abstract, also serialised. The server is not known to read it.
[].timestamplongNoEndorsement time (epoch ms).
[].createdAtlongNoMapped to created_at. FetchEndorsementDataWorker drops rows older than ref-data endorsementStartDate.

Response

Example response body (synthetic data)
[
  3101
]
FieldTypeRequiredNotes
[0]intQueued path: rawResponse has "[" and "]" stripped and is parsed as the new endorsement id (Data.repository.updateEndorsementId). The online path declares Call<Any> and ignores the body.

Errors

StatusWhenWhat the app does
401Session expiredCoreAPI invalidates the session id and retries once. If it still fails, the response handler returns TryAgain.LATER and the operation stays in the queue.
409Duplicate X-REQUESTIDSyncQueueProcessor records the request id and swaps in a fresh X-REQUESTID for the next attempt.
4xx/5xx (other)Server rejects or failsonError returns TryAgain.LATER (EndorsementPostRH). Each failure is logged in the local error_log table (URL, body, headers). Every 10th attempt (up to attempt 5000) the whole operation is copied to Firestore urvi_sync_log. A 500 on a gateway URL in the first 5 attempts forces a session refresh.
non-2xx (online bulk)Bulk save failsLocal rows are not updated. No success or error is posted unless an exception is thrown (bug).

Source

  • app/src/main/java/org/platformcommons/urvi/datasource/network/retrofit/APIInterface.kt
  • app/src/main/java/org/platformcommons/urvi/datasource/repository/impl/EndorsementRepositoryImpl.kt
  • app/src/main/java/org/platformcommons/urvi/view/endorsements/vo/EndorsementViewModel.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/entity/EndorsementEntity.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/work/EndorsementWorker.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/sync/rh/EndorsementPostRH.kt
  • data/src/main/java/org/mindtree/app/agricultor/data/fcm/FcmUtil.kt
  • offline-core/src/main/java/com/mindtree/bridge/coreapi/offline/SyncQueueProcessor.java