-
Notifications
You must be signed in to change notification settings - Fork 3
fix: enhance repository management with validation and pagination improvements #118
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
Conversation
WalkthroughAdds validation in updateRepository to reject empty descriptions; expands submitRepository to enforce unique URLs; and changes getActiveRepositories to a paginated interface with offset/limit and revised implementation and docs. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Registry as RepositoryRegistry
rect rgb(240,248,255)
note right of Client: Pagination flow for active repositories
Client->>Registry: getActiveRepositories(offset, limit)
Registry->>Registry: Collect all active repositories into temp list
alt offset >= totalActive
Registry-->>Client: []
else valid range
Registry->>Registry: Slice temp list [offset : offset+limit]
Registry-->>Client: Repository[] page
end
end
sequenceDiagram
autonumber
actor Client
participant Registry as RepositoryRegistry
rect rgb(245,255,240)
note right of Client: Submission with uniqueness checks
Client->>Registry: submitRepository(name, url, ...)
Registry->>Registry: Check name uniqueness
Registry->>Registry: Check URL uniqueness (new)
alt any duplicate
Registry-->>Client: revert Duplicate
else no duplicates
Registry->>Registry: Persist repository
Registry-->>Client: success
end
end
sequenceDiagram
autonumber
actor Client
participant Registry as RepositoryRegistry
rect rgb(255,250,240)
note right of Client: Update validation
Client->>Registry: updateRepository(id, description, ...)
Registry->>Registry: Validate description not empty (new)
alt empty description
Registry-->>Client: revert InvalidDescription
else valid
Registry->>Registry: Apply updates
Registry-->>Client: success
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
contracts/RepositoryRegistry.sol (2)
84-93
: Verify: Should inactive repositories block URL reuse?The duplicate URL check iterates over all repositories (including inactive ones). If a repository is deactivated, should its URL be available for reuse, or should it remain reserved?
Additionally, as the repository count grows, this O(n) loop will increase gas costs for submissions. Consider implementing a mapping-based approach for constant-time lookups.
Consider this pattern for efficient duplicate detection:
// Add at contract level: mapping(bytes32 => bool) private usedNameHashes; mapping(bytes32 => bool) private usedUrlHashes; // In submitRepository, replace the loop with: bytes32 nameHash = keccak256(abi.encodePacked(name)); bytes32 urlHash = keccak256(abi.encodePacked(url)); require(!usedNameHashes[nameHash], "Repository name already exists"); require(!usedUrlHashes[urlHash], "Repository URL already exists"); // After creating repository: usedNameHashes[nameHash] = true; usedUrlHashes[urlHash] = true; // In deactivateRepository, decide whether to allow reuse: // delete usedNameHashes[...]; // if reuse allowed // delete usedUrlHashes[...]; // if reuse allowed
176-203
: Consider gas optimization: pagination still scans all repositories.While pagination reduces the data returned, the function still iterates through all repositories twice (counting + collecting) regardless of offset/limit. For large registries, this can be gas-intensive even for view functions.
Consider these alternatives:
- If iteration order isn't critical, maintain a separate array of active repository IDs updated on submit/deactivate.
- Document that this function may become expensive and recommend using events to track active repositories off-chain.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
contracts/RepositoryRegistry.sol
(3 hunks)
🔇 Additional comments (2)
contracts/RepositoryRegistry.sol (2)
60-60
: LGTM! Good validation addition.The empty description check prevents meaningless updates and is appropriately placed before state modification.
175-175
: Verify external consumers of getActiveRepositories signature change. The function signature changed fromgetActiveRepositories()
togetActiveRepositories(uint256 offset, uint256 limit)
, which will break any external callers (other contracts, interfaces, off-chain scripts, UIs, subgraphs, etc.). No internal references were found in this codebase—please confirm all external consumers are updated to match the new signature.
function getActiveRepositories(uint256 offset, uint256 limit) external view returns (Repository[] memory) { | ||
// Gather all active repositories | ||
uint256 activeCount = 0; | ||
for (uint256 i = 1; i <= repoCounter; i++) { | ||
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | ||
activeCount++; | ||
} | ||
} | ||
|
||
// Create array with exact size needed | ||
Repository[] memory activeRepos = new Repository[](activeCount); | ||
|
||
// Second pass: populate the array | ||
Repository[] memory tempRepos = new Repository[](activeCount); | ||
uint256 index = 0; | ||
for (uint256 i = 1; i <= repoCounter; i++) { | ||
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | ||
activeRepos[index] = repositories[i]; | ||
tempRepos[index] = repositories[i]; | ||
index++; | ||
} | ||
} | ||
|
||
return activeRepos; | ||
// Pagination | ||
if (offset >= activeCount) { | ||
return new Repository[](0); | ||
} | ||
uint256 end = offset + limit; | ||
if (end > activeCount) { | ||
end = activeCount; | ||
} | ||
Repository[] memory pagedRepos = new Repository[](end - offset); | ||
for (uint256 i = offset; i < end; i++) { | ||
pagedRepos[i - offset] = tempRepos[i]; | ||
} | ||
return pagedRepos; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add input validation for pagination parameters.
The implementation correctly handles pagination logic, but missing input validation could lead to unexpected behavior:
limit = 0
would create a zero-length array, which while not breaking, may not be the intended behavior.- No upper bound on
limit
means callers could request excessively large arrays.
Apply this diff to add input validation:
function getActiveRepositories(uint256 offset, uint256 limit) external view returns (Repository[] memory) {
+ require(limit > 0, "Limit must be greater than 0");
+ require(limit <= 100, "Limit exceeds maximum allowed");
// Gather all active repositories
uint256 activeCount = 0;
📝 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.
function getActiveRepositories(uint256 offset, uint256 limit) external view returns (Repository[] memory) { | |
// Gather all active repositories | |
uint256 activeCount = 0; | |
for (uint256 i = 1; i <= repoCounter; i++) { | |
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | |
activeCount++; | |
} | |
} | |
// Create array with exact size needed | |
Repository[] memory activeRepos = new Repository[](activeCount); | |
// Second pass: populate the array | |
Repository[] memory tempRepos = new Repository[](activeCount); | |
uint256 index = 0; | |
for (uint256 i = 1; i <= repoCounter; i++) { | |
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | |
activeRepos[index] = repositories[i]; | |
tempRepos[index] = repositories[i]; | |
index++; | |
} | |
} | |
return activeRepos; | |
// Pagination | |
if (offset >= activeCount) { | |
return new Repository[](0); | |
} | |
uint256 end = offset + limit; | |
if (end > activeCount) { | |
end = activeCount; | |
} | |
Repository[] memory pagedRepos = new Repository[](end - offset); | |
for (uint256 i = offset; i < end; i++) { | |
pagedRepos[i - offset] = tempRepos[i]; | |
} | |
return pagedRepos; | |
} | |
function getActiveRepositories(uint256 offset, uint256 limit) external view returns (Repository[] memory) { | |
require(limit > 0, "Limit must be greater than 0"); | |
require(limit <= 100, "Limit exceeds maximum allowed"); | |
// Gather all active repositories | |
uint256 activeCount = 0; | |
for (uint256 i = 1; i <= repoCounter; i++) { | |
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | |
activeCount++; | |
} | |
} | |
Repository[] memory tempRepos = new Repository[](activeCount); | |
uint256 index = 0; | |
for (uint256 i = 1; i <= repoCounter; i++) { | |
if (repositories[i].isActive && repositories[i].maintainer != address(0)) { | |
tempRepos[index] = repositories[i]; | |
index++; | |
} | |
} | |
// Pagination | |
if (offset >= activeCount) { | |
return new Repository[](0); | |
} | |
uint256 end = offset + limit; | |
if (end > activeCount) { | |
end = activeCount; | |
} | |
Repository[] memory pagedRepos = new Repository[](end - offset); | |
for (uint256 i = offset; i < end; i++) { | |
pagedRepos[i - offset] = tempRepos[i]; | |
} | |
return pagedRepos; | |
} |
🤖 Prompt for AI Agents
In contracts/RepositoryRegistry.sol around lines 175-204, add validation for
pagination inputs: require limit > 0 (fail fast) and enforce an upper bound by
introducing a MAX_LIMIT constant (e.g., 100) and require(limit <= MAX_LIMIT);
also ensure offset handling remains safe by keeping the existing offset >=
activeCount early-return or, when computing end, cap end to offset + min(limit,
activeCount - offset) so callers cannot request more items than remain. Ensure
the MAX_LIMIT constant is declared near the top of the contract and update
pagination logic to use the validated/capped limit.
These changes have been implemented in
RepositoryRegistry.sol
:getActiveRepositories
function now supports pagination viaoffset
andlimit
parameters, preventing excessive data return.Summary by CodeRabbit