-
Notifications
You must be signed in to change notification settings - Fork 4
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
404 add reputation score api endpoint #405
Conversation
WalkthroughThe changes in this pull request introduce several new features and enhancements to the existing codebase. A new environment variable, Changes
Possibly related PRs
Warning Rate limit exceeded@Behzad-rabiei has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 23 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 13
🧹 Outside diff range and nitpick comments (16)
src/validations/nft.validation.ts (1)
4-11
: Enhance tokenId validation rulesWhile the current implementation validates the presence of tokenId, consider adding more specific validation rules to ensure it matches your expected format and constraints.
const getReputationScore = { params: Joi.object().keys({ - tokenId: Joi.string().required(), + tokenId: Joi.string() + .pattern(/^[0-9]+$/) // Assuming tokenId should be numeric + .required() + .messages({ + 'string.pattern.base': 'tokenId must be a numeric string' + }), address: Joi.string() .regex(/^0x[a-fA-F0-9]{40}$/) .required(), }), };src/routes/v1/nft.route.ts (1)
8-12
: Consider adding specific error handling for NFT-related errorsThe endpoint might encounter NFT-specific errors (e.g., invalid token, network issues). Consider implementing:
- A try-catch block in the controller
- Specific error types for NFT-related failures
- Consistent error response format
Example structure:
interface NFTError extends Error { code: string; statusCode: number; } // In controller: try { // reputation score logic } catch (error) { if (error instanceof NFTError) { return res.status(error.statusCode).json({ code: error.code, message: error.message }); } next(error); }src/services/oci.service.ts (2)
1-1
: Consider using built-in fetch APIThe
node-fetch
package might be unnecessary as newer Node.js versions (18+) have built-in fetch support. Consider removing this dependency if your Node.js version supports it.-import fetch from 'node-fetch';
8-8
: Add return type annotation for better type safetyThe function should explicitly declare its return type for better type safety and documentation.
-async function getProfiles(address: string, chainId: number) { +async function getProfiles(address: string, chainId: number): Promise<ProfileResponse> {Consider adding an interface to define the expected response structure:
interface ProfileResponse { // Define the expected response structure here }src/services/nft.service.ts (2)
11-15
: Enhance function documentation for better clarity.While the basic documentation is present, it would be more helpful to include:
- Description of the function's purpose and behavior
- Explanation of platform body requirements
- Possible error conditions
- Example usage
/** - * get reputation score + * Calculates and retrieves reputation score for a given platform. * @param {IPlatform} PlatformBody + * @param {IPlatform} PlatformBody.name - Platform name (Discord or Discourse) + * @param {Object} [PlatformBody.metadata] - Optional metadata for the platform * @returns {Promise<HydratedDocument<IPlatform>>} + * @throws {ApiError} When platform creation fails + * @example + * const platform = await getReputationScore({ + * name: PlatformNames.Discord, + * metadata: { /* platform specific data */ } + * }); */
27-29
: Handle potential race condition in saga creation.The saga creation for Discord platforms could fail independently of platform creation. Consider using a transaction or implementing a rollback mechanism.
if (PlatformBody.name === PlatformNames.Discord) { - await sagaService.createAndStartFetchMemberSaga(platform._id); + try { + await sagaService.createAndStartFetchMemberSaga(platform._id); + } catch (error) { + // Consider implementing cleanup or rollback + await Platform.findByIdAndDelete(platform._id); + throw new ApiError( + httpStatus.INTERNAL_SERVER_ERROR, + 'Failed to initialize member fetching' + ); + } }src/config/index.ts (3)
59-59
: LGTM! Consider enhancing the description.The new environment variable is properly configured with required validation. Consider making the description more specific about its purpose (e.g., "URL for the OCI backend service used for reputation score requests").
Line range hint
76-78
: Remove hardcoded credentials from comments.These commented MongoDB connection strings contain credentials that should not be in the codebase, even if commented out. This poses a security risk.
Apply this diff to remove the sensitive information:
serverURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}/${envVars.DB_NAME}?authSource=admin`, botURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`, dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`, - // serverURL: `mongodb://tcmongo:[email protected]:37017/Core?directConnection=true`, - // botURL: `mongodb://tcmongo:[email protected]:37017/?directConnection=true`, - // dbURL: `mongodb://tcmongo:[email protected]:37017/?directConnection=true`,
Line range hint
13-33
: Fix typos in environment variable descriptions.There are several occurrences of "clinet" instead of "client" in the environment variable descriptions.
Apply this diff to fix the typos:
- DISCORD_CLIENT_ID: Joi.string().required().description('Discord clinet id'), - DISCORD_CLIENT_SECRET: Joi.string().required().description('Discord clinet secret'), + DISCORD_CLIENT_ID: Joi.string().required().description('Discord client id'), + DISCORD_CLIENT_SECRET: Joi.string().required().description('Discord client secret'), DISCORD_BOT_TOKEN: Joi.string().required().description('Discord bot token'), DISCORD_AUTHORIZE_CALLBACK_URI: Joi.string().required().description('Discord authorize callback uri'), DISCORD_CONNECT_CALLBACK_URI: Joi.string().required().description('Discord connect callback uri'), DISCORD_REQUEST_ACCESS_CALLBACK_URI: Joi.string().required().description('Discord request access callback uri'), TWITTER_CONNECT_CALLBACK_URI: Joi.string().required().description('Twitter connect callback uri'), - TWITTER_CLIENT_ID: Joi.string().required().description('Twitter clinet id'), - TWITTER_CLIENT_SECRET: Joi.string().required().description('Twitter clinet secret'), + TWITTER_CLIENT_ID: Joi.string().required().description('Twitter client id'), + TWITTER_CLIENT_SECRET: Joi.string().required().description('Twitter client secret'), GOOGLE_CONNECT_CALLBACK_URI: Joi.string().required().description('Google connect callback uri'), - GOOGLE_CLIENT_SECRET: Joi.string().required().description('Google clinet id'), - GOOGLE_CLIENT_ID: Joi.string().required().description('Google clinet secret'), + GOOGLE_CLIENT_SECRET: Joi.string().required().description('Google client id'), + GOOGLE_CLIENT_ID: Joi.string().required().description('Google client secret'), GITHUB_APP_ID: Joi.string().required().description('Github app id'), GITHUB_APP_PUBLIC_LINK: Joi.string().required().description('Github app public link'), GITHUB_APP_CLIENT_ID: Joi.string().required().description('Github app client id'), GITHUB_APP_CLIENT_SECRET: Joi.string().required().description('Github app client secret'), GITHUB_APP_CONNECT_CALLBACK_URI: Joi.string().required().description('Github app connect callback uri'), NOTION_CONNECT_CALLBACK_URI: Joi.string().required().description('Notion connect callback uri'), - NOTION_CLIENT_SECRET: Joi.string().required().description('Notion clinet id'), - NOTION_CLIENT_ID: Joi.string().required().description('Notion clinet secret'), + NOTION_CLIENT_SECRET: Joi.string().required().description('Notion client id'), + NOTION_CLIENT_ID: Joi.string().required().description('Notion client secret'),src/services/platform.service.ts (1)
41-41
: Consider typing the options parameter as wellWhile the filter parameter is now properly typed with FilterQuery, the options parameter could also benefit from stronger typing using mongoose's PaginateOptions type.
-const queryPlatforms = async (filter: FilterQuery<IPlatform>, options: object) => { +const queryPlatforms = async (filter: FilterQuery<IPlatform>, options: PaginateOptions) => {Don't forget to add the import:
import { PaginateOptions } from 'mongoose';src/controllers/nft.controller.ts (6)
69-69
: Consider making supported chain IDs configurableThe
supportedChainIds
array currently contains a hardcoded value[11155111]
. Consider making this configurable or moving it to a constants file for better maintainability and scalability.
79-79
: Improve error message clarity and fix typosThe error message in
shouldProfilesExist
has awkward phrasing and a typo. It should be rephrased for clarity.Apply the following diff:
- throw new ApiError(400, 'User has no any onchain profiles'); + throw new ApiError(400, 'User has no on-chain profiles');
85-85
: Fix typos in error messageThe error message contains typos and inconsistent capitalization.
Apply the following diff:
- throw new ApiError(400, "There's not any assoicated dynamic nft module to the token Id"); + throw new ApiError(400, "There is no associated dynamic NFT module for the provided tokenId");
91-91
: Clarify error messageThe error message is unclear and could be improved for better understanding.
Apply the following diff:
- throw new ApiError(400, "There's not any platform connected for requested platform"); + throw new ApiError(400, "The requested platform is not connected to the community");
97-97
: Fix typo in error messageThe error message in
shouldProfileExist
contains a typo: "oncahin" should be "on-chain".Apply the following diff:
- throw new ApiError(400, "There's not any user oncahin profile for requested platform"); + throw new ApiError(400, "There is no user on-chain profile for the requested platform");
25-63
: Optimize loop structures for better readabilityConsider using modern JavaScript iterators like
for...of
loops to enhance readability and maintainability of your code.Apply the following refactor:
- for (let i = 0; i < supportedPlatforms.length; i++) { - const platform = await platformService.getPlatformByFilter({ - name: supportedPlatforms[i], + for (const platformName of supportedPlatforms) { + const platform = await platformService.getPlatformByFilter({ + name: platformName, community: dynamicNftModule?.community, }); // ... - for (let j = 0; j < profiles.length; j++) { - const profile = profiles[j]; + for (const profile of profiles) { // ... - if (profile.profile.provider === supportedPlatforms[i]) { + if (profile.profile.provider === platformName) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (11)
src/config/index.ts
(2 hunks)src/controllers/index.ts
(2 hunks)src/controllers/nft.controller.ts
(1 hunks)src/routes/v1/index.ts
(2 hunks)src/routes/v1/nft.route.ts
(1 hunks)src/services/index.ts
(2 hunks)src/services/nft.service.ts
(1 hunks)src/services/oci.service.ts
(1 hunks)src/services/platform.service.ts
(3 hunks)src/validations/index.ts
(2 hunks)src/validations/nft.validation.ts
(1 hunks)
🔇 Additional comments (21)
src/validations/nft.validation.ts (2)
13-15
: LGTM! Export structure is clean and well-organized.
4-11
: Verify route integration and error handling
Let's ensure this validation schema is properly integrated with the route handler and includes appropriate error handling.
✅ Verification successful
Validation schema is properly integrated with error handling middleware
The validation schema is correctly integrated:
- Route uses the
validate
middleware withnftValidation.getReputationScore
- Validation middleware is properly imported from
../../middlewares
- Controller accesses validated params via
req.params
- Error handling is built into the
catchAsync
wrapper in the controller
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the integration of validation schema with route handler
# Check for route definition using this validation
echo "Checking route definition..."
rg -l "nftValidation.getReputationScore" "src/routes"
# Check for error handling in the controller
echo "Checking error handling in controller..."
rg -l "reputation-score" "src/controllers"
Length of output: 277
Script:
#!/bin/bash
# Let's check the actual route implementation and controller
echo "Checking route implementation..."
rg -A 5 "getReputationScore" src/routes/v1/nft.route.ts
echo "\nChecking NFT controller..."
rg -A 5 "getReputationScore" src/controllers/nft.controller.ts
# Let's also verify the validation middleware usage
echo "\nChecking validation middleware..."
rg "validate" src/routes/v1/nft.route.ts
Length of output: 986
src/routes/v1/nft.route.ts (3)
1-4
: LGTM! Clean and well-organized imports
The imports follow standard Express.js practices and maintain a clear separation of concerns between controllers, validations, and middlewares.
5-5
: LGTM! Standard Express router initialization
14-14
: LGTM! Standard router export
src/validations/index.ts (2)
11-11
: LGTM! Clean and consistent implementation.
The addition of nftValidation
follows the established patterns in both import and export statements, maintaining consistency with the existing codebase structure.
Also applies to: 24-24
11-11
: Verify the NFT validation implementation.
Let's ensure the referenced validation file exists and contains proper validation schema for the reputation score endpoint.
✅ Verification successful
NFT validation schema is properly implemented ✅
The validation schema for the reputation score endpoint is correctly implemented with:
- Proper Joi validation library import
- Schema for
getReputationScore
endpoint that validates:tokenId
as a required stringaddress
as a required string with Ethereum address format (0x followed by 40 hex characters)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence and implementation of nft.validation.ts
# Check if the file exists
echo "Checking for nft.validation.ts file..."
fd "nft.validation.ts" src/validations
# Verify the validation schema implementation
echo "Checking validation schema implementation..."
rg -A 10 "getReputationScore.*schema" src/validations/nft.validation.ts
# Look for proper type imports
echo "Checking type imports..."
rg "import.*from.*@/types" src/validations/nft.validation.ts
Length of output: 454
Script:
#!/bin/bash
# Let's check the actual content of the nft.validation.ts file
echo "Content of nft.validation.ts:"
cat src/validations/nft.validation.ts
# Also check for any validation schema patterns
echo -e "\nChecking for validation schema patterns:"
rg "schema.*=.*{" src/validations/nft.validation.ts
# Look for Joi or other validation library imports
echo -e "\nChecking validation library imports:"
rg "^import.*joi" -i src/validations/nft.validation.ts
Length of output: 824
src/controllers/index.ts (2)
12-12
: LGTM! Clean import statement.
The import follows the established pattern and naming convention used for other controllers.
26-26
: LGTM! Export statement is properly placed.
The export maintains alphabetical ordering and is consistent with the existing pattern.
Let's verify we're not missing any controllers in the exports:
✅ Verification successful
Let me check the actual content of the index.ts file to understand why we're seeing these warnings and verify if there's a different export pattern being used.
The review comment is accurate - all controllers are properly exported
The initial script generated false warnings because it was looking for individual export statements, but the index.ts file uses a grouped export pattern. All controllers found in the directory (announcement, auth, category, community, discourse, heatmap, memberActivity, module, nft, notion, platform, and user) are correctly imported and exported in the index.ts file. The export of nftController
maintains alphabetical ordering and follows the established pattern.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any controller files that might not be exported
# Find all controller files
echo "Looking for controller files that might not be exported..."
fd -e ts -e js "controller" src/controllers | grep -v "index.ts" | while read -r file; do
# Extract the controller name
controller=$(basename "$file" | sed 's/\..*$//')
# Check if it's exported in index.ts
if ! grep -q "export.*${controller}" src/controllers/index.ts; then
echo "Warning: $controller found but not exported in index.ts"
fi
done
Length of output: 3486
Script:
#!/bin/bash
# Let's see the actual content of index.ts
cat src/controllers/index.ts
Length of output: 961
src/services/oci.service.ts (2)
27-29
: LGTM!
The export structure is clean and follows common patterns.
1-29
: Verify integration points
Let's verify the integration of this service with the rest of the system:
✅ Verification successful
Integration points verified successfully
The service is well-integrated with the system:
- Configuration:
ociBackendURL
is properly defined inconfig/index.ts
and correctly used in the service - Error handling:
ApiError
usage is consistent with the rest of the codebase, using similar error codes and message patterns - The custom error code 590 is used consistently across external service failures (discord, twitter, discourse)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if config.ociBackendURL is properly defined
echo "Checking configuration..."
rg "ociBackendURL" -t ts
# Check for proper error handling in consumers
echo "Checking error handling in consumers..."
rg "getProfiles.*catch" -t ts
# Check if ApiError is consistently used
echo "Checking ApiError usage patterns..."
ast-grep --pattern 'throw new ApiError($_, $_)'
Length of output: 7851
src/services/index.ts (2)
17-17
: LGTM! Clean integration of the OCI service.
The import and export statements follow the existing patterns and are correctly placed.
Also applies to: 35-35
17-17
: Verify the OCI service implementation.
Let's ensure the referenced service file exists and contains the expected implementation for reputation score functionality.
✅ Verification successful
Let me verify the implementation further by checking the complete OCI service implementation.
OCI service implementation verified with reputation score integration
The OCI service is correctly implemented with:
- A
getProfiles
function that fetches profiles from the OCI backend - Proper error handling and logging
- Integration with reputation score functionality through:
- Usage in
nft.controller.ts
for fetching profiles across supported chains - Connection to the reputation score calculation flow via
getReputationScore
endpoints
- Usage in
The service meets the expected implementation requirements and is properly integrated with the reputation score functionality.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify OCI service implementation and its integration with reputation score functionality
# Check if the service file exists and contains reputation-related functions
echo "Checking OCI service implementation..."
rg -l "oci.service.ts$"
# Look for reputation score related functions in the OCI service
echo "Checking reputation score functionality..."
rg -A 5 "getProfiles|getReputationScore" --type ts
Length of output: 4182
Script:
#!/bin/bash
# Check the complete implementation of oci.service.ts
echo "Checking complete OCI service implementation..."
cat src/services/oci.service.ts
Length of output: 1061
src/services/nft.service.ts (2)
1-10
: LGTM! Well-structured imports with proper typing.
The imports are well-organized, using specific imports rather than whole modules, which is a good practice for tree-shaking.
33-35
: LGTM! Clean and standard export pattern.
The export follows the common pattern for service modules, making it easy to import and use.
src/routes/v1/index.ts (2)
67-70
: LGTM! Verify security measures for NFT endpoints
The route configuration follows the established pattern and is properly integrated. Since this handles NFT-related operations, ensure that:
- Input validation is implemented for token IDs and addresses
- Rate limiting is in place
- Authentication/authorization is properly configured
#!/bin/bash
# Description: Verify security measures in the NFT route implementation
# Expected: Find validation, rate limiting, and auth middleware
# Check for validation implementation
rg -A 5 "validation|validate|schema" src/validations/nft.validation.ts
# Check for authentication/middleware usage
rg -A 5 "authenticate|authorize|middleware" src/routes/v1/nft.route.ts
# Check for rate limiting
rg "rateLimiter|RateLimit" src/routes/v1/nft.route.ts
14-14
: Verify the NFT route implementation
The import statement follows the established pattern. Let's verify the existence and implementation of the NFT route file.
✅ Verification successful
Import statement is correctly implemented and all dependencies exist
The import of nftRoute
is properly implemented with all required dependencies present:
nft.route.ts
exists and exports a router with the reputation score endpoint- Route is properly configured with validation middleware and controller
- Supporting files (
nft.validation.ts
andnft.controller.ts
) are implemented with the necessary logic
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence and implementation of nft.route.ts
# Expected: Find the file and confirm it exports a router with the reputation score endpoint
# Check if the file exists
fd "nft.route.ts" src/routes/v1
# Verify the route implementation
rg -A 10 "router\.(get|post|put|delete).*reputation-score" src/routes/v1/nft.route.ts
Length of output: 345
Script:
#!/bin/bash
# Let's verify the NFT route implementation more thoroughly
# First, check if the file exists and see its content
fd "nft.route.ts" src/routes/v1 --exec cat {}
# Also check for any validation schemas related to NFT
fd "nft.validation.ts" src/validations --exec cat {}
# Check for the controller implementation
fd "nft.controller.ts" src/controllers --exec cat {}
Length of output: 5097
src/config/index.ts (1)
162-162
: LGTM! Export follows consistent pattern.
The new configuration export is properly positioned and follows the established pattern.
src/services/platform.service.ts (3)
1-1
: LGTM! Clean import addition
The FilterQuery import is properly grouped with other mongoose types, maintaining good code organization.
50-50
: LGTM! Improved type safety
The filter parameter is now properly typed with FilterQuery, providing better type checking and IDE support.
Line range hint 41-50
: Verify integration with reputation score endpoint
The type improvements are good, but let's verify how these platform query methods are used by the new reputation score endpoint.
✅ Verification successful
Let me gather more context about how the platform service is used in the NFT controller.
Based on the evidence gathered, I can now provide the final response:
Type improvements are justified by actual platform service usage
The platform service methods are actively used within the NFT controller's reputation score implementation:
getPlatformByFilter
is used to fetch platform details for each supported platform when calculating reputation scores- The type improvements ensure type safety when integrating with the Neo4j-based reputation score calculation
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check how platform query methods are used in the new reputation score endpoint
# Search for usage of queryPlatforms and getPlatformByFilter in NFT-related files
echo "Searching for platform query method usage in NFT controller..."
rg -l "queryPlatforms|getPlatformByFilter" --type ts | grep -i "nft"
# Search for any reputation score related code
echo "Searching for reputation score related code..."
rg -i "reputation.?score" --type ts
Length of output: 1758
Script:
#!/bin/bash
# Check the actual usage of platform service methods in NFT controller
echo "Checking platform service usage in NFT controller..."
rg -A 5 -B 5 "getPlatformByFilter|queryPlatforms" src/controllers/nft.controller.ts
# Check the platform service usage in NFT service
echo "Checking platform service usage in NFT service..."
rg -A 5 -B 5 "getPlatformByFilter|queryPlatforms" src/services/nft.service.ts
# Check the implementation of getReputationScore
echo "Checking getReputationScore implementation..."
ast-grep --pattern 'const getReputationScore = async function (req: Request, res: Response) {
$$$
}'
Length of output: 1426
import Joi from 'joi'; | ||
import { PlatformNames } from '@togethercrew.dev/db'; | ||
|
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
Remove unused import PlatformNames
The PlatformNames
import from '@togethercrew.dev/db' is not used in this file.
import Joi from 'joi';
-import { PlatformNames } from '@togethercrew.dev/db';
📝 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.
import Joi from 'joi'; | |
import { PlatformNames } from '@togethercrew.dev/db'; | |
import Joi from 'joi'; |
src/routes/v1/nft.route.ts
Outdated
router.post( | ||
'/:tokenId/:address/reputation-score', | ||
validate(nftValidation.getReputationScore), | ||
nftController.getReputationScore, | ||
); |
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.
💡 Codebase verification
Consider adding authentication middleware to protect NFT reputation endpoint
The codebase has authentication middleware (auth
) implemented and used across multiple routes, but it's not applied to this NFT reputation endpoint. This endpoint processes sensitive NFT data and should be protected.
- Add authentication middleware to
src/routes/v1/nft.route.ts
:
router.post(
'/:tokenId/:address/reputation-score',
auth('admin', 'view'), // Add this line
validate(nftValidation.getReputationScore),
nftController.getReputationScore,
);
The validation schema correctly validates:
tokenId
: Required stringaddress
: Required Ethereum address format (0x followed by 40 hex chars)
🔗 Analysis chain
Consider adding authentication and rate limiting middleware
This endpoint processes NFT reputation scores based on user input. Consider implementing:
- Authentication middleware to prevent unauthorized access
- Rate limiting to prevent abuse
- Input sanitization for the URL parameters
Let's verify the validation schema and any existing auth middleware:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for validation schema implementation
echo "Checking validation schema..."
rg -A 10 "getReputationScore" --glob "src/validations/**/*.ts"
# Check for existing auth middleware in the project
echo "Checking auth middleware..."
rg -l "auth.*middleware" --glob "src/middlewares/**/*.ts"
Length of output: 894
Script:
#!/bin/bash
# Check for any auth or rate limiting middleware in the route file
echo "Checking route middleware..."
rg -B 5 -A 5 "router\.(get|post|put|delete)" src/routes/v1/nft.route.ts
# Check for any auth middleware implementations
echo "Checking auth implementations..."
fd -e ts -e js --exec rg -l "auth" {} \; -X rg -A 5 "(middleware|auth)" {}
# Check for rate limiting implementations
echo "Checking rate limiting..."
fd -e ts -e js --exec rg -l "limit" {} \; -X rg -A 5 "(rateLimit|rateLimiter)"
Length of output: 1083
Script:
#!/bin/bash
# Check for auth implementations with corrected syntax
echo "Checking auth implementations..."
rg -l "auth" --type ts --type js | xargs rg -A 5 "(middleware|auth)"
# Check for rate limiting with corrected syntax
echo "Checking rate limiting..."
rg -l "limit" --type ts --type js | xargs rg -A 5 "(rateLimit|rateLimiter)"
# Check middleware imports in the main app setup
echo "Checking app middleware setup..."
rg -A 10 "express\(\)" --type ts --type js
Length of output: 51320
src/services/oci.service.ts
Outdated
} catch (error: any) { | ||
logger.error(error, 'Failed to get profiles from oci backend'); | ||
throw new ApiError(590, 'Failed to get profiles from oci backend '); | ||
} |
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
Improve error handling specificity
The error handling could be more specific and informative:
- The magic number 590 should be a named constant
- Error messages should include more context
- Original error details should be preserved
- } catch (error: any) {
- logger.error(error, 'Failed to get profiles from oci backend');
- throw new ApiError(590, 'Failed to get profiles from oci backend ');
+ } catch (error) {
+ const errorMessage = `Failed to get profiles for address ${address} on chain ${chainId}`;
+ logger.error({ error, address, chainId }, errorMessage);
+ if (error instanceof Error) {
+ throw new ApiError(
+ OCI_ERROR_CODES.PROFILE_FETCH_FAILED,
+ `${errorMessage}: ${error.message}`
+ );
+ }
+ throw new ApiError(OCI_ERROR_CODES.PROFILE_FETCH_FAILED, errorMessage);
Consider adding an error codes enum:
enum OCI_ERROR_CODES {
PROFILE_FETCH_FAILED = 590,
// Add other OCI-related error codes here
}
src/services/oci.service.ts
Outdated
logger.debug(`${config.ociBackendURL}/oci/profiles/${chainId}/${address}`); | ||
const response = await fetch(`${config.ociBackendURL}/api/v1/oci/profiles/${chainId}/${address}`, { | ||
method: 'GET', | ||
headers: { 'Content-Type': 'application/json' }, | ||
}); |
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
Enhance URL construction and request configuration
Several improvements can be made to the request setup:
- The URL parameters should be properly encoded to prevent injection
- API version should be configurable
- Request timeout should be added
- logger.debug(`${config.ociBackendURL}/oci/profiles/${chainId}/${address}`);
- const response = await fetch(`${config.ociBackendURL}/api/v1/oci/profiles/${chainId}/${address}`, {
+ const url = new URL(`${config.ociBackendURL}/api/v1/oci/profiles/${chainId}/${encodeURIComponent(address)}`);
+ logger.debug(`Fetching profiles from: ${url.toString()}`);
+ const response = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
+ timeout: 5000, // Add reasonable timeout
});
📝 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.
logger.debug(`${config.ociBackendURL}/oci/profiles/${chainId}/${address}`); | |
const response = await fetch(`${config.ociBackendURL}/api/v1/oci/profiles/${chainId}/${address}`, { | |
method: 'GET', | |
headers: { 'Content-Type': 'application/json' }, | |
}); | |
const url = new URL(`${config.ociBackendURL}/api/v1/oci/profiles/${chainId}/${encodeURIComponent(address)}`); | |
logger.debug(`Fetching profiles from: ${url.toString()}`); | |
const response = await fetch(url, { | |
method: 'GET', | |
headers: { 'Content-Type': 'application/json' }, | |
timeout: 5000, // Add reasonable timeout | |
}); |
src/services/nft.service.ts
Outdated
const getReputationScore = async (PlatformBody: IPlatform): Promise<HydratedDocument<IPlatform>> => { | ||
if (PlatformBody.name === PlatformNames.Discord || PlatformBody.name === PlatformNames.Discourse) { | ||
if (PlatformBody.metadata) { | ||
PlatformBody.metadata = { | ||
action: analyzerAction, | ||
window: analyzerWindow, | ||
...PlatformBody.metadata, | ||
}; | ||
} | ||
} | ||
const platform = await Platform.create(PlatformBody); |
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.
Add error handling and input validation.
The function lacks proper error handling and input validation:
- Platform creation could fail silently
- No validation of required PlatformBody fields
- Metadata structure is not type-checked
const getReputationScore = async (PlatformBody: IPlatform): Promise<HydratedDocument<IPlatform>> => {
+ if (!PlatformBody || !PlatformBody.name) {
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid platform data');
+ }
+
if (PlatformBody.name === PlatformNames.Discord || PlatformBody.name === PlatformNames.Discourse) {
if (PlatformBody.metadata) {
- PlatformBody.metadata = {
+ const enrichedMetadata = {
action: analyzerAction,
window: analyzerWindow,
...PlatformBody.metadata,
};
+ PlatformBody = { ...PlatformBody, metadata: enrichedMetadata };
}
}
- const platform = await Platform.create(PlatformBody);
+ try {
+ const platform = await Platform.create(PlatformBody);
+ } catch (error) {
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Failed to create platform');
+ }
Committable suggestion skipped: line range outside the PR's diff.
src/controllers/nft.controller.ts
Outdated
const profile = profiles[j]; | ||
logger.debug({ i, j, profile, supportedPlatforms: supportedPlatforms[i] }); | ||
if (profile.profile.provider === supportedPlatforms[i]) { |
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.
Validate the existence of the user profile
After retrieving profile
, you should validate that it exists before proceeding to prevent runtime errors.
Apply the following diff:
const profile = profiles[j];
+ shouldProfileExist(profile);
logger.debug({ i, j, profile, supportedPlatforms: supportedPlatforms[i] });
if (profile.profile.provider === supportedPlatforms[i]) {
Ensure that the shouldProfileExist
function is defined and imported.
Committable suggestion skipped: line range outside the PR's diff.
src/controllers/nft.controller.ts
Outdated
const profiles: Array<any> = await getProfilesOnAllSupportedChains(address); | ||
logger.debug(profiles); |
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.
Validate profiles after retrieval
After retrieving profiles, you should validate that profiles exist before proceeding to prevent errors when profiles are empty.
Apply the following diff:
const profiles: Array<any> = await getProfilesOnAllSupportedChains(address);
+ shouldProfilesExist(profiles);
logger.debug(profiles);
Ensure that the shouldProfilesExist
function is correctly implemented and imported.
Committable suggestion skipped: line range outside the PR's diff.
src/controllers/nft.controller.ts
Outdated
} | ||
} | ||
} | ||
return repuationScore; |
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.
Send the reputation score in the HTTP response
The controller function currently returns the reputation score but does not send it in the HTTP response. In an Express controller, you should use res.json()
or res.send()
to send data back to the client.
Apply the following diff to send the reputation score in the response:
- return reputationScore;
+ res.json({ reputationScore });
Committable suggestion skipped: line range outside the PR's diff.
src/controllers/nft.controller.ts
Outdated
} | ||
|
||
function shouldProfilesExist(profiles: Array<any>) { | ||
if (profiles.length < 0) { |
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.
Fix incorrect condition in profiles existence check
The condition profiles.length < 0
is incorrect because an array's length cannot be negative. It should be profiles.length === 0
to check for an empty array.
Apply the following diff:
function shouldProfilesExist(profiles: Array<any>) {
- if (profiles.length < 0) {
+ if (profiles.length === 0) {
throw new ApiError(400, 'User has no on-chain profiles');
}
}
src/controllers/nft.controller.ts
Outdated
community: dynamicNftModule?.community, | ||
}); | ||
logger.debug({ i, platform, supportedPlatforms: supportedPlatforms[i] }); | ||
// shouldPlatformExist(platform); |
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.
Uncomment the platform existence validation
The call to shouldPlatformExist(platform);
is commented out. This validation is important to ensure that the platform exists before proceeding.
Apply the following diff:
- // shouldPlatformExist(platform);
+ shouldPlatformExist(platform);
Ensure that shouldPlatformExist
is defined and imported.
Committable suggestion skipped: line range outside the PR's diff.
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: 4
🧹 Outside diff range and nitpick comments (2)
src/controllers/nft.controller.ts (2)
19-21
: Avoid logging sensitive user dataLogging detailed user addresses and profile information may expose sensitive data. Consider limiting the amount of sensitive information in logs.
Modify the logging statements to reduce sensitive data exposure:
- logger.debug(tokenId, address); - logger.debug(profiles); + logger.debug(`Processing tokenId: ${tokenId}`); + logger.debug(`Retrieved ${profiles.length} profiles`);This way, you still get useful debugging information without exposing sensitive details.
68-69
: Specify the type of profiles for better type safetyUsing
Array<any>
reduces type safety and can lead to runtime errors. Define a proper interface for profiles to enhance maintainability and catch errors at compile time.Define an interface for profiles and update the code:
interface Profile { profile: { id: string; provider: string; // Add other relevant fields }; }Then update the code:
- let profiles: Array<any> = []; + let profiles: Profile[] = [];
const { tokenId, address } = req.params; | ||
const supportedPlatforms = ['discord', 'discourse']; | ||
|
||
let repuationScore; |
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.
Handle potential undefined 'repuationScore' before returning
If none of the profiles match the supported platforms, repuationScore
may remain undefined
, leading to unexpected behavior when returned. Initialize repuationScore
to a default value and ensure it is always defined before returning.
Apply the following diff to initialize repuationScore
and handle the response correctly:
- let repuationScore;
+ let repuationScore = 0;
...
- return repuationScore;
+ res.json({ reputationScore: repuationScore });
Also applies to: 64-64
const { _fieldLookup, _fields } = reputationScoreResponse as unknown as { | ||
_fieldLookup: Record<string, number>; | ||
_fields: number[]; | ||
}; |
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
Avoid using internal properties of Neo4j response objects
Accessing internal properties like _fieldLookup
and _fields
is not recommended, as they are subject to change and not part of the public API. Use the official Neo4j driver methods to extract data from the result records.
Refactor the code to use public methods:
- const { _fieldLookup, _fields } = reputationScoreResponse as unknown as {
- _fieldLookup: Record<string, number>;
- _fields: number[];
- };
- repuationScore = _fields[_fieldLookup['reputation_score']];
+ const reputationScoreRecord = reputationScoreResponse.get('reputation_score');
+ repuationScore = reputationScoreRecord;
This approach uses the get
method to safely access the desired field from the record.
📝 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.
const { _fieldLookup, _fields } = reputationScoreResponse as unknown as { | |
_fieldLookup: Record<string, number>; | |
_fields: number[]; | |
}; | |
const reputationScoreRecord = reputationScoreResponse.get('reputation_score'); | |
repuationScore = reputationScoreRecord; |
const chainProfiles = await ociService.getProfiles(address, supportedChainIds[i]); | ||
profiles = profiles.concat(chainProfiles); |
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.
Handle potential errors from OCI service calls
The call to ociService.getProfiles
might fail or throw an exception. It's important to handle such errors to prevent the application from crashing.
Add error handling for the OCI service call:
for (let i = 0; i < supportedChainIds.length; i++) {
- const chainProfiles = await ociService.getProfiles(address, supportedChainIds[i]);
+ let chainProfiles;
+ try {
+ chainProfiles = await ociService.getProfiles(address, supportedChainIds[i]);
+ } catch (error) {
+ logger.error(`Failed to get profiles for chain ID ${supportedChainIds[i]}:`, error);
+ continue;
+ }
profiles = profiles.concat(chainProfiles);
}
This ensures that a failure in one chain does not halt the entire process.
Committable suggestion skipped: line range outside the PR's diff.
const reputationScoreQuery = ` | ||
MATCH (:${NEO4J_PLATFORM_INFO[temp].member} {id: "${profile.profile.id}"})-[r:HAVE_METRICS {platformId: "${platform?.id}"}]->(a) | ||
WITH r.date as metrics_date, r.closenessCentrality as memberScore | ||
ORDER BY metrics_date DESC | ||
LIMIT 1 | ||
MATCH (user:${NEO4J_PLATFORM_INFO[temp].member})-[user_r:HAVE_METRICS {platformId: "${platform?.id}", date: metrics_date}]->(user) | ||
WITH memberScore, MAX(user_r.closenessCentrality) as maxScore | ||
RETURN memberScore / maxScore AS reputation_score | ||
`; |
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.
Prevent injection attacks by parameterizing Neo4j queries
Directly interpolating variables into Neo4j query strings can lead to injection vulnerabilities if any variables contain malicious input. Use parameterized queries to securely pass variables to the query.
Refactor the code to use parameterized queries:
- const reputationScoreQuery = `
- MATCH (:${NEO4J_PLATFORM_INFO[temp].member} {id: "${profile.profile.id}"})-[r:HAVE_METRICS {platformId: "${platform?.id}"}]->(a)
- WITH r.date as metrics_date, r.closenessCentrality as memberScore
- ORDER BY metrics_date DESC
- LIMIT 1
- MATCH (user:${NEO4J_PLATFORM_INFO[temp].member})-[user_r:HAVE_METRICS {platformId: "${platform?.id}", date: metrics_date}]->(user)
- WITH memberScore, MAX(user_r.closenessCentrality) as maxScore
- RETURN memberScore / maxScore AS reputation_score
- `;
+ const reputationScoreQuery = `
+ MATCH (:${NEO4J_PLATFORM_INFO[temp].member} {id: $profileId})-[r:HAVE_METRICS {platformId: $platformId}]->(a)
+ WITH r.date as metrics_date, r.closenessCentrality as memberScore
+ ORDER BY metrics_date DESC
+ LIMIT 1
+ MATCH (user:${NEO4J_PLATFORM_INFO[temp].member})-[user_r:HAVE_METRICS {platformId: $platformId, date: metrics_date}]->(user)
+ WITH memberScore, MAX(user_r.closenessCentrality) as maxScore
+ RETURN memberScore / maxScore AS reputation_score
+ `;
...
- const neo4jData = await Neo4j.read(reputationScoreQuery);
+ const neo4jData = await Neo4j.read(reputationScoreQuery, {
+ profileId: profile.profile.id,
+ platformId: platform?.id,
+ });
This refactoring ensures that variables are passed safely to the query, mitigating the risk of injection attacks.
Committable suggestion skipped: line range outside the PR's diff.
Summary by CodeRabbit
Release Notes
New Features
/:tokenId/:address/reputation-score
.Enhancements
OCI_BACKEND_URL
for configuration.Documentation