Skip to content

RI-8267 Complete error-code coverage for i18n#6120

Open
valkirilov wants to merge 8 commits into
mainfrom
be/RI-8267/error-code-coverage
Open

RI-8267 Complete error-code coverage for i18n#6120
valkirilov wants to merge 8 commits into
mainfrom
be/RI-8267/error-code-coverage

Conversation

@valkirilov

@valkirilov valkirilov commented Jun 26, 2026

Copy link
Copy Markdown
Member

What

Give every user-facing API error a stable errorCode (and structured resource data where it already exists) so the client can translate error messages on its own. The backend localizes nothing — it just exposes a code clients can map to a translated string.

Strictly additive & backwards-compatible:

  • only new/optional response fields are added
  • message (English), statusCode, and error/name are untouched.

Note: This API is also consumed by the Redis-for-VS-Code plugin, so not changing the response shape is a hard requirement.

Why

Pre-PR, ~58/78 custom exceptions carried an errorCode, but nothing was translatable for:

  • ~27 codeless custom exceptions (SSH, encryption, database-import, RDI, misc),
  • the ~149 built-in Nest exceptions (NotFoundException, BadRequestException, …),
  • class-validator validation failures.

This PR closes those gaps so the upcoming UI work can map any error to a localized message, with the English message always available as a fallback. No nestjs-i18n / Accept-Language — translation stays client-side.

Commit breakdown (by change type)

1. Specific codes for codeless custom exceptions — add an errorCode from CustomErrorCodes to each, one line per file, response shape otherwise unchanged. Grouped per domain:

2. Generic chokepointfeat(api): inject generic + validation error codes via global filter. GlobalExceptionFilter stamps a code onto any HttpException still missing one: ValidationError when message is an array (class-validator), otherwise a status-based Generic* code. Covers all built-in Nest throws + validation in one place, without editing ~149 throw sites. Custom-coded exceptions are left untouched.

3. Docsdocs(api): bruno error-code examples. A Bruno "Error Codes" folder with runnable requests showing each case.

New code-ranges in custom-error-codes.ts:

  • SSH 116xx
  • Database Import 117xx
  • Encryption 118xx
  • Misc 119xx
  • Generic 124xx/125xx (12_000 + HTTP status)
  • Validation 12100

Response shapes (with these changes)

Custom exception (now coded) — SSH:

{ 
    "message": "Unable to create ssh connection. ...", 
    "name": "UnableToCreateSshConnectionException", 
    "statusCode": 503, 
    "errorCode": 11600 
}

Generic built-in exception — 404 on an unknown id:

{ 
    "message": "Invalid database instance id.", 
    "statusCode": 404, 
    "error": "Not Found", 
    "errorCode": 12404
    }

Validation failure (note message stays an array of field errors):

{ 
     "message": ["port must be a number conforming to the specified constraints"], 
     "statusCode": 400, 
     "error": "Bad Request", 
     "errorCode": 12100 
 }

Exception carrying structured context — DatabaseAlreadyExists:

{ 
    "message": "The database already exists.", 
    "statusCode": 409, 
    "error": "DatabaseAlreadyExists", 
    "errorCode": 11200, 
    "resource": { 
        "databaseId": "a1b2c3d4-..." 
    } 
}

How we'll use this (next PR, UI — pseudo-code)

A small errorCode → i18n key map plus a translate helper. resource supplies interpolation vars.

Note: the English message is the fallback when a code isn't mapped yet.

// errorCodeMap.ts
const ERROR_CODE_TO_KEY: Record<number, string> = {
  11200: 'errors.database.alreadyExists',
  11600: 'errors.ssh.unableToConnect',
  12404: 'errors.notFound',
  12100: 'errors.validation',
}

// translateApiError.ts
export function translateApiError(err: ApiErrorResponse, t: TFunction): string {
  const key = err.errorCode ? ERROR_CODE_TO_KEY[err.errorCode] : undefined
  if (key) return t(key, err.resource)            // resource fills {{vars}} in the template
  return Array.isArray(err.message)               // fallback: keep the English message
    ? err.message.join(', ')
    : err.message
}
// locales/en.json                       // locales/bg.json
{ "errors.database.alreadyExists":       { "errors.database.alreadyExists":
    "The database already exists." }          "Базата данни вече съществува." }
// usage at an API error boundary / toast
catch (e) {
  showError(translateApiError(e.response.data, t))
}

So this PR is the data contract; the next PR adds the map + locale strings and wires translateApiError into the UI's error surfaces.

Testing

  • yarn lint:api, yarn --cwd redisinsight/api type-check (baseline) — clean.
  • yarn test:api — green; updated the 3 specs that asserted full getResponse() shapes, added a GlobalExceptionFilter spec (status→code mapping, validation array → ValidationError, doesn't overwrite existing codes, non-HttpException passthrough, static-asset branch).
  • Manual: the Bruno Error Codes folder (POST /databases/test, GET /databases/:id) reproduces each case live against yarn dev:api.

Ref: RI-8267

Related community requests: #605, #1317, #1883, #2040, #2137, #2181, #2213, #2417, #2550, #2590, #2695, #2715, #2716, #2727, #2828, #2912, #2943, #2945, #3081, #3141, #3142, #3319, #3411, #3601, #3625, #3628, #3663, #3671, #3812, #3815, #4150, #4182, #4408, #4664, #4672, #4744, #5052, #5967


Note

Medium Risk
Broad change to global error serialization affects every API error path (including encryption/SSH), but behavior is additive and existing fields are preserved; regression risk is mainly wrong or missing codes on edge cases.

Overview
Adds an optional errorCode (and keeps existing resource where present) on API error responses so clients can translate errors without backend localization. message, statusCode, and error are unchanged.

Central stamping: New stampErrorCode runs in GlobalExceptionFilter for every HttpException missing a code—12100 when message is a class-validator array, otherwise 12_000 + HTTP status (e.g. 404 → 12404). Existing custom codes are not overwritten. The body-parser 413 path uses the same helper.

Domain exceptions: SSH, encryption, database-import, RDI (incl. RdiTimeout), remote-config, and redis client-not-found responses now set explicit CustomErrorCodes. ValidationException embeds ValidationError in its body.

Docs/tests: Bruno Error Codes requests document live shapes; new/updated specs cover the filter, body-parser, and RDI timeout/internal-error responses.

Reviewed by Cursor Bugbot for commit b04f7b1. Bugbot is set up for automated code reviews on this repo. Configure here.

@valkirilov valkirilov requested a review from a team as a code owner June 26, 2026 12:26
@jit-ci

jit-ci Bot commented Jun 26, 2026

Copy link
Copy Markdown

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d4b15b79f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread redisinsight/api/src/exceptions/global-exception.filter.ts Outdated
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Code Coverage - Integration Tests

Status Category Percentage Covered / Total
🟡 Statements 79.12% 18070/22838
🟡 Branches 61.88% 8375/13534
🟡 Functions 66.86% 2448/3661
🟡 Lines 78.73% 17008/21602

@valkirilov valkirilov force-pushed the be/RI-8267/error-code-coverage branch from 6d4b15b to 30f1209 Compare June 26, 2026 12:42
@valkirilov valkirilov changed the title RI-8267 Backend: complete error-code coverage for i18n RI-8267 Complete error-code coverage for i18n Jun 26, 2026
@valkirilov valkirilov force-pushed the be/RI-8267/error-code-coverage branch from 30f1209 to b9ec404 Compare June 26, 2026 12:56
@valkirilov valkirilov self-assigned this Jun 26, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b9ec404ce7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread redisinsight/api/src/exceptions/global-exception.filter.ts Outdated
valkirilov and others added 7 commits June 29, 2026 11:29
Assign CustomErrorCodes to the SSH connection/tunnel exceptions so clients
can translate them. Additive — message/name/statusCode unchanged.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assign CustomErrorCodes to the encryption/keytar exceptions (incl. the
base) so clients can translate them. Additive only.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assign CustomErrorCodes to the database-import validation exceptions
(invalid certs/keys/compressor, file parsing, etc.). Additive only.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add CustomErrorCodes.RdiTimeout and assign codes to the RDI internal-server
and timeout exceptions; update their specs. Additive only.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assign CustomErrorCodes to the remaining codeless exceptions: feature
remote-config fetch and redis client-not-found. Additive only.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(RI-8267)

GlobalExceptionFilter stamps an errorCode onto any HttpException lacking
one: ValidationError for class-validator (array message) failures, else a
status-based Generic* code. Covers built-in Nest exceptions across the app
without touching throw sites. Custom-coded exceptions are left untouched;
response shape is otherwise unchanged.

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an "Error Codes" Bruno folder demonstrating errorCode/resource in
responses: connection failed (custom code), not found (generic code),
validation error, and already-exists (resource).

Ref: RI-8267

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@valkirilov valkirilov force-pushed the be/RI-8267/error-code-coverage branch from 8ac1d48 to 8a78ebd Compare June 29, 2026 08:29

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a78ebd7dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread redisinsight/api/src/exceptions/global-exception.filter.ts
…RI-8267)

The body-parser error middleware writes the 413 entity.too.large response
directly, bypassing GlobalExceptionFilter. Extract the stamping into a shared
stampErrorCode helper and use it there too, so the payload-too-large error
carries an errorCode like every other user-facing API error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant