Skip to content

fix(i18n): gateway GetTranslations catalog for multi-app terminology - #258

Merged
buke merged 9 commits into
mainfrom
fix/i18n-gateway-gettranslations-req-wrap
Aug 7, 2026
Merged

fix(i18n): gateway GetTranslations catalog for multi-app terminology#258
buke merged 9 commits into
mainfrom
fix/i18n-gateway-gettranslations-req-wrap

Conversation

@buke

@buke buke commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

  • Wrap gateway GetTranslations RPC args as protobuf Value req so catalog fetches match generated service stubs.

  • Enable web TranslationTerm codegen/runtime (api/web/proto, bundle scripts) and register app protos under {app}/ paths for ProtoLoader.

  • Bypass RecordRule/FieldRule on TranslationTerm.GetTranslations catalog Search so non-meta hosts (e.g. base) return terms under the gateway's internal identity.

Test plan

  • go test ./internal/i18n/gateway/... ./internal/service/... ./internal/module/plan/... ./internal/server/runplan/... -count=1

  • ./choysum test unit core --be --pattern 'GetTranslations'

  • Restart server after upgrade; GET /web/i18n/translations?lang=zh_CN includes base (and other apps), not only meta/core

  • UI at /web/base/companies shows Chinese field/menu labels when locale is zh_CN


By submitting this pull request, you agree to the Contributor License Agreement (CLA) of this project. If you have not yet signed it, please follow the instructions provided by @cla-bot below.

Made with Cursor


PR Type

Bug fix, Enhancement, Tests


Description

  • Go core changes for web i18n

    • Wrap gateway GetTranslations RPC args in req.
    • Enable web app proto generation and script routing.
    • Register protos under {app}/{file}.proto for loader.
  • TypeScript module changes for catalog reads

    • Bypass authz rules on TranslationTerm catalog Search.
  • License compliance and test coverage

    • No new files or SPDX header changes required.
    • Updated Go tests and added TypeScript unit test.

File Walkthrough

Relevant files
Bug fix
2 files
client.go
wrap dynamic GetTranslations RPC arguments and unwrap response
+13/-4   
translation_term_base_model.ts
bypass repository authz rules during GetTranslations catalog search
+12/-6   
Tests
11 files
terms_rpc_fixture_test.go
update RPC fixtures to reflect wrapped request and response values
+7/-14   
pipeline_test.go
add pipeline test covering web module generation                 
+23/-5   
sync_test.go
update proto sync test to verify web app restoration         
+9/-2     
modulemanager_coverage_test.go
update installed app listing tests to include web app       
+12/-12 
planner_test.go
update build plan tests for affected apps with web             
+4/-4     
dist_validation_test.go
update dist validation unit tests for web proto requirement
+28/-2   
server_bootstrap_orchestration_test.go
seed web bundle and proto dist in server bootstrap tests 
+3/-11   
server_service_registration_bootstrap_test.go
seed web application dist in bootstrap registration test 
+1/-0     
server_test_fixtures_test.go
add helpers to seed web backend and bundle dist assets     
+37/-0   
service_test.go
test loader register path logic and web service scripts   
+72/-3   
translation_term_base_model_coverage.test.ts
add unit test for GetTranslations authz rule bypass           
+33/-0   
Enhancement
6 files
pipeline.go
include web application in app-stage module generation     
+3/-4     
sync.go
include web application in missing proto synchronization 
+1/-1     
modulemanager.go
rebuild TranslationTerm IR before app generation and include web
+83/-4   
planner.go
include web application in affected apps planning               
+4/-5     
dist_validation.go
validate dist assets for web backend proto targets             
+28/-2   
service.go
add scriptDistPath and register app protos under app path
+43/-6   
Additional files
2 files
plan_test.go +5/-6     
server_serve_test.go +1/-3     

Summary by CodeRabbit

  • New Features

    • Web application modules are now generated, synchronized, and included in upgrade workflows.
    • Web services can serve static content while using dedicated backend scripts and API assets.
    • Web applications are recognized consistently across planning, installation, and runtime startup.
    • Generated applications now retain injected translation and model data.
  • Bug Fixes

    • Improved translation gateway compatibility with updated request and response formats.
    • Translation catalog reads now work reliably when authorization rules would otherwise restrict access.
    • Web bundles and required API assets are validated before application mode starts.

buke and others added 4 commits August 7, 2026 15:52
- Encode lang/module_names/hash under the generated req field and unwrap result so catalog reads match TranslationTerm_GetTranslations_* shapes.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Include application web in app-stage codegen and stop treating it as SPA-only.

- Load api/web/proto plus bundle scripts on ApplicationService(web) while keeping dist/web static assets.

- Persist missing TranslationTerm IR before GenerateApp so EnsureServiceEntry hosts emit descriptors.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Prefix ServiceDescs RegisterProto paths as {app}/{file}.proto so web works without JS bundle registration.

- Cover api/<app>/proto layout and loaderRegisterPath helpers in unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Wrap gateway catalog Search with withRepositoryAuthzRuleBypass so non-meta hosts return terms under internal identity.

- Cover the bypass path in TranslationTerm GetTranslations unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06fc4062-bc5a-4e47-85b6-a72c46b8dc15

📥 Commits

Reviewing files that changed from the base of the PR and between 97e67f6 and 8879d48.

📒 Files selected for processing (12)
  • internal/i18n/gateway/client.go
  • internal/i18n/gateway/rpc_client_test.go
  • internal/i18n/gateway/terms_rpc_fixture_test.go
  • internal/module/lifecycle/ensure_injected_codegen_test.go
  • internal/module/lifecycle/installer_commit_test.go
  • internal/module/lifecycle/module_index_sync_test.go
  • internal/module/lifecycle/modulemanager.go
  • internal/module/lifecycle/split_builder_stub_test.go
  • internal/server/runplan/dist_validation_test.go
  • internal/service/service_test.go
  • modules/core/service/orm/model/translation_term_base_model.ts
  • modules/core/service/orm/model/translation_term_base_model_coverage.test.ts
💤 Files with no reviewable changes (1)
  • internal/module/lifecycle/installer_commit_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • modules/core/service/orm/model/translation_term_base_model.ts
  • modules/core/service/orm/model/translation_term_base_model_coverage.test.ts
  • internal/service/service_test.go

📝 Walkthrough

Walkthrough

The change aligns translation RPC envelopes, includes web in module generation and runtime validation, separates web scripts from static assets, registers web API protobufs, and bypasses repository authorization rules for translation catalog reads.

Changes

Web application and translation runtime

Layer / File(s) Summary
Translation RPC envelopes
internal/i18n/gateway/*
Translation requests now use req, and responses read result. Fixtures cover legacy responses, malformed results, decode failures, and request-build failures.
Web module planning and staging
internal/module/plan/*, internal/module/artifact/*
The planner, artifact pipeline, and runtime synchronization now retain and process the web application.
Module code generation and installed app discovery
internal/module/lifecycle/*
Code generation persists injected TranslationTerm models, supports Ensure-only applications, creates virtual bundle imports, and includes web in installed application targets.
Web distribution validation and fixtures
internal/server/runplan/*, internal/server/server_*test.go
Web targets require bundle, application, index, and API proto assets. Shared test helpers seed valid bundle-mode and application-mode distributions.
Web service scripts and protobuf registration
internal/service/*
Web services resolve scripts, static files, and API protobufs from separate paths. Loader registration prefixes app-owned protobuf paths.
Translation catalog authorization bypass
modules/core/service/orm/model/translation_term_base_model*
GetTranslations performs its repository search with authorization-rule bypasses. Regression coverage verifies both bypass depths and the returned translations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the gateway GetTranslations fix for multi-application terminology, which matches the primary pull request objective.
Description check ✅ Passed The description explains the changes, objectives, affected areas, test plan, and includes the required CLA statement.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/i18n-gateway-gettranslations-req-wrap

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

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 97e67f6)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to 97e67f6
Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent panic when no installed modules exist

If no installed modules exist for appName, mods will be empty. Accessing
mods[len(mods)-1] later in this function will cause a runtime panic due to a slice
index out of bounds (-1). Return early if mods is empty.

internal/module/lifecycle/modulemanager.go [153-163]

 var mods []meta.Module
 if err := m.runtimeScope.Session().
     Where("application_str = ? AND status = ?", appName, meta.Installed).
     Order("id ASC").
     Find(&mods).Error; err != nil {
     return xfmt.Errorf("error loading backend modules for app %s: %w", appName, err)
 }
+if len(mods) == 0 {
+    return nil
+}
 
 runtimeOpts := m.resolvedRuntimeOptions()
Suggestion importance[1-10]: 7

__

Why: If mods is empty, accessing mods[len(mods)-1] later in buildBackendAppToDir will cause a slice index out of bounds panic. Adding a check for len(mods) == 0 safely prevents this issue.

Medium

Previous suggestions

Suggestions up to commit 5ad02a1
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent panic when no modules found

If no installed modules are found for the specified appName, the mods slice will be
empty. Attempting to access mods[len(mods)-1] later in this function will trigger an
out-of-bounds index panic. Add an early return check to handle empty mods
gracefully.

internal/module/lifecycle/modulemanager.go [151-159]

 	var mods []meta.Module
 	if err := m.runtimeScope.Session().
 		Where("application_str = ? AND status = ?", appName, meta.Installed).
 		Order("id ASC").
 		Find(&mods).Error; err != nil {
 		return xfmt.Errorf("error loading backend modules for app %s: %w", appName, err)
 	}
+	if len(mods) == 0 {
+		return nil
+	}
 
 	runtimeOpts := m.resolvedRuntimeOptions()
Suggestion importance[1-10]: 8

__

Why: If no installed modules are found for appName, the mods slice will be empty, causing mods[len(mods)-1] to panic with an out-of-bounds index. Adding an early check prevents this.

Medium
General
Handle empty service entry points

If mod.ServiceEntryPoint is empty (which is common for frontend-only "web" modules),
entry will be empty. Passing an empty string as the entry point to NewModuleBuilder
causes compilation/bundling to fail. Create a temporary stub file with export {};
when entry is empty, and ensure it is cleaned up.

internal/module/lifecycle/modulemanager.go [116-127]

 	runtimeOpts := m.resolvedRuntimeOptions()
 	entry := strings.TrimSpace(mod.ServiceEntryPoint)
-	if entry != "" && !filepath.IsAbs(entry) {
+	var tempFile *os.File
+	if entry == "" {
+		var err error
+		tempFile, err = os.CreateTemp("", "choysum_stub_*.ts")
+		if err != nil {
+			return xfmt.Errorf("create stub entry: %w", err)
+		}
+		defer os.Remove(tempFile.Name())
+		if _, err := tempFile.WriteString("export {};\n"); err != nil {
+			tempFile.Close()
+			return xfmt.Errorf("write stub entry: %w", err)
+		}
+		tempFile.Close()
+		entry = tempFile.Name()
+	} else if !filepath.IsAbs(entry) {
 		entry = filepath.Join(runtimeOpts.modulesPath, mod.Name, entry)
 	}
 	builder := internalbackendbuilder.NewModuleBuilder(
 		m.runtimeScope,
 		m.jsExecutor,
 		mod,
 		entry,
 		internalbackendbuilder.WithPublishDist(false),
 	)
Suggestion importance[1-10]: 8

__

Why: If mod.ServiceEntryPoint is empty (common for frontend-only "web" modules), passing an empty entry to NewModuleBuilder can cause bundling to fail. Creating a temporary stub file avoids this.

Medium

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

- Treat web as TranslationTerm backend in tests by seeding bundles and api/web/proto.
- Keep application-mode bootstrap readiness covering apps/web index + proto.

Co-authored-by: Cursor <cursoragent@cursor.com>
@buke
buke marked this pull request as ready for review August 7, 2026 09:56
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Persistent review updated to latest commit 97e67f6

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
modules/core/service/orm/model/translation_term_base_model_coverage.test.ts (1)

63-65: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Assert bypass-depth restoration after GetTranslations.

The test verifies that both bypass depths are enabled during Search. It does not verify that the awaited call restores them afterward. A leaked bypass can authorize later repository operations in the same request.

Add zero-depth assertions after GetTranslations resolves.

Proposed fix
     const out = await TtCovTerm.GetTranslations({ lang: 'zh_CN', module_names: ['auth'] });
     expect(sawBypass).toBe(true);
+    expect(getRepositoryRecordRuleBypassDepth()).toBe(0);
+    expect(getRepositoryFieldRuleBypassDepth()).toBe(0);
     expect(out.terms_by_module).toEqual({ auth: { ui: { Hi: '你好' } } });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/core/service/orm/model/translation_term_base_model_coverage.test.ts`
around lines 63 - 65, Extend the test around TtCovTerm.GetTranslations to assert
that both bypass-depth values are zero after the awaited call resolves. Keep the
existing assertions confirming bypass during Search and the returned
translations unchanged, and use the test’s existing bypass-depth symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/i18n/gateway/client.go`:
- Around line 77-82: Update the response handling before parseAppTranslations so
the legacy fallback occurs only when out lacks the "result" key. If "result" is
present but cannot be decoded as map[string]any, return a decode error instead
of parsing an empty catalog; preserve normal parsing for valid envelopes and
absent-result legacy responses.

---

Nitpick comments:
In `@modules/core/service/orm/model/translation_term_base_model_coverage.test.ts`:
- Around line 63-65: Extend the test around TtCovTerm.GetTranslations to assert
that both bypass-depth values are zero after the awaited call resolves. Keep the
existing assertions confirming bypass during Search and the returned
translations unchanged, and use the test’s existing bypass-depth symbols.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc5170a4-fbc9-4f11-9ced-8969b18cbd45

📥 Commits

Reviewing files that changed from the base of the PR and between fdf6bb3 and 97e67f6.

📒 Files selected for processing (21)
  • internal/i18n/gateway/client.go
  • internal/i18n/gateway/terms_rpc_fixture_test.go
  • internal/module/artifact/pipeline/pipeline.go
  • internal/module/artifact/pipeline/pipeline_test.go
  • internal/module/artifact/runtimeapi/sync.go
  • internal/module/artifact/runtimeapi/sync_test.go
  • internal/module/lifecycle/modulemanager.go
  • internal/module/lifecycle/modulemanager_coverage_test.go
  • internal/module/plan/planner.go
  • internal/module/plan/planner_test.go
  • internal/server/runplan/dist_validation.go
  • internal/server/runplan/dist_validation_test.go
  • internal/server/runplan/plan_test.go
  • internal/server/server_bootstrap_orchestration_test.go
  • internal/server/server_serve_test.go
  • internal/server/server_service_registration_bootstrap_test.go
  • internal/server/server_test_fixtures_test.go
  • internal/service/service.go
  • internal/service/service_test.go
  • modules/core/service/orm/model/translation_term_base_model.ts
  • modules/core/service/orm/model/translation_term_base_model_coverage.test.ts

Comment thread internal/i18n/gateway/client.go Outdated

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 21 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread modules/core/service/orm/model/translation_term_base_model.ts
buke and others added 2 commits August 7, 2026 18:18
- Reject a present non-object GetTranslations result instead of legacy empty-catalog fallback.
- Assert authz bypass depths restore after GetTranslations Search.
- Clarify that catalog-wide bypass is intentional (FieldDefault.GetEffective pattern).

Co-authored-by: Cursor <cursoragent@cursor.com>
- Cover malformed GetTranslations result unwrap via gateway RPC fixture.
- Cover web application-mode dist validation and default-target dedupe.
- Cover web application-mode scriptDistPath and empty loaderRegisterPath.
- Cover ensureInjectedAppModelsForCodegen branches via testable builder hook.
- Cover Ensure-only stub entry inject errors and core upgrade listInstalledApps.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread internal/module/lifecycle/module_index_sync_test.go
Comment thread internal/service/service_test.go Outdated
Comment thread internal/module/lifecycle/ensure_injected_codegen_test.go Outdated
- Assert core upgrade plan apps include web via listInstalledApps, not lease entry.
- Check protoImportPaths for application-mode web ApplicationService.
- Reuse commitStubSplitBuilder for ensureInjected codegen tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread internal/module/lifecycle/installer_commit_test.go Outdated
- Keep entrySeen/buildErr on the package stub used by ensureInjected coverage.
- Avoid looking like dead fields inside installer_commit_test.go alone.

Co-authored-by: Cursor <cursoragent@cursor.com>
@buke
buke merged commit 1e3b529 into main Aug 7, 2026
46 checks passed
@buke
buke deleted the fix/i18n-gateway-gettranslations-req-wrap branch August 7, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant