Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AllianceBioversityCIAT/alliance-research-indicators-client/llms.txt

Use this file to discover all available pages before exploring further.

Every HTTP call in the Alliance Research Indicators client goes through ApiService or a domain service that delegates to it. Components never call HttpClient directly. This page explains the service URL conventions, the MainResponse<T> contract that every response conforms to, the representative endpoints exposed by the Main API, and how to handle errors correctly.
Never inject HttpClient into a component. All HTTP access must go through ApiService (located at src/app/shared/services/api.service.ts) or a domain-specific service that delegates to it. This rule ensures the interceptor chain runs consistently and keeps HTTP wiring out of presentation components.

The three backend services

Environment variables in src/environments/environment*.ts supply the base URL for each service. The jWtInterceptor inspects outgoing request URLs to determine which authentication header format to use.
VariableServiceWhen to use it
mainApiUrlMain API (NestJS)All core business logic — results CRUD, indicators, users, CLARISA controlled-list proxying, dashboards, authorization, and projects
textMiningUrlText-Mining microserviceAI/NLP auto-fill and content extraction for result form fields
fileManagerUrlFile Manager microserviceMultipart evidence file uploads; the service returns a persistent URL that is then stored on the result record
The Main API and File Manager accept Authorization: Bearer <token> headers. The Text-Mining service expects the token in an access-token header and, for multipart requests, also embedded in the request FormData as a token field. The jWtInterceptor handles these differences automatically.

The MainResponse<T> envelope

Every response from the Main API is wrapped in a standard envelope. The TypeScript interface is defined in src/app/shared/interfaces/responses.interface.ts:
export interface MainResponse<T> {
  data: T;
  status: number;
  description: string;
  timestamp: string;
  path: string;
  successfulRequest: boolean;
  errorDetail: ErrorResponse;
}

export interface ErrorResponse {
  errors: string;
  detail: string;
  description: string;
}
successfulRequest is the primary signal of whether the request succeeded from the application’s point of view — it can be false even when the HTTP status code is 200 (for example, when the server reports a business-logic failure). Always check this field before consuming data.

Reading a response

const response = await this.api.GET_GeneralInformation(resultId);

if (!response.successfulRequest) {
  // Pass to ActionsService so the user sees a toast
  this.actions.showToast({
    detail: response.errorDetail?.description,
    severity: 'error',
    summary: 'Could not load result'
  });
  return;
}

// Safe to use response.data here
this.generalInfo.set(response.data);

ApiService call patterns

ApiService is provided in root and injected wherever HTTP calls are needed. Method naming follows a consistent convention:
PrefixHTTP verbExample
GET_GETGET_GeneralInformation(id)
POST_POSTPOST_Result(body)
PATCH_PATCHPATCH_GeneralInformation(id, body)
DELETE_DELETEDELETE_Result(resultCode)
All methods return Promise<MainResponse<T>>. They delegate to ToPromiseService (src/app/shared/services/to-promise.service.ts), which wraps the HttpClient observable into a promise and applies request options (loading triggers, result interceptor opt-in, etc.).
// Domain service extending ApiService (or delegating to it)
async loadGeneralInfo(id: number) {
  const response = await this.api.GET_GeneralInformation(id);
  if (response.successfulRequest) {
    this.generalInfo.set(response.data);
  }
}

// Creating a result
const response = await this.api.POST_Result({ title, indicator_id });
if (response.successfulRequest) {
  this.router.navigate(['/result', response.data.result_code]);
}

Representative endpoints

The following endpoints are consumed by ApiService and reflect the API surface documented in the Detailed Design §4.3.
MethodPathDescription
POST/authorization/loginExchange an AWS Cognito token for an application JWT
POST/authorization/refresh-tokenRefresh an expired access token
GET/authorization/users/currentFetch the currently authenticated user’s profile
GET/authorization/view/scomponentsRetrieve view-component visibility rules for the current user’s role
MethodPathDescription
GET/indicator-typesAll indicator type definitions
GET/indicator-types/:idSingle indicator type
GET/indicatorsFull indicator catalog
GET/indicators/:idSingle indicator
GET/maturity-levelsMaturity level reference data
GET/indicators/results-amount/current-userResult count per indicator for the current user
MethodPathDescription
GET/tools/clarisa/institutionsInstitution list (with location and type)
GET/tools/clarisa/institutions-typesInstitution type hierarchy
GET/tools/clarisa/sdgsSustainable Development Goals
GET/tools/clarisa/leversAlliance strategic levers
GET/tools/clarisa/sdg-targetsSDG target reference data
GET/tools/clarisa/impact-areasCGIAR impact areas
GET/tools/clarisa/countriesCountry list (optional is-sub-national filter)
GET/tools/clarisa/languagesLanguage list
GET/tools/clarisa/regionsRegion list
GET/tools/clarisa/sub-nationals/country/:isoAlpha2Sub-national units by country
GET/tools/clarisa/innovation-typesInnovation type reference data
GET/tools/clarisa/innovation-readiness-levelsInnovation readiness level reference data
MethodPathDescription
GET/v2/resultsPaginated result list (supports filtering, sorting, search)
POST/resultsCreate a new result
GET/results/:id/general-informationLoad general-information tab
PATCH/results/:id/general-informationSave general-information tab
GET/results/:id/geo-locationLoad geographic scope tab
PATCH/results/:id/geo-locationSave geographic scope tab
GET/results/:id/alignmentsLoad alliance-alignment tab
PATCH/results/:id/alignmentsSave alliance-alignment tab
GET/results/evidences/principal/:idLoad evidence tab
PATCH/results/evidences/by-result-id/:idSave evidence tab
DELETE/results/:code/deleteDelete a result
GET/results/validate-titleCheck whether a result title is unique
GET/results/versions/:resultCodeRetrieve available versions of a result
MethodPathDescription
POST/results/status/workflow/change-status/:code/to-status/:statusSubmit or transition a result through the MEL workflow
GET/results/status/:idCurrent status of a result
GET/results/status/review-statusesAll review status options
GET/results/status/workflow/result/:code/next-stepNext permitted workflow step for a result
GET/results/green-checks/:resultCodeGreen-check completion status for all tabs
MethodPathDescription
GET/results/status/result-amount/current-userResults grouped by status for the current user
GET/results/last-updated/current-userMost recently updated results for the current user
GET/agresso/contracts/results/current-userContracts (projects) linked to the current user
GET/agresso/contracts/find-contractsSearch contracts with optional filters
GET/agresso/contracts/:id/results/countResult count for a specific contract
GET/results/contracts/:contractIdAll results linked to a contract
GET/results/general-report/allFull general report dataset

Error handling

successfulRequest === false

Never swallow a failed response. Pass it through to ActionsService so the user receives a human-readable toast or alert.
const response = await this.api.PATCH_GeneralInformation(id, body);
if (!response.successfulRequest) {
  this.actions.showToast({
    detail: response.errorDetail?.description ?? 'An unexpected error occurred',
    severity: 'error',
    summary: 'Save failed'
  });
  return;
}

401 — token expiry

Handled automatically by jWtInterceptor. The interceptor attempts a token refresh and retries the original request once. If the refresh also fails, the user is logged out and redirected to /login. Components do not need to handle 401 explicitly.

409 — conflict / duplicate

A 409 response means the server detected a duplicate (for example, a result with the same platform_code + official_code already exists). Surface the structured “link to existing” prompt — do not retry the request blindly. The httpErrorInterceptor intentionally suppresses the automatic error toast for 409s, leaving the component to handle the flow.
const response = await this.api.POST_Result(body);
if (response.status === 409) {
  // Open the link-to-existing modal with the conflicting record reference
  this.modals.openLinkToExisting(response.errorDetail);
  return;
}

Validation errors

When the server returns field-level validation failures, they arrive in errorDetail.errors. Render these inline next to the offending form field rather than showing a generic toast.
if (!response.successfulRequest && response.errorDetail?.errors) {
  // Map errorDetail.errors to inline form field error messages
  this.validationErrors.set(response.errorDetail.errors);
}

Error tracking

The httpErrorInterceptor automatically sends all HTTP errors to the remote error-tracking endpoint configured in environment.saveErrorsUrl. It also starts a 5-second timer on every request — if no response arrives, a “pending” (slow-request) event is logged. No additional error reporting is needed in individual services or components for standard HTTP failures.

Build docs developers (and LLMs) love