Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: proxy mode #571

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open

feat: proxy mode #571

wants to merge 52 commits into from

Conversation

hezhengxu2018
Copy link
Collaborator

@hezhengxu2018 hezhengxu2018 commented Aug 16, 2023

#366 开启代理模式时如果找不到依赖会直接返回上游仓库的manifest信息并缓存于nfs,当请求的tgz文件不存在时从上游仓库获取并返回,同时创建对应版本的同步任务。每小时检查更新已缓存的manifest文件保证上游仓库发布新版本时不会因为缓存落后而404。

Summary by CodeRabbit

  • New Features

    • Introduced proxy cache management for package manifests and versions.
    • Added new HTTP methods for managing proxy caches.
    • Implemented scheduled workers for updating and synchronizing proxy cache.
  • Updates

    • Expanded SyncMode enum to include a new value proxy.
    • Updated constants with PROXY_CACHE_DIR_NAME and ABBREVIATED_META_TYPE.
  • Tests

    • Added comprehensive test cases for ProxyCacheService, ProxyCacheRepository, and related controllers.
    • Verified functionality of scheduled workers for proxy cache updates and synchronization.
    • Enhanced testing coverage for handling package downloads in proxy mode.

@hezhengxu2018 hezhengxu2018 marked this pull request as draft August 16, 2023 06:31
@codecov
Copy link

codecov bot commented Aug 16, 2023

Codecov Report

Attention: Patch coverage is 97.43276% with 21 lines in your changes missing coverage. Please review.

Project coverage is 96.83%. Comparing base (bd49917) to head (1ce994c).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
app/core/service/ProxyCacheService.ts 96.44% 9 Missing ⚠️
app/port/schedule/SyncProxyCacheWorker.ts 91.07% 5 Missing ⚠️
app/repository/ProxyCacheRepository.ts 95.16% 3 Missing ⚠️
app/port/controller/ProxyCacheController.ts 98.71% 2 Missing ⚠️
app/port/schedule/CheckProxyCacheUpdateWorker.ts 96.22% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #571      +/-   ##
==========================================
+ Coverage   96.81%   96.83%   +0.02%     
==========================================
  Files         181      188       +7     
  Lines       18003    18799     +796     
  Branches     2336     2466     +130     
==========================================
+ Hits        17429    18204     +775     
- Misses        574      595      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hezhengxu2018 hezhengxu2018 changed the title [WIP] feat: proxy mode feat: proxy mode Aug 18, 2023
@hezhengxu2018 hezhengxu2018 marked this pull request as ready for review August 18, 2023 02:44
@hezhengxu2018
Copy link
Collaborator Author

目前代理模式无法配置上游仓库认证头的信息,认证头应该作为仓库的一个属性记录在数据库里,可以通过接口修改。

@elrrrrrrr
Copy link
Member

@hezhengxu2018 🤩 已经改完了吗?

@hezhengxu2018
Copy link
Collaborator Author

@hezhengxu2018 🤩 已经改完了吗?

是的,现在会定时更新已缓存的manifest,不过还没有手动管理缓存的接口。verdicca和nexus都没做这个,先基础功能确定没问题再做吧

@hezhengxu2018 hezhengxu2018 marked this pull request as draft December 12, 2023 14:37
@hezhengxu2018 hezhengxu2018 marked this pull request as ready for review December 24, 2023 15:43
@hezhengxu2018
Copy link
Collaborator Author

管理缓存的接口也加上了,功能相对来说比较完整了,帮忙看一下吧 @elrrrrrrr @fengmk2

@elrrrrrrr
Copy link
Member

管理缓存的接口也加上了,功能相对来说比较完整了,帮忙看一下吧 @elrrrrrrr @fengmk2

🤩 改动内容比较多 我明天详细看下 🙏🏻

pkg = await this.getPackageEntityByFullname(fullname, allowSync);
packageVersion = await this.getPackageVersionEntity(pkg, version, allowSync);
} catch (error) {
if (this.config.cnpmcore.syncMode === SyncMode.proxy) {
Copy link
Member

Choose a reason for hiding this comment

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

这里直接用 AOP 来组织一下逻辑会清晰一下,类似 EventCorkAdvice 的实现

在原来方法返回前,判断一下是否有返回值,如果没有的话再去触发 proxyMode 的 fallback 逻辑

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🤯

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

尝试了一下好像没有改写函数返回的切点?这样的话确实可以较为优雅的处理代理的逻辑但是改不了接口的返回值。用户还是会收到not found的返回。还是希望代理模式的表现和nexus与verdaccio保持一致。

@elrrrrrrr
Copy link
Member

@hezhengxu2018

proxyMode 定位是做代理模式,然后本地 registry 做缓存加速?需要确认一下 manifest 以本地为准还是以上游为准。

目前实现是 manifest 和 tgz 请求时,将代理结果直接返回,并创建同步任务

  1. proxyMode 下,对于
  2. 优先返回 本地 registry 信息
  3. 若本地没有版本数据,则代理返回上游 registry
  4. tgz 访问时触发下载,仅同步当前访问报的单个版本

如果 proxyMode 中直接命中 tgz 下载,需要等异步定时任务补偿后才能继续访问,否则 manifest 会返回单个版本?客户单查包信息的时候就过期了。

我们是否可以改为 proxyMode 下,始终返回上游 registry 信息已获取更高的实时性。

@hezhengxu2018
Copy link
Collaborator Author

@hezhengxu2018

proxyMode 定位是做代理模式,然后本地 registry 做缓存加速?需要确认一下 manifest 以本地为准还是以上游为准。

目前实现是 manifest 和 tgz 请求时,将代理结果直接返回,并创建同步任务

  1. proxyMode 下,对于
  2. 优先返回 本地 registry 信息
  3. 若本地没有版本数据,则代理返回上游 registry
  4. tgz 访问时触发下载,仅同步当前访问报的单个版本

如果 proxyMode 中直接命中 tgz 下载,需要等异步定时任务补偿后才能继续访问,否则 manifest 会返回单个版本?客户单查包信息的时候就过期了。

我们是否可以改为 proxyMode 下,始终返回上游 registry 信息已获取更高的实时性。

开启proxyMode之后返回的manifest都是上游仓库上次更新的manifest,不会使用数据库里的manifest信息。如果上游仓库无法正常使用了,切回到none模式下才会返回代理仓库已缓存版本的manifest。如果异步任务还没同步完成会一直通过反向代理返回上游仓库的tgz信息,用户不会需要等待异步任务完成,当异步任务完成后才会优先从对象存储中读取tgz。

代理仓库主要的功能点是在访问外网或者官方npm仓库非常缓慢甚至无法访问的情况下能够缓存结果加速内网用户安装依赖,同时即使外网无法访问了也不影响内网用户照常使用已经缓存的依赖,所以始终返回上游仓库可能不行,如果内网用户发现代理仓库的缓存需要更新或者缓存脏数据了可以使用/-/proxy-cache接口进行一些缓存的刷新删除。

@hezhengxu2018 hezhengxu2018 marked this pull request as draft December 25, 2023 16:08
@hezhengxu2018
Copy link
Collaborator Author

manifest 以本地为准,因为nexus是这么做的。代理模式整体是一个缓存,如果网络不好还一直使用上游仓库的索引的话就没有缓存的意义了,不过nexus默认刷新manifest的频率很高,30分钟就会去刷新一次,我设置每天刷新一次感觉有点保守了。

@hezhengxu2018 hezhengxu2018 marked this pull request as ready for review December 29, 2023 16:04
Copy link
Contributor

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

Caution

Inline review comments failed to post

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 563a1b5 and 6f0dce3.

Files selected for processing (6)
  • app/port/controller/ProxyCacheController.ts (1 hunks)
  • app/repository/ProxyCacheRepository.ts (1 hunks)
  • app/repository/model/ProxyCache.ts (1 hunks)
  • sql/3.47.0.sql (1 hunks)
  • test/port/controller/ProxyCacheController/index.test.ts (1 hunks)
  • test/repository/ProxyCachePepository.test.ts (1 hunks)
Additional comments not posted (46)
sql/3.47.0.sql (12)

1-1: LGTM!

The CREATE TABLE IF NOT EXISTS statement ensures that the table is only created if it does not already exist.


2-2: LGTM!

The id column is correctly defined as the primary key with auto-increment.


3-3: LGTM!

The gmt_create column is correctly defined to store the creation time with a comment.


4-4: LGTM!

The gmt_modified column is correctly defined to store the modification time with a comment.


5-5: LGTM!

The fullname column is correctly defined to store the package name with a comment.


6-6: LGTM!

The version column is correctly defined to store the package version with a comment.


7-7: LGTM!

The file_type column is correctly defined to store the file type with a comment.


8-8: LGTM!

The file_path column is correctly defined to store the NFS file path with a comment.


9-9: LGTM!

The primary key constraint is correctly defined for the id column.


10-10: LGTM!

The unique key constraint is correctly defined for the file_path column.


11-11: LGTM!

The unique key constraint is correctly defined for the combination of fullname, file_type, and version columns.


12-12: LGTM!

The storage engine and default character set are correctly defined for the table.

app/repository/model/ProxyCache.ts (12)

1-1: LGTM!

The import of Attribute and Model from @eggjs/tegg/orm is necessary for defining the model and its attributes.


2-2: LGTM!

The import of DataTypes and Bone from leoric is necessary for defining the data types and extending the Bone class.


3-3: LGTM!

The import of DIST_NAMES from ../../core/entity/Package is necessary for defining the fileType attribute.


5-5: LGTM!

The ProxyCache model class is correctly defined with the @Model decorator.


6-6: LGTM!

Extending the Bone class is necessary for defining the model.


7-10: LGTM!

The id attribute is correctly defined as the primary key with the BIGINT data type and auto-increment configuration.


13-14: LGTM!

The createdAt attribute is correctly defined with the DATE data type and a custom name.


16-17: LGTM!

The updatedAt attribute is correctly defined with the DATE data type and a custom name.


19-20: LGTM!

The fullname attribute is correctly defined with the STRING data type and a length of 214.


22-23: LGTM!

The fileType attribute is correctly defined with the STRING data type and a length of 30.


25-28: LGTM!

The filePath attribute is correctly defined with the STRING data type and a length of 512, and a unique constraint.


30-31: LGTM!

The version attribute is correctly defined with the STRING data type and a length of 214.

app/port/controller/ProxyCacheController.ts (5)

33-48: Ensure proxy mode is enabled before listing proxy caches.

The method correctly checks if the proxy mode is enabled before proceeding. This is crucial for ensuring that the functionality is only available when the proxy mode is active.


50-63: Ensure proxy mode is enabled and parameters are correctly handled.

The method correctly checks if the proxy mode is enabled and handles parameters appropriately. This ensures that the functionality is only available when the proxy mode is active and the parameters are correctly processed.


65-97: Ensure proxy mode is enabled and handle task creation logic.

The method correctly checks if the proxy mode is enabled, handles parameters appropriately, and includes logic for task creation. This ensures that the functionality is only available when the proxy mode is active, the parameters are correctly processed, and tasks are created as needed.


99-127: Ensure admin privileges and proxy mode are enabled before removing proxy caches.

The method correctly checks for admin privileges and ensures the proxy mode is enabled before proceeding. This is crucial for ensuring that only authorized users can perform this action and that the functionality is only available when the proxy mode is active.


129-155: Ensure admin privileges and proxy mode are enabled before truncating proxy caches.

The method correctly checks for admin privileges and ensures the proxy mode is enabled before proceeding. It also handles background tasks for removing the proxy cache directory. This ensures that only authorized users can perform this action, the functionality is only available when the proxy mode is active, and background tasks are managed properly.

test/port/controller/ProxyCacheController/index.test.ts (5)

45-67: Ensure comprehensive test coverage for listing proxy caches.

The test case covers various scenarios for listing proxy caches, including checks for proxy mode and pagination. This ensures that the functionality is thoroughly tested and behaves as expected in different situations.


70-87: Ensure comprehensive test coverage for showing specific proxy caches.

The test case covers various scenarios for showing specific proxy caches, including checks for proxy mode and not found scenarios. This ensures that the functionality is thoroughly tested and behaves as expected in different situations.


90-116: Ensure comprehensive test coverage for refreshing proxy caches.

The test case covers various scenarios for refreshing proxy caches, including checks for proxy mode and task creation. This ensures that the functionality is thoroughly tested and behaves as expected in different situations.


118-159: Ensure comprehensive test coverage for removing proxy caches.

The test case covers various scenarios for removing proxy caches, including checks for admin privileges, proxy mode, and not found scenarios. This ensures that the functionality is thoroughly tested and behaves as expected in different situations.


161-189: Ensure comprehensive test coverage for truncating all proxy caches.

The test case covers various scenarios for truncating all proxy caches, including checks for admin privileges and proxy mode. This ensures that the functionality is thoroughly tested and behaves as expected in different situations.

test/repository/ProxyCachePepository.test.ts (7)

20-27: LGTM!

The test case for creating a new proxy cache is correct and covers the necessary assertions.


29-37: LGTM!

The test case for updating a proxy cache is correct and covers the necessary assertions.


39-42: LGTM!

The test case for listing proxy caches is correct and covers the necessary assertions.


44-47: LGTM!

The test case for querying a non-existent proxy cache is correct and covers the necessary assertions.


49-52: LGTM!

The test case for querying an existing proxy cache is correct and covers the necessary assertions.


54-58: LGTM!

The test case for removing a proxy cache is correct and covers the necessary assertions.


60-68: LGTM!

The test case for truncating proxy caches is correct and covers the necessary assertions.

app/repository/ProxyCacheRepository.ts (5)

33-37: LGTM!

The method for finding a proxy cache entity is correct and covers the necessary functionality.


39-43: LGTM!

The method for finding proxy cache entities is correct and covers the necessary functionality.


45-53: LGTM!

The method for listing cached files is correct and covers the necessary functionality.


55-57: LGTM!

The method for removing a proxy cache entity is correct and covers the necessary functionality.


59-61: LGTM!

The method for truncating all proxy cache entities is correct and covers the necessary functionality.

Comments failed to post (1)
app/repository/ProxyCacheRepository.ts

15-31: Improve error message handling and add unit tests.

The custom error message modification in the saveProxyCache method lacks test coverage. This is essential for ensuring that the error handling behaves as expected.

Would you like me to help by adding some test cases for this scenario?

Copy link
Contributor

@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: 13

Outside diff range, codebase verification and nitpick comments (5)
app/port/schedule/CheckProxyCacheUpdateWorker.ts (1)

45-46: Improve error logging for better debugging.

The current error logging is too generic. It's beneficial to include more context about the error, especially since this is a background worker where direct debugging can be challenging.

- this.logger.error(err);
+ this.logger.error('Error processing proxy cache update:', err);
Tools
GitHub Check: codecov/patch

[warning] 45-46: app/port/schedule/CheckProxyCacheUpdateWorker.ts#L45-L46
Added lines #L45 - L46 were not covered by tests

app/core/entity/Task.ts (1)

256-278: Ensure test coverage for createUpdateProxyCache method

No test cases were found for the createUpdateProxyCache method, specifically for the new logic that throws an error when the file type is not a package manifest. Please add test cases to cover this logic to ensure proper test coverage.

  • Add test cases for createUpdateProxyCache in the relevant test files.
  • Ensure that the error-throwing logic is tested.
Analysis chain

New method createUpdateProxyCache is correctly implemented.

The new method correctly creates update proxy cache tasks and integrates well with existing code.

However, ensure that the new logic for throwing an error when the file type is not a package manifest is covered by tests.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for the new task creation logic.

# Test: Search for test cases covering the new logic. Expect: At least one test case covering the error-throwing logic.
rg --type typescript -A 50 $'createUpdateProxyCache'

Length of output: 86


Script:

#!/bin/bash
# Description: Verify test coverage for the new task creation logic.

# Test: Search for test cases covering the new logic. Expect: At least one test case covering the error-throwing logic.
rg --glob '*.ts' -A 50 'createUpdateProxyCache'

Length of output: 6935


Script:

#!/bin/bash
# Description: Verify test coverage for the new task creation logic.

# Search for test files that include references to ProxyCacheService or Task.
rg --glob '*test*.ts' -l 'ProxyCacheService|Task'

Length of output: 1615


Script:

#!/bin/bash
# Description: Verify test coverage for the new task creation logic.

# Search within the identified test files for test cases that involve createUpdateProxyCache.
rg 'createUpdateProxyCache' test/core/service/ProxyCacheService.test.ts test/core/service/TaskService/findExecuteTask.test.ts

Length of output: 125


Script:

#!/bin/bash
# Description: Verify test coverage for the new task creation logic.

# Search all test files for test cases that involve createUpdateProxyCache.
rg 'createUpdateProxyCache' --glob '*test*.ts'

Length of output: 46

Tools
Biome

[error] 275-276: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

GitHub Check: codecov/patch

[warning] 258-259: app/core/entity/Task.ts#L258-L259
Added lines #L258 - L259 were not covered by tests

app/core/service/ProxyCacheService.ts (3)

54-57: Consider logging the error before throwing it.

Logging the error before throwing it can help in debugging and monitoring.

-  throw new ForbiddenError(`stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}`);
+  this.logger.warn(`stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}`);
+  throw new ForbiddenError(`stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}`);
Tools
GitHub Check: codecov/patch

[warning] 56-57: app/core/service/ProxyCacheService.ts#L56-L57
Added lines #L56 - L57 were not covered by tests


102-108: Consider adding logging for cache removals.

Adding logging statements can help in tracking successful cache removals and diagnosing issues.

+  this.logger.info(`Successfully removed cache for ${fullname} with fileType ${fileType}`);

118-159: Consider adding logging for the start and successful completion of tasks.

Adding logging statements can help in comprehensive debugging and monitoring.

+  this.logger.info(`[ProxyCacheService.executeTask:start] taskId: ${task.taskId}, targetName: ${task.targetName}`);
+  this.logger.info(`[ProxyCacheService.executeTask:success] taskId: ${task.taskId}, targetName: ${task.targetName}`);
Tools
GitHub Check: codecov/patch

[warning] 132-139: app/core/service/ProxyCacheService.ts#L132-L139
Added lines #L132 - L139 were not covered by tests


[warning] 153-157: app/core/service/ProxyCacheService.ts#L153-L157
Added lines #L153 - L157 were not covered by tests

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 6f0dce3 and fe1c16c.

Files selected for processing (28)
  • app/common/adapter/NPMRegistry.ts (1 hunks)
  • app/common/constants.ts (1 hunks)
  • app/common/enum/Task.ts (1 hunks)
  • app/core/entity/ProxyCache.ts (1 hunks)
  • app/core/entity/Task.ts (5 hunks)
  • app/core/service/ProxyCacheService.ts (1 hunks)
  • app/port/controller/ProxyCacheController.ts (1 hunks)
  • app/port/controller/package/DownloadPackageVersionTar.ts (3 hunks)
  • app/port/controller/package/ShowPackageController.ts (3 hunks)
  • app/port/controller/package/ShowPackageVersionController.ts (1 hunks)
  • app/port/schedule/CheckProxyCacheUpdateWorker.ts (1 hunks)
  • app/port/schedule/CheckRecentlyUpdatedPackages.ts (1 hunks)
  • app/port/schedule/SyncProxyCacheWorker.ts (1 hunks)
  • app/repository/ProxyCacheRepository.ts (1 hunks)
  • app/repository/model/ProxyCache.ts (1 hunks)
  • package.json (1 hunks)
  • sql/3.47.0.sql (1 hunks)
  • test/core/service/ProxyCacheService.test.ts (1 hunks)
  • test/fixtures/registry.npmjs.org/abbreviated_foobar.json (1 hunks)
  • test/fixtures/registry.npmjs.org/foobar/1.0.0/abbreviated.json (1 hunks)
  • test/fixtures/registry.npmjs.org/foobar/1.0.0/package.json (1 hunks)
  • test/port/controller/ProxyCacheController/index.test.ts (1 hunks)
  • test/port/controller/package/DownloadPackageVersionTarController.test.ts (2 hunks)
  • test/port/controller/package/ShowPackageController.test.ts (2 hunks)
  • test/port/controller/package/ShowPackageVersionController.test.ts (2 hunks)
  • test/repository/ProxyCachePepository.test.ts (1 hunks)
  • test/schedule/CheckProxyCacheUpdateWorker.test.ts (1 hunks)
  • test/schedule/SyncProxyCacheWorker.test.ts (1 hunks)
Files skipped from review due to trivial changes (5)
  • app/common/enum/Task.ts
  • package.json
  • test/fixtures/registry.npmjs.org/abbreviated_foobar.json
  • test/fixtures/registry.npmjs.org/foobar/1.0.0/abbreviated.json
  • test/fixtures/registry.npmjs.org/foobar/1.0.0/package.json
Files skipped from review as they are similar to previous changes (3)
  • app/repository/model/ProxyCache.ts
  • sql/3.47.0.sql
  • test/repository/ProxyCachePepository.test.ts
Additional context used
GitHub Check: codecov/patch
app/port/schedule/CheckProxyCacheUpdateWorker.ts

[warning] 45-46: app/port/schedule/CheckProxyCacheUpdateWorker.ts#L45-L46
Added lines #L45 - L46 were not covered by tests

app/port/schedule/SyncProxyCacheWorker.ts

[warning] 43-46: app/port/schedule/SyncProxyCacheWorker.ts#L43-L46
Added lines #L43 - L46 were not covered by tests


[warning] 51-51: app/port/schedule/SyncProxyCacheWorker.ts#L51
Added line #L51 was not covered by tests

app/repository/ProxyCacheRepository.ts

[warning] 26-28: app/repository/ProxyCacheRepository.ts#L26-L28
Added lines #L26 - L28 were not covered by tests

app/port/controller/ProxyCacheController.ts

[warning] 117-118: app/port/controller/ProxyCacheController.ts#L117-L118
Added lines #L117 - L118 were not covered by tests

app/core/entity/Task.ts

[warning] 258-259: app/core/entity/Task.ts#L258-L259
Added lines #L258 - L259 were not covered by tests

app/core/service/ProxyCacheService.ts

[warning] 56-57: app/core/service/ProxyCacheService.ts#L56-L57
Added lines #L56 - L57 were not covered by tests


[warning] 132-139: app/core/service/ProxyCacheService.ts#L132-L139
Added lines #L132 - L139 were not covered by tests


[warning] 153-157: app/core/service/ProxyCacheService.ts#L153-L157
Added lines #L153 - L157 were not covered by tests


[warning] 177-177: app/core/service/ProxyCacheService.ts#L177
Added line #L177 was not covered by tests


[warning] 248-250: app/core/service/ProxyCacheService.ts#L248-L250
Added lines #L248 - L250 were not covered by tests

Biome
app/core/entity/Task.ts

[error] 275-276: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

Additional comments not posted (49)
app/common/constants.ts (3)

4-4: Addition of PROXY_CACHE_DIR_NAME looks good.

The constant is correctly defined and follows the naming conventions.


5-5: Addition of ABBREVIATED_META_TYPE looks good.

The constant is correctly defined and follows the naming conventions.


11-11: Update to SyncMode enum looks good.

The addition of the proxy value is consistent with the existing enum values and aligns with the new proxy mode functionality.

test/schedule/CheckProxyCacheUpdateWorker.test.ts (2)

1-8: Addition of imports looks good.

The imports are correctly added and necessary for the new test case.


12-26: Addition of test case looks good.

The test case is well-defined and covers the necessary functionality. It uses appropriate mocking and assertions.

app/core/entity/ProxyCache.ts (4)

1-5: Addition of imports looks good.

The imports are correctly added and necessary for the ProxyCache entity.


6-10: Addition of ProxyCacheData interface looks good.

The interface is well-defined and includes all necessary properties. It is consistent with the existing data structures.


12-12: Addition of CreateProxyCacheData type looks good.

The type is well-defined and includes all necessary properties. It is consistent with the existing data structures.


14-42: Addition of ProxyCache class looks good.

The class is well-defined and includes all necessary properties and methods. It is consistent with the existing entity structures.

test/schedule/SyncProxyCacheWorker.test.ts (2)

1-6: Addition of imports looks good.

The imports are correctly added and necessary for the new test case.


10-35: Addition of test case looks good.

The test case is well-defined and covers the necessary functionality. It uses appropriate mocking and assertions.

app/port/schedule/CheckRecentlyUpdatedPackages.ts (1)

33-33: LGTM! Ensures correct behavior in proxy mode.

Adding SyncMode.proxy to the notAllowUpdateModeList ensures that the check for recently updated packages does not run in proxy mode, which is appropriate as proxy mode fetches data directly from the upstream repository.

app/common/adapter/NPMRegistry.ts (1)

114-114: LGTM! Necessary for proxy mode functionality.

Making the genAuthorizationHeader method public allows it to be accessed from outside the class, which is necessary to support the new proxy mode functionality that requires setting authorization headers for upstream requests.

app/port/controller/package/ShowPackageController.ts (5)

15-18: LGTM! Imports necessary for proxy mode.

The imports for ABBREVIATED_META_TYPE, SyncMode, ProxyCacheService, and calculateIntegrity are necessary for handling proxy mode in the show method.


26-27: LGTM! Injecting ProxyCacheService.

Injecting the ProxyCacheService is necessary for handling proxy mode in the show method.


38-38: LGTM! Determine if full manifests are requested.

The isFullManifests variable determines if full manifests are requested based on the Accept header.


72-79: LGTM! Handling proxy mode in the show method.

The changes handle proxy mode by fetching package manifests from the proxy cache service. This ensures that the system can return cached manifests or fetch them from the upstream repository if not found.


81-86: LGTM! Handling sync mode in the show method.

The changes handle sync mode by listing package manifests using the package manager service.

app/port/controller/ProxyCacheController.ts (6)

1-23: LGTM! Imports and class definition for ProxyCacheController.

The imports and class definition for ProxyCacheController are necessary for handling HTTP requests for managing proxy caches.


24-48: LGTM! Method for listing proxy caches.

The listProxyCache method lists proxy caches with pagination. It includes appropriate error handling for when proxy mode is not enabled.


50-63: LGTM! Method for showing proxy caches.

The showProxyCaches method shows proxy caches for a specific package with pagination. It includes appropriate error handling for when proxy mode is not enabled.


65-97: LGTM! Method for refreshing proxy caches.

The refreshProxyCaches method refreshes proxy caches for a specific package. It includes appropriate error handling for when proxy mode is not enabled and when no caches are found.


99-127: LGTM! Method for removing proxy caches.

The removeProxyCaches method removes proxy caches for a specific package. It includes appropriate error handling for when proxy mode is not enabled, when the user is not an admin, and when no caches are found.

Tools
GitHub Check: codecov/patch

[warning] 117-118: app/port/controller/ProxyCacheController.ts#L117-L118
Added lines #L117 - L118 were not covered by tests


129-155: LGTM! Method for truncating proxy caches.

The truncateProxyCaches method truncates all proxy caches. It includes appropriate error handling for when proxy mode is not enabled and when the user is not an admin.

app/port/controller/package/DownloadPackageVersionTar.ts (4)

15-19: New imports are necessary and correctly used.

The new imports SyncMode, ProxyCacheService, and PackageSyncerService are necessary for the added proxy mode functionality.


27-31: New injected services are necessary and correctly used.

The new injected services registryManagerService, proxyCacheService, and packageSyncerService are necessary for the added proxy mode functionality.


115-131: New method #getTgzStream is correctly implemented.

The new method correctly fetches the .tgz file from the upstream repository in proxy mode and creates a synchronization task.


66-79: New logic for handling proxy mode is correctly implemented.

The new logic correctly handles proxy mode when a package version is not found by fetching the .tgz file from the upstream repository and creating a synchronization task.

However, ensure that all function calls to download handle the new proxy mode logic correctly.

test/port/controller/ProxyCacheController/index.test.ts (7)

1-8: New imports are necessary and correctly used.

The new imports ProxyCache, ProxyCacheRepository, TaskRepository, and SyncMode are necessary for the added proxy cache functionality.


14-43: Setup code is correctly implemented.

The setup code correctly initializes proxyCacheRepository and saves the necessary proxy cache entries for the tests.


45-67: Test cases for listProxyCache are correctly implemented.

The test cases comprehensively cover the listProxyCache functionality, including different sync modes and pagination.


70-87: Test cases for showProxyCaches are correctly implemented.

The test cases comprehensively cover the showProxyCaches functionality, including different sync modes and not found scenarios.


90-115: Test cases for refreshProxyCaches are correctly implemented.

The test cases comprehensively cover the refreshProxyCaches functionality, including different sync modes and task creation.


118-157: Test cases for removeProxyCaches are correctly implemented.

The test cases comprehensively cover the removeProxyCaches functionality, including different sync modes, authorization, and not found scenarios.


161-189: Test cases for truncateProxyCaches are correctly implemented.

The test cases comprehensively cover the truncateProxyCaches functionality, including different sync modes and authorization.

app/core/entity/Task.ts (1)

Line range hint 47-76:
New types are necessary and correctly defined.

The new types UpdateProxyCacheTaskOptions and CreateUpdateProxyCacheTaskData are necessary for the new proxy cache functionality and are correctly defined.

test/core/service/ProxyCacheService.test.ts (9)

1-9: New imports are necessary and correctly used.

The new imports ProxyCacheService, ProxyCacheRepository, TaskService, and DIST_NAMES are necessary for the added proxy cache functionality.


12-18: Setup code is correctly implemented.

The setup code correctly initializes proxyCacheService and proxyCacheRepository for the tests.


20-51: Test cases for getPackageManifest are correctly implemented.

The test cases comprehensively cover the getPackageManifest functionality, including fetching from the upstream repository and reading from NFS.


54-88: Test cases for getPackageVersionManifest are correctly implemented.

The test cases comprehensively cover the getPackageVersionManifest functionality, including fetching from the upstream repository and reading from NFS.


90-114: Test cases for getPackageVersionManifest are correctly implemented.

The test cases comprehensively cover the getPackageVersionManifest functionality, including fetching the correct version via tag and caching the package manifest.


117-195: Test cases for rewriteManifestAndStore are correctly implemented.

The test cases comprehensively cover the rewriteManifestAndStore functionality, including fetching full and abbreviated package manifests from the upstream repository.


199-219: Test cases for removeProxyCache are correctly implemented.

The test cases comprehensively cover the removeProxyCache functionality, including removing cached entries from the repository.


222-235: Test cases for createTask and findExecuteTask are correctly implemented.

The test cases comprehensively cover the createTask and findExecuteTask functionality, including creating tasks and finding them in the repository.


238-275: Test cases for executeTask are correctly implemented.

The test cases comprehensively cover the executeTask functionality, including handling not found errors and successful updates.

However, ensure that the tests also cover scenarios where the upstream registry returns errors or non-200 status codes.

Verification successful

Test cases for executeTask cover scenarios with upstream registry errors.

The test cases in test/core/service/PackageSyncerService/executeTask.test.ts include handling non-200 status codes, confirming that error scenarios from the upstream registry are tested.

  • test/core/service/PackageSyncerService/executeTask.test.ts
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for error handling in `getSourceManifestAndCache`.

# Test: Search for test cases handling non-200 status codes. Expect: At least one test case covering these scenarios.
rg --type typescript -A 50 $'getSourceManifestAndCache' | grep "status !== 200"

Length of output: 113


Script:

#!/bin/bash
# Description: Verify test coverage for error handling in `getSourceManifestAndCache`.

# Test: Search for test cases handling non-200 status codes. Expect: At least one test case covering these scenarios.
rg --type ts -A 50 'getSourceManifestAndCache' | grep -A 10 -B 10 'status !== 200'

Length of output: 82


Script:

#!/bin/bash
# Description: Verify test coverage for error handling in `getSourceManifestAndCache`.

# Step 1: Search for any HTTP status code checks within the `getSourceManifestAndCache` function.
rg --type ts -A 50 'getSourceManifestAndCache' | grep -E 'status [!=<>]'

# Step 2: Identify related test cases that handle these scenarios.
rg --type ts -A 50 'status [!=<>]'

Length of output: 320634

app/core/service/ProxyCacheService.ts (3)

110-112: LGTM!

The method is correctly implemented.


114-116: LGTM!

The method is correctly implemented.


211-235: LGTM!

The method is correctly implemented.

test/port/controller/package/ShowPackageController.test.ts (1)

868-883: LGTM!

The new test case for reading the manifest from the source in proxy mode is well-written and covers the necessary assertions.

Comment on lines +45 to +46
this.logger.error(err);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests to cover error handling.

The error handling block in the subscribe method lacks test coverage. This is crucial for ensuring that the system behaves as expected under failure conditions.

Would you like me to help by adding some test cases for this scenario?

Tools
GitHub Check: codecov/patch

[warning] 45-46: app/port/schedule/CheckProxyCacheUpdateWorker.ts#L45-L46
Added lines #L45 - L46 were not covered by tests

Comment on lines +72 to +79
if (this.config.cnpmcore.syncMode === SyncMode.proxy) {
// proxy mode
const fileType = isFullManifests ? DIST_NAMES.FULL_MANIFESTS : DIST_NAMES.ABBREVIATED_MANIFESTS;
const pkgManifest = await this.proxyCacheService.getPackageManifest(fullname, fileType);

const nfsBytes = Buffer.from(JSON.stringify(pkgManifest));
const { shasum: etag } = await calculateIntegrity(nfsBytes);
result = { data: pkgManifest, etag, blockReason: '' };
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests for the show method in proxy mode.

Add unit tests to cover the error handling and cache refresh task creation in the show method when in proxy mode.

Do you want me to help by adding some test cases for this scenario?

Also applies to: 81-86

Comment on lines +43 to +46
this.logger.info('[SyncProxyCacheWorker:subscribe:executeTask] current sync task count %s, exceed max concurrent tasks %s',
executingCount, this.config.cnpmcore.syncPackageWorkerMaxConcurrentTasks);
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests to cover the concurrency control logic.

The concurrency control logic in the subscribe method lacks test coverage. This is critical for ensuring that the system correctly handles multiple concurrent tasks.

Would you like me to help by adding some test cases for this scenario?

Tools
GitHub Check: codecov/patch

[warning] 43-46: app/port/schedule/SyncProxyCacheWorker.ts#L43-L46
Added lines #L43 - L46 were not covered by tests

task = await this.proxyCacheService.findExecuteTask();
}
} catch (err) {
this.logger.error('[SyncProxyCacheWorker:subscribe:executeTask:error][%s] %s', executingCount, err);
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests to cover error handling in the subscribe method.

The error handling in the subscribe method lacks test coverage. It's important to ensure that the system behaves as expected under failure conditions.

Would you like me to help by adding some test cases for this scenario?

Tools
GitHub Check: codecov/patch

[warning] 51-51: app/port/schedule/SyncProxyCacheWorker.ts#L51
Added line #L51 was not covered by tests

Comment on lines +117 to +118
throw new NotFoundError();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests for the removeProxyCaches method.

The removeProxyCaches method lacks test coverage for the scenario where no caches are found.

Do you want me to help by adding some test cases for this scenario?

Tools
GitHub Check: codecov/patch

[warning] 117-118: app/port/controller/ProxyCacheController.ts#L117-L118
Added lines #L117 - L118 were not covered by tests

app/core/service/ProxyCacheService.ts Outdated Show resolved Hide resolved
app/core/service/ProxyCacheService.ts Outdated Show resolved Hide resolved
app/core/service/ProxyCacheService.ts Outdated Show resolved Hide resolved
Comment on lines +373 to +387
it('should read package version manifest from source in proxy mode', async () => {
mock(app.config.cnpmcore, 'syncMode', SyncMode.proxy);
mock(app.config.cnpmcore, 'redirectNotFound', false);
const data = await TestUtil.readJSONFile(TestUtil.getFixtures('registry.npmjs.org/foobar/1.0.0/abbreviated.json'));
app.mockHttpclient('https://registry.npmjs.org/foobar/1.0.0', 'GET', {
data,
persist: false,
});
const res = await app.httpRequest()
.get('/foobar/1.0.0')
.set('user-agent', publisher.ua + ' node/16.0.0')
.set('Accept', 'application/vnd.npm.install-v1+json');
assert(res.status === 200);
assert(res.body.dist.tarball.includes(app.config.cnpmcore.registry));
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure comprehensive test coverage for proxy mode.

Currently, only one test case covers the proxy mode scenario. To ensure robustness, consider adding more tests for edge cases and error scenarios.

  • Add tests for different response statuses from the upstream registry.
  • Include tests for network failures or timeouts.
  • Verify behavior with various package version formats and invalid inputs.

Do you want me to generate additional test cases or open a GitHub issue to track this task?

Copy link
Contributor

@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: 0

Outside diff range, codebase verification and nitpick comments (8)
app/port/controller/package/DownloadPackageVersionTar.ts (1)

115-131: New getTgzProxyStream method looks good.

The method encapsulates the logic for obtaining the tar stream from the proxy cache service. The logic is correct and follows best practices.

Consider adding logging for background task.

Adding logging statements can help in tracking the start and successful completion of the background task.

+    ctx.logger.info('[DownloadPackageVersionTarController.createSyncTask:start] taskId: %s, fullname: %s', task.taskId, fullname);
test/core/service/ProxyCacheService.test.ts (2)

216-238: Test cases for removeProxyCache look good.

The test cases cover scenarios such as removing cache and follow best practices.

Consider adding logging for cache removals.

Adding logging statements can help in tracking successful cache removals and diagnosing issues.

+    this.logger.info(`Successfully removed cache for ${fullname} with fileType ${fileType}`);

271-316: Test cases for executeTask look good.

The test cases cover scenarios such as throwing not found error and updating success. They follow best practices.

Consider adding logging for task execution.

Adding logging statements can help in tracking the start and successful completion of tasks.

+    this.logger.info(`[ProxyCacheService.executeTask:start] taskId: ${task.taskId}, targetName: ${task.targetName}`);
+    this.logger.info(`[ProxyCacheService.executeTask:success] taskId: ${task.taskId}, targetName: ${task.targetName}`);
app/core/service/ProxyCacheService.ts (5)

61-76: getPackageManifest method looks good.

The method retrieves the package manifest from NFS or rewrites and stores it. The logic is correct and follows best practices.

Use runInBackground for the background task.

Using runInBackground can improve readability and maintainability.

-    this.backgroundTaskHelper.run(async () => {
+    this.backgroundTaskHelper.runInBackground(async () => {

79-100: getPackageVersionManifest method looks good.

The method retrieves the package version manifest from NFS or rewrites and stores it. The logic is correct and follows best practices.

Use runInBackground for the background task.

Using runInBackground can improve readability and maintainability.

-    this.backgroundTaskHelper.run(async () => {
+    this.backgroundTaskHelper.runInBackground(async () => {

102-108: removeProxyCache method looks good.

The method removes the proxy cache from NFS and the repository. The logic is correct and follows best practices.

Consider adding logging for cache removals.

Adding logging statements can help in tracking successful cache removals and diagnosing issues.

+    this.logger.info(`Successfully removed cache for ${fullname} with fileType ${fileType}`);

118-149: executeTask method looks good.

The method executes a task, updates the cache, and logs the results. The logic is correct and follows best practices.

Consider adding logging for task execution.

Adding logging statements can help in tracking the start and successful completion of tasks.

+    this.logger.info(`[ProxyCacheService.executeTask:start] taskId: ${task.taskId}, targetName: ${task.targetName}`);
+    this.logger.info(`[ProxyCacheService.executeTask:success] taskId: ${task.taskId}, targetName: ${task.targetName}`);

151-199: rewriteManifestAndStore method looks good.

The method rewrites the manifest and stores it in NFS. The logic is correct and follows best practices.

Consider adding validation for upstream responses.

Adding a check to ensure that the response from the upstream source is valid (e.g., checking for a 200 status code) before proceeding with the rewrite and storage of the manifest can improve robustness.

+  if (responseResult.status !== 200) {
+    throw new Error('Invalid response from upstream source');
+  }
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between fe1c16c and 19b348d.

Files selected for processing (4)
  • app/core/service/ProxyCacheService.ts (1 hunks)
  • app/port/controller/package/DownloadPackageVersionTar.ts (3 hunks)
  • test/core/service/ProxyCacheService.test.ts (1 hunks)
  • test/port/controller/package/DownloadPackageVersionTarController.test.ts (2 hunks)
Additional context used
Learnings (2)
app/core/service/ProxyCacheService.ts (1)
Learnt from: hezhengxu2018
PR: cnpm/cnpmcore#571
File: app/core/service/ProxyCacheService.ts:161-209
Timestamp: 2024-07-19T06:26:05.533Z
Learning: In the `ProxyCacheService` class, requests to the upstream repository already throw an error if they fail, making additional checks for the response status unnecessary.
test/port/controller/package/DownloadPackageVersionTarController.test.ts (1)
Learnt from: hezhengxu2018
PR: cnpm/cnpmcore#571
File: test/port/controller/package/DownloadPackageVersionTarController.test.ts:304-315
Timestamp: 2024-07-19T06:21:32.040Z
Learning: In proxy mode, the response status should be 200 when the package version is not found.
Additional comments not posted (15)
app/port/controller/package/DownloadPackageVersionTar.ts (3)

18-20: Imports look good.

The new import statements for ProxyCacheService, PackageSyncerService, and RegistryManagerService are appropriate.


27-31: Injected services look good.

The new injected services registryManagerService, proxyCacheService, and packageSyncerService are appropriate.


66-79: Modifications to the download method look good.

The modifications improve error handling and add support for proxy mode. The logic is correct and follows best practices.

test/core/service/ProxyCacheService.test.ts (6)

15-18: Setup in beforeEach looks good.

The initialization of proxyCacheService and proxyCacheRepository is appropriate.


20-29: Test cases for getPackageVersionTarResponse look good.

The test cases cover scenarios such as hitting the block list and follow best practices.


32-63: Test cases for getPackageManifest look good.

The test cases cover scenarios such as invoking rewriteManifestAndStore and reading data from NFS. They follow best practices.


66-127: Test cases for getPackageVersionManifest look good.

The test cases cover scenarios such as invoking rewriteManifestAndStore, reading data from NFS, and getting the correct version via tag. They follow best practices.


130-213: Test cases for rewriteManifestAndStore look good.

The test cases cover scenarios such as getting full package manifest, abbreviated package manifest, full package version manifest, and abbreviated package version manifest. They follow best practices.


241-268: Test cases for createTask and findExecuteTask look good.

The test cases cover scenarios such as creating a task and finding an executed task. They follow best practices.

app/core/service/ProxyCacheService.ts (5)

54-59: getPackageVersionTarResponse method looks good.

The method checks if the package is in the block list and retrieves the proxy response. The logic is correct and follows best practices.


110-112: createTask method looks good.

The method creates a task using the taskService. The logic is correct and follows best practices.


114-116: findExecuteTask method looks good.

The method finds an execute task using the taskService. The logic is correct and follows best practices.


201-225: getProxyResponse method looks good.

The method retrieves the proxy response from the upstream registry. The logic is correct and follows best practices.


228-244: getUpstream* methods look good.

The methods retrieve various manifests from the upstream registry. The logic is correct and follows best practices.

test/port/controller/package/DownloadPackageVersionTarController.test.ts (1)

304-320: Ensure comprehensive test coverage.

The new test case for proxy mode is well-structured and covers the creation of a synchronization task. However, consider adding assertions to verify the content of the response body and the behavior of the synchronization task.

+      assert(res.body.message.includes('task created'));

Copy link
Contributor

@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: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 19b348d and f2c84ed.

Files selected for processing (1)
  • test/port/controller/package/DownloadPackageVersionTarController.test.ts (2 hunks)
Files skipped from review as they are similar to previous changes (1)
  • test/port/controller/package/DownloadPackageVersionTarController.test.ts

Copy link
Contributor

@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: 2

Outside diff range, codebase verification and nitpick comments (6)
app/core/service/ProxyCacheService.ts (6)

54-59: Consider adding logging for block list check.

Adding a logging statement before throwing the ForbiddenError can help in diagnosing issues related to blocked packages.

+    if (this.config.cnpmcore.syncPackageBlockList.includes(fullname)) {
+      this.logger.info(`Package ${fullname} is blocked by the block list`);
       throw new ForbiddenError(`stop proxy by block list: ${JSON.stringify(this.config.cnpmcore.syncPackageBlockList)}`);
    }

104-110: Consider adding logging for cache removal.

Adding logging statements can help in tracking successful cache removals and diagnosing issues.

    await this.nfsAdapter.remove(storeKey);
    await this.proxyCacheRepository.removeProxyCache(fullname, fileType, version);
+    this.logger.info(`Successfully removed cache for ${fullname} with fileType ${fileType}`);

120-152: Consider adding logging for task execution.

Adding logging statements for the start and success of the task can help in tracking task execution and diagnosing issues.

    logs.push(`[${isoNow()}] 🚧🚧🚧🚧🚧 Start update "${fullname}-${fileType}" 🚧🚧🚧🚧🚧`);
+    this.logger.info(`Starting task execution for ${fullname} with fileType ${fileType}`);
    try {
      const cachedFiles = await this.proxyCacheRepository.findProxyCache(fullname, fileType);
      if (!cachedFiles) throw new Error('task params error, can not found record in repo.');
      cachedManifest = await this.getRewrittenManifest<typeof fileType>(fullname, fileType);
      await this.storeRewrittenManifest(cachedManifest, fullname, fileType);
      ProxyCache.update(cachedFiles);
      await this.proxyCacheRepository.saveProxyCache(cachedFiles);
    } catch (error) {
      task.error = error;
      logs.push(`[${isoNow()}] ❌ ${task.error}`);
      logs.push(`[${isoNow()}] ❌❌❌❌❌ ${fullname}-${fileType} ${version ?? ''} ❌❌❌❌❌`);
      await this.taskService.finishTask(task, TaskState.Fail, logs.join('\n'));
      this.logger.info('[ProxyCacheService.executeTask:fail] taskId: %s, targetName: %s, %s',
        task.taskId, task.targetName, task.error);
      return;
    }
    logs.push(`[${isoNow()}] 🟢 Update Success.`);
+    this.logger.info(`Successfully executed task for ${fullname} with fileType ${fileType}`);
    const isFullManifests = fileType === DIST_NAMES.FULL_MANIFESTS;
    const cachedKey = await this.cacheService.getPackageEtag(fullname, isFullManifests);
    if (cachedKey) {
      const cacheBytes = Buffer.from(JSON.stringify(cachedManifest));
      const { shasum: etag } = await calculateIntegrity(cacheBytes);
      await this.cacheService.savePackageEtagAndManifests(fullname, isFullManifests, etag, cacheBytes);
      logs.push(`[${isoNow()}] 🟢 Update Cache Success.`);
    }
    await this.taskService.finishTask(task, TaskState.Success, logs.join('\n'));

154-193: Consider adding logging for manifest retrieval.

Adding logging statements for the start and success of the manifest retrieval can help in tracking the process and diagnosing issues.

    switch (fileType) {
      case DIST_NAMES.FULL_MANIFESTS:
+        this.logger.info(`Retrieving full manifests for ${fullname}`);
        responseResult = await this.getUpstreamFullManifests(fullname);
        break;
      case DIST_NAMES.ABBREVIATED_MANIFESTS:
+        this.logger.info(`Retrieving abbreviated manifests for ${fullname}`);
        responseResult = await this.getUpstreamAbbreviatedManifests(fullname);
        break;
      case DIST_NAMES.MANIFEST:
+        this.logger.info(`Retrieving package version manifest for ${fullname} with version ${versionOrTag}`);
        responseResult = await this.getUpstreamPackageVersionManifest(fullname, versionOrTag!);
        break;
      case DIST_NAMES.ABBREVIATED:
+        this.logger.info(`Retrieving abbreviated package version manifest for ${fullname} with version ${versionOrTag}`);
        responseResult = await this.getUpstreamAbbreviatedPackageVersionManifest(fullname, versionOrTag!);
        break;
      default:
        break;
    }
    // replace tarball url
    const manifest = responseResult.data;
    const { sourceRegistry, registry } = this.config.cnpmcore;
    if (isPkgManifest(fileType)) {
      // pkg manifest
      const versionMap = manifest.versions || {};
      for (const key in versionMap) {
        const versionItem = versionMap[key];
        if (versionItem?.dist?.tarball) {
          versionItem.dist.tarball = versionItem.dist.tarball.replace(sourceRegistry, registry);
        }
      }
    } else {
      // pkg version manifest
      const distItem = manifest.dist || {};
      if (distItem.tarball) {
        distItem.tarball = distItem.tarball.replace(sourceRegistry, registry);
      }
    }
+    this.logger.info(`Successfully retrieved and rewrote manifest for ${fullname}`);
    return manifest;

195-205: Consider adding logging for manifest storage.

Adding logging statements for the start and success of the storage process can help in tracking the process and diagnosing issues.

    let storeKey: string;
    if (isPkgManifest(fileType)) {
      storeKey = `/${PROXY_CACHE_DIR_NAME}/${fullname}/${fileType}`;
    } else {
      const version = manifest.version;
      storeKey = `/${PROXY_CACHE_DIR_NAME}/${fullname}/${version}/${fileType}`;
    }
+    this.logger.info(`Storing rewritten manifest for ${fullname} with fileType ${fileType}`);
    const nfsBytes = Buffer.from(JSON.stringify(manifest));
    await this.nfsAdapter.uploadBytes(storeKey, nfsBytes);
+    this.logger.info(`Successfully stored rewritten manifest for ${fullname} with fileType ${fileType}`);

207-231: Consider adding logging for proxy response retrieval.

Adding logging statements for the start and success of the proxy response retrieval can help in tracking the process and diagnosing issues.

    const registry = this.npmRegistry.registry;
    const remoteAuthToken = await this.registryManagerService.getAuthTokenByRegistryHost(registry);
    const authorization = this.npmRegistry.genAuthorizationHeader(remoteAuthToken);

    const url = `${this.npmRegistry.registry}${ctx.url}`;

+    this.logger.info(`Retrieving proxy response for ${url}`);
    const res = await this.httpclient.request(url, {
      timing: true,
      followRedirect: true,
      retry: 3,
      dataType: 'stream',
      timeout: 10000,
      compressed: true,
      ...options,
      headers: {
        accept: ctx.header?.accept,
        'user-agent': ctx.header?.['user-agent'],
        authorization,
        'x-forwarded-for': ctx?.ip,
        via: `1.1, ${this.config.cnpmcore.registry}`,
      },
    }) as HttpClientResponse;
    this.logger.info('[ProxyCacheService:getProxyStreamResponse] %s, status: %s', url, res.status);
+    this.logger.info(`Successfully retrieved proxy response for ${url}`);
    return res;
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f2c84ed and 732be72.

Files selected for processing (2)
  • app/core/service/ProxyCacheService.ts (1 hunks)
  • test/core/service/ProxyCacheService.test.ts (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • test/core/service/ProxyCacheService.test.ts
Additional context used
Learnings (1)
app/core/service/ProxyCacheService.ts (1)
Learnt from: hezhengxu2018
PR: cnpm/cnpmcore#571
File: app/core/service/ProxyCacheService.ts:161-209
Timestamp: 2024-07-19T06:26:05.533Z
Learning: In the `ProxyCacheService` class, requests to the upstream repository already throw an error if they fail, making additional checks for the response status unnecessary.
Additional comments not posted (4)
app/core/service/ProxyCacheService.ts (4)

1-27: Imports and utility functions look good!

The imports and utility functions (isoNow, isPkgManifest) are appropriate and correctly implemented.


112-114: Method looks good!

The createTask method is correctly implemented.


116-118: Method looks good!

The findExecuteTask method is correctly implemented.


234-250: Methods look good!

The private methods for retrieving upstream manifests are correctly implemented.

Comment on lines +79 to +102
// used by GET /:fullname/:versionOrTag
async getPackageVersionManifest(fullname: string, fileType: DIST_NAMES.ABBREVIATED | DIST_NAMES.MANIFEST, versionOrTag: string): Promise<AbbreviatedPackageJSONType|PackageJSONType> {
let version;
if (semverValid(versionOrTag)) {
version = versionOrTag;
} else {
const pkgManifest = await this.getPackageManifest(fullname, DIST_NAMES.ABBREVIATED_MANIFESTS);
const distTags = pkgManifest['dist-tags'] || {};
version = distTags[versionOrTag] ? distTags[versionOrTag] : versionOrTag;
}
const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType, version))?.filePath;
if (cachedStoreKey) {
const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey);
const nfsString = Buffer.from(nfsBytes!).toString();
return JSON.parse(nfsString) as PackageJSONType | AbbreviatedPackageJSONType;
}
const manifest = await this.getRewrittenManifest(fullname, fileType, versionOrTag);
this.backgroundTaskHelper.run(async () => {
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType, version });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
});
return manifest;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error handling and logging.

Consider adding error handling for the background task and logging the start and success of the task.

    const manifest = await this.getRewrittenManifest(fullname, fileType, versionOrTag);
    this.backgroundTaskHelper.run(async () => {
+      try {
+        this.logger.info(`Starting background task to store manifest for ${fullname}`);
        await this.storeRewrittenManifest(manifest, fullname, fileType);
        const cachedFiles = ProxyCache.create({ fullname, fileType, version });
        await this.proxyCacheRepository.saveProxyCache(cachedFiles);
+        this.logger.info(`Successfully stored manifest for ${fullname}`);
+      } catch (error) {
+        this.logger.error(`Failed to store manifest for ${fullname}: ${error.message}`);
+      }
    });
    return manifest;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// used by GET /:fullname/:versionOrTag
async getPackageVersionManifest(fullname: string, fileType: DIST_NAMES.ABBREVIATED | DIST_NAMES.MANIFEST, versionOrTag: string): Promise<AbbreviatedPackageJSONType|PackageJSONType> {
let version;
if (semverValid(versionOrTag)) {
version = versionOrTag;
} else {
const pkgManifest = await this.getPackageManifest(fullname, DIST_NAMES.ABBREVIATED_MANIFESTS);
const distTags = pkgManifest['dist-tags'] || {};
version = distTags[versionOrTag] ? distTags[versionOrTag] : versionOrTag;
}
const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType, version))?.filePath;
if (cachedStoreKey) {
const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey);
const nfsString = Buffer.from(nfsBytes!).toString();
return JSON.parse(nfsString) as PackageJSONType | AbbreviatedPackageJSONType;
}
const manifest = await this.getRewrittenManifest(fullname, fileType, versionOrTag);
this.backgroundTaskHelper.run(async () => {
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType, version });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
});
return manifest;
}
const manifest = await this.getRewrittenManifest(fullname, fileType, versionOrTag);
this.backgroundTaskHelper.run(async () => {
try {
this.logger.info(`Starting background task to store manifest for ${fullname}`);
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType, version });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
this.logger.info(`Successfully stored manifest for ${fullname}`);
} catch (error) {
this.logger.error(`Failed to store manifest for ${fullname}: ${error.message}`);
}
});
return manifest;

Comment on lines +61 to +77
async getPackageManifest(fullname: string, fileType: DIST_NAMES.FULL_MANIFESTS| DIST_NAMES.ABBREVIATED_MANIFESTS): Promise<AbbreviatedPackageManifestType|PackageManifestType> {
const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType))?.filePath;
if (cachedStoreKey) {
const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey);
const nfsString = Buffer.from(nfsBytes!).toString();
const nfsPkgManifgest = JSON.parse(nfsString);
return nfsPkgManifgest;
}

const manifest = await this.getRewrittenManifest<typeof fileType>(fullname, fileType);
this.backgroundTaskHelper.run(async () => {
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
});
return manifest;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error handling and logging.

Consider adding error handling for the background task and logging the start and success of the task.

    const manifest = await this.getRewrittenManifest<typeof fileType>(fullname, fileType);
    this.backgroundTaskHelper.run(async () => {
+      try {
+        this.logger.info(`Starting background task to store manifest for ${fullname}`);
        await this.storeRewrittenManifest(manifest, fullname, fileType);
        const cachedFiles = ProxyCache.create({ fullname, fileType });
        await this.proxyCacheRepository.saveProxyCache(cachedFiles);
+        this.logger.info(`Successfully stored manifest for ${fullname}`);
+      } catch (error) {
+        this.logger.error(`Failed to store manifest for ${fullname}: ${error.message}`);
+      }
    });
    return manifest;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getPackageManifest(fullname: string, fileType: DIST_NAMES.FULL_MANIFESTS| DIST_NAMES.ABBREVIATED_MANIFESTS): Promise<AbbreviatedPackageManifestType|PackageManifestType> {
const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType))?.filePath;
if (cachedStoreKey) {
const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey);
const nfsString = Buffer.from(nfsBytes!).toString();
const nfsPkgManifgest = JSON.parse(nfsString);
return nfsPkgManifgest;
}
const manifest = await this.getRewrittenManifest<typeof fileType>(fullname, fileType);
this.backgroundTaskHelper.run(async () => {
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
});
return manifest;
}
async getPackageManifest(fullname: string, fileType: DIST_NAMES.FULL_MANIFESTS| DIST_NAMES.ABBREVIATED_MANIFESTS): Promise<AbbreviatedPackageManifestType|PackageManifestType> {
const cachedStoreKey = (await this.proxyCacheRepository.findProxyCache(fullname, fileType))?.filePath;
if (cachedStoreKey) {
const nfsBytes = await this.nfsAdapter.getBytes(cachedStoreKey);
const nfsString = Buffer.from(nfsBytes!).toString();
const nfsPkgManifgest = JSON.parse(nfsString);
return nfsPkgManifgest;
}
const manifest = await this.getRewrittenManifest<typeof fileType>(fullname, fileType);
this.backgroundTaskHelper.run(async () => {
try {
this.logger.info(`Starting background task to store manifest for ${fullname}`);
await this.storeRewrittenManifest(manifest, fullname, fileType);
const cachedFiles = ProxyCache.create({ fullname, fileType });
await this.proxyCacheRepository.saveProxyCache(cachedFiles);
this.logger.info(`Successfully stored manifest for ${fullname}`);
} catch (error) {
this.logger.error(`Failed to store manifest for ${fullname}: ${error.message}`);
}
});
return manifest;
}

Copy link
Contributor

@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: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 732be72 and 63d610b.

Files selected for processing (1)
  • app/port/controller/package/DownloadPackageVersionTar.ts (4 hunks)
Additional context used
Learnings (1)
app/port/controller/package/DownloadPackageVersionTar.ts (1)
Learnt from: hezhengxu2018
PR: cnpm/cnpmcore#571
File: test/port/controller/package/DownloadPackageVersionTarController.test.ts:304-315
Timestamp: 2024-07-19T06:21:32.040Z
Learning: In proxy mode, the response status should be 200 when the package version is not found.
Additional comments not posted (2)
app/port/controller/package/DownloadPackageVersionTar.ts (2)

68-87: LGTM! The changes enhance the package download handling in proxy mode.

The key aspects of the changes are:

  • If the package version is not found and the sync mode is proxy, it tries to get the package version from the proxy stream using the new getTgzProxyStream method.
  • It uses the PassThrough stream and pipeline function to stream the package data, improving performance and responsiveness.
  • The error handling logic is correct and aligns with the proxy mode requirements.

123-139: LGTM!

The new getTgzProxyStream method is well-structured and follows the single responsibility principle. It handles the proxy response and initiates the sync task correctly. The logging statement provides useful information for debugging.

Copy link
Contributor

@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: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 63d610b and ce71b9f.

Files selected for processing (1)
  • app/port/controller/package/DownloadPackageVersionTar.ts (4 hunks)
Additional context used
Learnings (1)
app/port/controller/package/DownloadPackageVersionTar.ts (1)
Learnt from: hezhengxu2018
PR: cnpm/cnpmcore#571
File: test/port/controller/package/DownloadPackageVersionTarController.test.ts:304-315
Timestamp: 2024-07-19T06:21:32.040Z
Learning: In proxy mode, the response status should be 200 when the package version is not found.
Additional comments not posted (3)
app/port/controller/package/DownloadPackageVersionTar.ts (3)

67-83: Review the error handling and proxy mode logic in the download method.

The method has a complex flow that handles different scenarios based on the sync mode. Here are a few observations and suggestions:

  1. Error Handling: The method catches errors from getPackageEntityByFullname and getPackageVersionEntity. It's crucial to ensure that these errors are logged for better traceability.
  2. Proxy Mode Logic: In proxy mode, if a package version is not found, the method attempts to fetch it using getTgzProxyStream. This is a critical part of the functionality and should be thoroughly tested to ensure it handles all edge cases correctly.
  3. Stream Handling: The use of PassThrough stream is appropriate here as it allows for modifying the stream data if necessary before piping it to the response. However, ensure that the stream is properly managed to avoid memory leaks or hanging requests.

Consider adding more detailed logging at each step of the process to help with debugging and maintaining the system. Also, ensure that unit tests cover all branches of this method, particularly the new proxy mode logic.


119-135: Review the implementation of getTgzProxyStream method.

This method is key to handling proxy mode effectively. Here are some points to consider:

  1. Response Handling: The method sets the response status and headers based on the upstream response, which is crucial for client transparency. Ensure that all relevant headers are correctly forwarded to maintain session integrity and other necessary information.
  2. Background Task Creation: The method creates a synchronization task in the background. It's important to ensure that this task is created only when necessary and that it logs sufficient information for monitoring and debugging purposes.
  3. Stream Return: The method returns the response stream from the upstream service. Ensure that this stream is handled correctly in the calling method to avoid issues with stream corruption or leaks.

Verify that the headers manipulation and status setting are covered by unit tests to ensure they behave as expected under various scenarios. Also, check that the background task creation is robust and handles failures gracefully.


33-33: Approve the CORS settings in the downloadForOptions method.

The method correctly sets the necessary CORS headers to allow cross-origin requests, which is essential for web applications that might access this API from different domains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants