Skip to content

Conversation

@stepan662
Copy link
Contributor

@stepan662 stepan662 commented Aug 6, 2025

Summary by CodeRabbit

  • New Features

    • Push: new CLI options and schema settings to control override mode and behavior on unresolved conflicts.
    • Better conflict feedback: unresolved conflicts rendered and printed with colorized, detailed messages.
    • Exports: HTML escaping disabled for pull/backup exports.
    • Sync output: key printing supports optional colors and notes.
    • Errors: fuller stack traces returned.
  • Chores

    • Renamed env key: tolgee.rateLimits.enabled → tolgee.rate-limits.enabled.
  • Tests

    • Added e2e tests covering override modes and unresolved-conflict behavior.

@stepan662 stepan662 marked this pull request as draft August 6, 2025 15:07
@coderabbitai
Copy link

coderabbitai bot commented Aug 6, 2025

Walkthrough

Adds two push configuration options (overrideMode, errorOnUnresolvedConflict) with schema/types and CLI wiring; surfaces unresolved import conflicts via new printing utilities and appends conflict messages to loadable errors; disables HTML escaping in export calls; expands sync printing API; updates e2e fixtures, import API usage, and minor env/util changes.

Changes

Cohort / File(s) Summary
Schema & Types
schema.json, src/schema.d.ts
Added push.overrideMode and push.errorOnUnresolvedConflict ($defs/types/enums ALL
Push command & conflict handling
src/commands/push.ts, src/utils/printFailedKeys.ts, src/client/errorFromLoadable.ts
New CLI options --override-mode and --error-on-unresolved-conflict; map CLI values to import params; added printUnresolvedConflicts and printing/rendering utilities; append unresolved-conflict message to loadable errors.
Export/backup options
src/commands/pull.ts, src/commands/sync/sync.ts, src/client/TolgeeClient.js
Pass escapeHtml: false to export calls (pull/backup); export options interface extended to include escapeHtml.
Sync printing util
src/commands/sync/syncUtils.ts
printKey signature expanded to (key, deletion?, color?, note?); output logic updated to support optional color, note, and three-state deletion handling.
Utilities
src/utils/getStackTrace.ts, src/utils/printFailedKeys.ts
getStackTrace made environment-aware and returns full stack; new printFailedKeys.ts added with renderKey, getUnresolvedConflictsMessage, printUnresolvedConflicts.
Environment config
scripts/tolgee.env
Renamed tolgee.rateLimits.enabledtolgee.rate-limits.enabled.
E2E tests: override behavior
test/e2e/push.override.test.ts
New e2e tests exercising override modes and error-on-unresolved-conflict behavior against reviewed/protected translations.
E2E API helpers & fixtures migration
test/e2e/utils/api/common.ts, .../fullLanguageNamesProject.ts, .../nestedArrayKeysProject.ts, .../nestedKeysProject.ts, .../project1.ts, .../project2.ts, .../project3.ts
Migrated fixtures and helpers to SingleStepImportResolvableRequest shape, removed per-language resolution fields, updated createProject flow and endpoint; expanded options type.
E2E data util
test/e2e/utils/data.ts
Inner translation building now filters out locales with falsy/missing text before mapping.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as push.ts (CLI)
  participant Schema as Schema/Options
  participant API as Server API
  participant Util as printFailedKeys
  participant Loader as errorFromLoadable

  CLI->>Schema: Parse --override-mode, --error-on-unresolved-conflict
  Note right of CLI: Map errorOnUnresolvedConflict<br/>auto→undefined, yes→true, no→false
  CLI->>API: importData({ overrideMode, errorOnUnresolvedConflict, ... })
  API-->>CLI: Import result (may include unresolved conflicts)
  alt Unresolved conflicts present
    CLI->>Util: printUnresolvedConflicts(translations, isError)
    Util-->>CLI: Message printed
    CLI->>Loader: Append unresolved conflict message to loadable errors
    alt errorOnUnresolvedConflict = yes
      CLI-->>CLI: Exit 1
    else
      CLI-->>CLI: Exit 0
    end
  else
    CLI-->>CLI: Exit 0
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • JanCizmar

Poem

I hop through keys both old and new,
I mark the conflicts in golden hue.
ALL or RECOMMENDED, choices bloom,
yes/no/auto decide the room.
I print the puzzles, then I chew. 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 475f168 and 2c13b88.

📒 Files selected for processing (2)
  • schema.json (2 hunks)
  • src/schema.d.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/schema.d.ts
  • schema.json
⏰ 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). (3)
  • GitHub Check: E2E Tests (ubuntu-latest, 20)
  • GitHub Check: E2E Tests (ubuntu-latest, 18)
  • GitHub Check: E2E Tests (ubuntu-latest, 22)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch stepangranat/use-new-import-endpoint

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@stepan662 stepan662 marked this pull request as ready for review August 13, 2025 08:53
@stepan662 stepan662 requested a review from JanCizmar August 13, 2025 08:53
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

🧹 Nitpick comments (17)
test/e2e/utils/data.ts (1)

8-10: Filtering drops intentionally empty translations; prefer null/undefined check instead

Boolean(data.text) removes empty strings, which might be valid test inputs. Consider filtering only null/undefined values to avoid unintended data loss.

Apply:

-          Object.entries(k.translations)
-            .filter(([_, data]: any) => Boolean(data.text))
+          Object.entries(k.translations ?? {})
+            .filter(([_, data]: any) => data?.text != null)
             .map(([locale, data]: any) => [locale, data.text])

If the intent is to drop empty strings, keep current logic; otherwise, this change preserves empty-but-valid values.

src/schema.d.ts (1)

147-149: Clarify scope wording for removeOtherKeys

Minor phrasing nit: “within imported namespaces” could be misread. Suggest “within the namespaces included in this import”.

Because this file is generated, update the wording in schema.json and re-generate.

schema.json (2)

93-99: New push options wired correctly; consider adding brief inline descriptions

The new push properties are correctly referenced via $defs and align with the rest of the schema. For discoverability, consider adding short descriptions here (in addition to the $defs descriptions), similar to other properties in this object.

Example:

-        "errorOnUnresolvedConflict": {
+        "errorOnUnresolvedConflict": {
+          "description": "Fail the import when unresolved conflicts remain (yes/no/auto).",
           "$ref": "#/$defs/errorOnUnresolvedConflict"
         },
-        "overrideMode": {
+        "overrideMode": {
+          "description": "Controls which existing translations are protected from being overridden (RECOMMENDED/ALL).",
           "$ref": "#/$defs/overrideMode"
         }

260-269: Clarify overrideMode semantics and fix minor grammar

As written, the overrideMode description can be misread (especially “ALL”). Given the CLI hint (“Overridable translations can be updated with --override-mode ALL”), it sounds like ALL should allow overriding more translations, not fewer. Also, polish the errorOnUnresolvedConflict description grammar.

-    "overrideMode": {
-      "description": "Specifies what is considered non-overridable translation: 
- - RECOMMENDED - protected reviewed translations are considered as non-overridable
- - ALL - translations that user has permissions for",
+    "overrideMode": {
+      "description": "Controls which existing translations are protected from being overridden:
+ - RECOMMENDED — reviewed (protected) translations are treated as non‑overridable
+ - ALL — all translations you have permissions for are overridable",
       "type": "string",
       "enum": ["ALL", "RECOMMENDED"]
     },
     "errorOnUnresolvedConflict": {
-      "description": "Fail the whole import if there are unresolved conflicts in import.",
+      "description": "Fail the import if there are unresolved conflicts in the import.",
       "type": "string",
       "enum": ["yes", "no", "auto"]
     },
src/utils/printFailedKeys.ts (4)

3-3: Mark PartialKey import as type-only to avoid unnecessary runtime import

PartialKey is used for typing only. Importing it as a type avoids generating a runtime import and potential side effects.

-import { PartialKey } from '../commands/sync/syncUtils.js';
+import type { PartialKey } from '../commands/sync/syncUtils.js';

17-21: Leverage isError and simplify overridable detection

  • isError is unused. Use it to adjust the heading style (e.g., red icon/wording when used in an error context).
  • Prefer Array.prototype.some over find+Boolean for intent and efficiency.
-export function getUnresolvedConflictsMessage(
+export function getUnresolvedConflictsMessage(
   translations: SimpleImportConflictResult[],
   isError: boolean
 ) {
-  const someOverridable = Boolean(translations.find((c) => c.isOverridable));
-  const result = [''];
+  const someOverridable = translations.some((c) => c.isOverridable);
+  const result: string[] = [];
 
-  result.push(`🟡 Some translations cannot be updated:`);
+  result.push(`${isError ? '🔴' : '🟡'} Some translations cannot be updated:`);

22-41: Avoid leading/trailing blank lines in the message

Currently the message begins and ends with a blank line (result initialized with '', and an empty push at the end). This produces double leading newlines in errorFromLoadable and an extra trailing newline in logs. Let the caller manage spacing.

-  const result = [''];
-
-  result.push(`🟡 Some translations cannot be updated:`);
+  const result: string[] = [];
+  result.push(`🟡 Some translations cannot be updated:`);
   translations.forEach((c) => {
     result.push(
       renderKey(
         { keyName: c.keyName, namespace: c.keyNamespace },
         `${c.language}` + (c.isOverridable ? ' (overridable)' : '')
       )
     );
   });
-  result.push('');
   if (someOverridable) {
     result.push(
       'HINT: Overridable translations can be updated with the `--override-mode ALL`'
     );
-    result.push('');
   }
   return result.join('\n');

If you adopt this change, also trim leading whitespace when appending the message in errorFromLoadable to avoid double newlines (see my comment there).


43-48: Consider directing error output to stderr when isError is true

To better integrate with shells/CI, write to stderr for error contexts.

-export function printUnresolvedConflicts(
+export function printUnresolvedConflicts(
   translations: SimpleImportConflictResult[],
   isError: boolean
 ) {
-  console.log(getUnresolvedConflictsMessage(translations, isError));
+  const msg = getUnresolvedConflictsMessage(translations, isError);
+  (isError ? console.error : console.log)(msg);
 }
src/client/errorFromLoadable.ts (1)

15-23: Harden shape check for params and avoid double-leading newlines

  • Use Array.isArray to verify params shape.
  • Trim leading/trailing newlines from the appended message to avoid double blank lines (since addErrorDetails also adds a newline).
-  if (
-    loadable.error?.code === 'conflict_is_not_resolved' &&
-    typeof loadable.error.params?.[0] === 'object'
-  ) {
-    additionalInfo += getUnresolvedConflictsMessage(
-      loadable.error.params as any,
-      true
-    );
-  }
+  if (
+    loadable.error?.code === 'conflict_is_not_resolved' &&
+    Array.isArray(loadable.error.params) &&
+    typeof loadable.error.params[0] === 'object'
+  ) {
+    const msg = getUnresolvedConflictsMessage(
+      loadable.error.params as any,
+      true
+    ).replace(/^\n+|\n+$/g, '');
+    additionalInfo += msg;
+  }
src/commands/sync/syncUtils.ts (1)

29-34: Update JSDoc to reflect new parameters

The function now accepts optional color and note parameters and supports a neutral mode when deletion is undefined. Please update the comment to prevent confusion.

-/**
- * Prints information about a key, with coloring and formatting.
- *
- * @param key The key to print.
- * @param deletion True if the key is about to be deleted.
- */
+/**
+ * Prints information about a key, with coloring and formatting.
+ *
+ * @param key The key to print.
+ * @param deletion True if the key is about to be deleted; false if added; undefined for neutral print (no +/- prefix).
+ * @param color Optional color function overriding the default (green for add/neutral, red for delete).
+ * @param note Optional note appended after the namespace.
+ */
test/e2e/utils/api/common.ts (1)

68-81: Optional: avoid unnecessary PUT when no overrides supplied

If options is undefined (or contains no overrides beyond defaults), you can skip the update call to reduce noise and speed up tests.

test/e2e/utils/api/project3.ts (1)

18-54: Use satisfies at the top level for consistency and better inference

Mirror the item-level approach so you keep literal types and catch shape issues at compile time without over-widening.

-export const PROJECT_3: components['schemas']['SingleStepImportResolvableRequest'] =
-  {
+export const PROJECT_3 =
+  {
     keys: [
       ...keys.map(
         (name) =>
           ({
             name,
             translations: {
               en: { text: en[name] },
               fr: { text: fr[name] },
             },
           }) satisfies SingleStepImportResolvableItemRequest
       ),
       ...keysDrinks.map(
         (name) =>
           ({
             name,
             namespace: 'drinks',
             translations: {
               en: { text: enDrinks[name] },
               fr: { text: frDrinks[name] },
             },
           }) satisfies SingleStepImportResolvableItemRequest
       ),
       ...keysFood.map(
         (name) =>
           ({
             name,
             namespace: 'food',
             translations: {
               en: { text: enFood[name] },
               fr: { text: frFood[name] },
             },
           }) satisfies SingleStepImportResolvableItemRequest
       ),
     ],
-  };
+  } satisfies components['schemas']['SingleStepImportResolvableRequest'];
test/e2e/utils/api/project2.ts (1)

6-15: Prefer satisfies for top-level PROJECT_2 to preserve literals and enforce schema

Align with project3’s pattern to reduce accidental widening and catch mistakes early.

-export const PROJECT_2: components['schemas']['SingleStepImportResolvableRequest'] =
-  {
+export const PROJECT_2 =
+  {
     keys: keys.map((name) => ({
       name,
       translations: {
         en: { text: en[name] },
         fr: { text: fr[name] },
       },
     })),
-  };
+  } satisfies components['schemas']['SingleStepImportResolvableRequest'];
test/e2e/utils/api/fullLanguageNamesProject.ts (1)

6-15: Use satisfies for consistency and better type inference

This mirrors the recommendation for other fixtures.

-export const FULL_LANGUAGE_NAMES_PROJECT: components['schemas']['SingleStepImportResolvableRequest'] =
-  {
+export const FULL_LANGUAGE_NAMES_PROJECT =
+  {
     keys: keys.map((name) => ({
       name,
       translations: {
         'en-GB': { text: en[name] },
         'fr-FR': { text: fr[name] },
       },
     })),
-  };
+  } satisfies components['schemas']['SingleStepImportResolvableRequest'];
test/e2e/utils/api/project1.ts (1)

6-15: Prefer satisfies for top-level PROJECT_1 to preserve literals and enforce schema

Brings it in line with other fixtures and avoids over-widening.

-export const PROJECT_1: components['schemas']['SingleStepImportResolvableRequest'] =
-  {
+export const PROJECT_1 =
+  {
     keys: keys.map((name) => ({
       name,
       translations: {
         en: { text: en[name] },
         fr: { text: fr[name] },
       },
     })),
-  };
+  } satisfies components['schemas']['SingleStepImportResolvableRequest'];
test/e2e/push.override.test.ts (1)

126-158: Consider extracting magic strings as constants.

The prepareData function works correctly, but the hardcoded values like 'controller', 'Controller (old)', and 'REVIEWED' could be extracted as constants at the top of the test file for better maintainability.

+const TEST_KEY = 'controller';
+const OLD_TRANSLATION = 'Controller (old)';
+const UPDATED_TRANSLATION = 'Controller';
+const REVIEWED_STATE = 'REVIEWED';
+
 describe('project 1', () => {
   // ... existing code ...
   
   async function prepareData() {
     const keys = await client.GET('/v2/projects/{projectId}/translations', {
       params: {
         path: { projectId: client.getProjectId() },
-        query: { filterKeyName: ['controller'] },
+        query: { filterKeyName: [TEST_KEY] },
       },
     });

     const key = keys.data?._embedded?.keys?.[0];

     await client.PUT('/v2/projects/{projectId}/translations', {
       params: { path: { projectId: client.getProjectId() } },
       body: {
-        key: 'controller',
+        key: TEST_KEY,
         translations: {
-          en: 'Controller (old)',
+          en: OLD_TRANSLATION,
         },
       },
     });

     await client.PUT(
       '/v2/projects/{projectId}/translations/{translationId}/set-state/{state}',
       {
         params: {
           path: {
             projectId: client.getProjectId(),
             translationId: key!.translations!['en']!.id!,
-            state: 'REVIEWED',
+            state: REVIEWED_STATE,
           },
         },
       }
     );
   }
src/commands/push.ts (1)

205-216: Consider simplifying the switch statement.

The switch statement correctly maps the CLI option to a boolean/undefined, but it could be more concise using an object lookup.

-    let errorOnUnresolvedConflict: boolean | undefined;
-    switch (opts.errorOnUnresolvedConflict) {
-      case 'auto':
-        errorOnUnresolvedConflict = undefined;
-        break;
-      case 'yes':
-        errorOnUnresolvedConflict = true;
-        break;
-      case 'no':
-        errorOnUnresolvedConflict = false;
-        break;
-    }
+    const errorOnUnresolvedConflictMap = {
+      auto: undefined,
+      yes: true,
+      no: false,
+    } as const;
+    const errorOnUnresolvedConflict = errorOnUnresolvedConflictMap[opts.errorOnUnresolvedConflict ?? 'auto'];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9f46d and fcc4260.

📒 Files selected for processing (19)
  • schema.json (2 hunks)
  • scripts/tolgee.env (1 hunks)
  • src/client/errorFromLoadable.ts (2 hunks)
  • src/commands/pull.ts (1 hunks)
  • src/commands/push.ts (5 hunks)
  • src/commands/sync/sync.ts (1 hunks)
  • src/commands/sync/syncUtils.ts (1 hunks)
  • src/schema.d.ts (3 hunks)
  • src/utils/getStackTrace.ts (1 hunks)
  • src/utils/printFailedKeys.ts (1 hunks)
  • test/e2e/push.override.test.ts (1 hunks)
  • test/e2e/utils/api/common.ts (2 hunks)
  • test/e2e/utils/api/fullLanguageNamesProject.ts (1 hunks)
  • test/e2e/utils/api/nestedArrayKeysProject.ts (1 hunks)
  • test/e2e/utils/api/nestedKeysProject.ts (1 hunks)
  • test/e2e/utils/api/project1.ts (1 hunks)
  • test/e2e/utils/api/project2.ts (1 hunks)
  • test/e2e/utils/api/project3.ts (1 hunks)
  • test/e2e/utils/data.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (8)
src/client/errorFromLoadable.ts (2)
src/client/TolgeeClient.ts (1)
  • LoadableData (7-9)
src/utils/printFailedKeys.ts (1)
  • getUnresolvedConflictsMessage (17-41)
test/e2e/push.override.test.ts (4)
src/client/TolgeeClient.ts (1)
  • TolgeeClient (29-29)
test/e2e/utils/api/common.ts (3)
  • createProjectWithClient (48-89)
  • createPak (103-109)
  • deleteProject (38-42)
test/e2e/utils/api/project1.ts (1)
  • PROJECT_1 (6-15)
test/e2e/utils/run.ts (1)
  • run (98-105)
src/utils/printFailedKeys.ts (2)
src/commands/sync/syncUtils.ts (1)
  • PartialKey (16-16)
test/e2e/__internal__/preload.cjs (1)
  • ansi (6-6)
test/e2e/utils/api/project1.ts (1)
src/client/internal/schema.generated.ts (1)
  • components (5102-9090)
test/e2e/utils/api/fullLanguageNamesProject.ts (1)
src/client/internal/schema.generated.ts (1)
  • components (5102-9090)
src/commands/sync/syncUtils.ts (1)
test/e2e/__internal__/preload.cjs (1)
  • ansi (6-6)
test/e2e/utils/api/project3.ts (1)
src/client/internal/schema.generated.ts (1)
  • components (5102-9090)
src/commands/push.ts (4)
src/schema.d.ts (2)
  • OverrideMode (73-73)
  • ErrorOnUnresolvedConflict (67-67)
src/client/ImportClient.ts (1)
  • ImportProps (11-13)
src/client/TolgeeClient.ts (1)
  • handleLoadableError (23-27)
src/utils/printFailedKeys.ts (1)
  • printUnresolvedConflicts (43-48)
🔇 Additional comments (19)
scripts/tolgee.env (1)

6-6: All references updated: no remaining tolgee.rateLimits.enabled occurrences
A quick ripgrep check confirms only the new kebab-case key appears:

• scripts/tolgee.env:6 → tolgee.rate-limits.enabled=false

No other files reference the old tolgee.rateLimits.enabled. You’re all set—just double-check that your Spring server version binds the new kebab-case property correctly.

src/commands/pull.ts (1)

43-44: Confirm backend compatibility for escapeHtml option

Client-side support is already in place—escapeHtml is defined in the generated schema (e.g. in src/client/internal/schema.generated.ts at lines 5616, 5704, 6072 and 18050). No changes to client typings are needed.

Please verify that your backend API schema (export mutations) also accepts the escapeHtml parameter; otherwise, older server versions may return 400 errors when this field is sent.

src/schema.d.ts (2)

64-74: New push option types look consistent and scoped appropriately

ErrorOnUnresolvedConflict and OverrideMode align with the PR’s CLI options and expected server values.


205-207: Doc tweak for removeUnused is correct and reduces ambiguity

The “within selected namespaces if specified” clarification matches expected behavior.

src/client/errorFromLoadable.ts (1)

25-26: Return string composition LGTM; ensure appended message doesn’t start with whitespace

With the suggested trimming above (or by removing leading blank lines from getUnresolvedConflictsMessage), this will render cleanly as:

[...items]

src/commands/sync/syncUtils.ts (1)

41-49: Output formatting LGTM

The prefixing and note rendering are clear and consistent with other output utilities.

src/commands/sync/sync.ts (1)

38-38: escapeHtml added consistently – confirm backend support

All occurrences of escapeHtml: false are now aligned across our commands:

  • src/commands/pull.ts:43
  • src/commands/sync/sync.ts:38

Before merging, please ensure that every targeted server version you support accepts the new escapeHtml parameter on this endpoint—otherwise clients may receive 400 errors when talking to older backends.

test/e2e/utils/api/project3.ts (1)

11-12: Good use of satisfies for item-level shape checks

Leveraging satisfies on SingleStepImportResolvableItemRequest is a solid way to keep inference while ensuring schema compliance.

test/e2e/utils/api/fullLanguageNamesProject.ts (1)

11-12: Ensure project languages include en-GB and fr-FR for these payloads

If you don’t pass matching languages to createProjectWithClient, the import will fail. Provide languages in options when using this fixture.

Example usage:

  • createProjectWithClient('x', FULL_LANGUAGE_NAMES_PROJECT, { languages: [{ tag: 'en-GB', name: 'English (UK)' }, { tag: 'fr-FR', name: 'Français (France)' }] })
test/e2e/utils/api/nestedKeysProject.ts (1)

5-15: LGTM! Type migration aligns with the new import schema.

The migration from ImportKeysResolvableDto to SingleStepImportResolvableRequest and the removal of the resolution field from translations is consistent with the API changes and correctly aligns with the new import conflict resolution approach.

test/e2e/utils/api/nestedArrayKeysProject.ts (1)

5-13: LGTM! Consistent with schema migration pattern.

The changes follow the same pattern as other test fixtures, correctly migrating to SingleStepImportResolvableRequest and removing the per-translation resolution field.

test/e2e/push.override.test.ts (3)

30-59: LGTM! Well-structured test for protected translation behavior.

The test correctly validates that reviewed translations are not updated when translation protection is enabled, and appropriately checks for the expected warning message and exit code.


61-91: LGTM! Proper validation of error-on-unresolved-conflict behavior.

The test correctly validates that the CLI fails with exit code 1 when --error-on-unresolved-conflict yes is specified and conflicts exist. The translation state verification ensures the data remains unchanged.


93-124: LGTM! Comprehensive test for override mode ALL.

The test properly validates that the --override-mode ALL option successfully overrides protected translations, with appropriate assertions for both the console output and the actual translation value.

src/commands/push.ts (5)

16-23: LGTM! Clean type imports for new override features.

The imports are properly organized and correctly bring in the new types needed for the override mode and error-on-unresolved-conflict features.


60-61: LGTM! Type definition aligns with new CLI options.

The PushOptions type correctly includes the new optional properties for override mode and error-on-unresolved-conflict handling.


244-268: LGTM! Clean refactoring of import attempt handling.

The consolidation from attempt1/attempt2 to a single attempt variable improves code clarity and the error handling flow is correct.


270-273: LGTM! Proper handling of unresolved conflicts.

The code correctly checks for unresolved conflicts and prints them using the new utility function. The false parameter indicates this is not an error case (exit code 0).


332-347: LGTM! Well-structured CLI option definitions.

The new options for --override-mode and --error-on-unresolved-conflict are properly defined with appropriate descriptions, choices, and defaults.

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

♻️ Duplicate comments (1)
test/e2e/utils/api/common.ts (1)

46-46: Good fix: Options now omits name to avoid conflicting sources

Switching to Partial<Omit<components['schemas']['EditProjectRequest'], 'name'>> correctly removes the conflicting name field while keeping the rest flexible.

🧹 Nitpick comments (1)
src/utils/getStackTrace.ts (1)

1-8: Normalize stack shape across environments and add explicit return type.

Currently, V8 excludes this helper via constructorOpt, but the non-V8 path returns the full stack including this helper and the header line. This yields inconsistent top frames across runtimes. Also, consider adding a return type and avoiding the unnecessary as cast on the object literal.

Proposed refactor:

  • Add a small normalizer that drops the header line and any frame containing getStackTrace.
  • Add explicit return type string.
  • Use (Error as any) to avoid TS compile errors in browser builds lacking Node typings.
  • Prefer const obj: { stack?: string } = {} over casting the literal.
-export const getStackTrace = () => {
-  if (typeof Error.captureStackTrace === 'function') {
-    const obj = {} as { stack?: string };
-    Error.captureStackTrace(obj, getStackTrace);
-    return obj.stack ?? '';
-  }
-  return new Error().stack ?? '';
+export const getStackTrace = (): string => {
+  const normalize = (stack?: string): string => {
+    if (!stack) return '';
+    const lines = stack.split('\n');
+    // Drop the first line (header) and any frame from this helper for consistency.
+    const filtered = lines.filter((line, idx) => idx !== 0 && !line.includes('getStackTrace'));
+    return filtered.join('\n');
+  };
+
+  const ErrorAny = Error as any;
+  if (typeof ErrorAny.captureStackTrace === 'function') {
+    const obj: { stack?: string } = {};
+    ErrorAny.captureStackTrace(obj, getStackTrace);
+    return normalize(obj.stack);
+  }
+  return normalize(new Error().stack);
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fcc4260 and 1556e25.

📒 Files selected for processing (3)
  • src/commands/sync/syncUtils.ts (1 hunks)
  • src/utils/getStackTrace.ts (1 hunks)
  • test/e2e/utils/api/common.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/commands/sync/syncUtils.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
test/e2e/utils/api/common.ts (1)
src/client/internal/schema.generated.ts (1)
  • components (5102-9090)
⏰ 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). (3)
  • GitHub Check: E2E Tests (ubuntu-latest, 20)
  • GitHub Check: E2E Tests (ubuntu-latest, 22)
  • GitHub Check: E2E Tests (ubuntu-latest, 18)
🔇 Additional comments (5)
src/utils/getStackTrace.ts (2)

2-7: Good cross-runtime fallback added; safer and typed.

The V8 check plus fallback to new Error().stack ?? '' addresses the previous review and prevents undefined returns. Using a narrowed { stack?: string } type is also cleaner than any.


2-7: No downstream parsing of stack traces detected

A repository-wide search showed:

  • Only usage of getStackTrace() is in src/utils/logger.ts:109 for logging the full stack.
  • No occurrences of .split( or other parsing on Error.stack or the result of getStackTrace().

Since no code slices or parses the stack string, changing its internal capture method won’t affect any downstream logic.

test/e2e/utils/api/common.ts (3)

62-62: Sane default for icuPlaceholders at create time

Using options?.icuPlaceholders ?? true provides a sensible default while allowing override.


84-87: Correct import endpoint and payload

The switch to /single-step-import-resolvable with the SingleStepImportResolvableRequest payload is correct and consistent with the updated schemas.


50-51: Approved: Data Type Updated to SingleStepImportResolvableRequest

Verified that no manual code still references the old DTOs or endpoints. The only remaining occurrences of ImportKeysResolvableDto/ImportTranslationResolvableDto are in src/client/internal/schema.generated.ts (auto-generated). The new type and /v2/projects/single-step-import-resolvable endpoint are used correctly throughout the codebase.

@stepan662 stepan662 merged commit af32420 into main Aug 13, 2025
15 checks passed
@stepan662 stepan662 deleted the stepangranat/use-new-import-endpoint branch August 13, 2025 10:52
@github-actions
Copy link

🎉 This PR is included in version 2.14.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@coderabbitai coderabbitai bot mentioned this pull request Aug 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 31, 2025
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.

3 participants