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

404 add reputation score api endpoint #405

Merged
merged 3 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const envVarsSchema = Joi.object()
REDIS_PORT: Joi.string().required().description('Redis port'),
REDIS_PASSWORD: Joi.string().required().description('Reids password').allow(''),
DISCOURSE_EXTRACTION_URL: Joi.string().required().description('Discourse extraction url'),
OCI_BACKEND_URL: Joi.string().required().description('Oci Backend url'),
})
.unknown();

Expand Down Expand Up @@ -158,4 +159,5 @@ export default {
discourse: {
extractionURL: envVars.DISCOURSE_EXTRACTION_URL,
},
ociBackendURL: envVars.OCI_BACKEND_URL,
};
3 changes: 3 additions & 0 deletions src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import announcementController from './announcement.controller';
import categoryController from './category.controller';
import moduleController from './module.controller';
import discourseController from './discourse.controller';
import nftController from './nft.controller';

export {
authController,
userController,
Expand All @@ -21,4 +23,5 @@ export {
categoryController,
moduleController,
discourseController,
nftController,
};
109 changes: 109 additions & 0 deletions src/controllers/nft.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Request, Response } from 'express';
import { catchAsync } from '../utils';
import parentLogger from '../config/logger';
import { moduleService, platformService, ociService } from '../services';
import { ApiError } from '../utils';
import { IModule, IPlatform } from '@togethercrew.dev/db';
import { HydratedDocument } from 'mongoose';
import * as Neo4j from '../neo4j';
import { NEO4J_PLATFORM_INFO } from '../constants/neo4j.constant';
import { SupportedNeo4jPlatforms } from '../types/neo4j.type';

const logger = parentLogger.child({ module: 'NftController' });

const getReputationScore = catchAsync(async function (req: Request, res: Response) {
const { tokenId, address } = req.params;
const supportedPlatforms = ['discord', 'discourse'];

let repuationScore;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Correct the misspelled variable name "repuationScore"

The variable repuationScore is misspelled in multiple places. It should be reputationScore for clarity and consistency.

Apply the following diff to fix the variable name:

- let repuationScore;
+ let reputationScore;

...

- reputationScore = _fields[_fieldLookup['reputation_score']];
+ reputationScore = _fields[_fieldLookup['reputation_score']];

...

- logger.debug(repuationScore);
+ logger.debug(reputationScore);

...

- return repuationScore;
+ return reputationScore;

Also applies to: 59-59, 60-60, 64-64

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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

logger.debug(tokenId, address);
const profiles: Array<any> = await getProfilesOnAllSupportedChains(address);
logger.debug(profiles);
Comment on lines +20 to +21
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

const dynamicNftModule = await moduleService.getModuleByFilter({ 'options.platforms.0.metadata.tokenId': tokenId });
logger.debug(dynamicNftModule);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate the existence of the dynamic NFT module

After retrieving dynamicNftModule, you should validate that it exists to avoid unexpected errors later in the code.

Apply the following diff:

  const dynamicNftModule = await moduleService.getModuleByFilter({ 'options.platforms.0.metadata.tokenId': tokenId });
+ shouldDynamicNftModuleExist(dynamicNftModule);
  logger.debug(dynamicNftModule);

Ensure that the shouldDynamicNftModuleExist function is defined and imported.

Committable suggestion skipped: line range outside the PR's diff.


for (let i = 0; i < supportedPlatforms.length; i++) {
const platform = await platformService.getPlatformByFilter({
name: supportedPlatforms[i],
community: dynamicNftModule?.community,
});
logger.debug({ i, platform, supportedPlatforms: supportedPlatforms[i] });
// shouldPlatformExist(platform);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

for (let j = 0; j < profiles.length; j++) {
const profile = profiles[j];
logger.debug({ i, j, profile, supportedPlatforms: supportedPlatforms[i] });
if (profile.profile.provider === supportedPlatforms[i]) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

const reputationScoreQuery = `
MATCH (:${NEO4J_PLATFORM_INFO[platform?.name as SupportedNeo4jPlatforms].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[platform?.name as SupportedNeo4jPlatforms].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 neo4jData = await Neo4j.read(reputationScoreQuery);
const { records } = neo4jData;
logger.debug(records);

const reputationScoreResponse = records[0];

logger.debug(reputationScoreResponse);

const { _fieldLookup, _fields } = reputationScoreResponse as unknown as {
_fieldLookup: Record<string, number>;
_fields: number[];
};
Comment on lines +54 to +57
Copy link

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.

Suggested change
const { _fieldLookup, _fields } = reputationScoreResponse as unknown as {
_fieldLookup: Record<string, number>;
_fields: number[];
};
const reputationScoreRecord = reputationScoreResponse.get('reputation_score');
repuationScore = reputationScoreRecord;


repuationScore = _fields[_fieldLookup['reputation_score']];
logger.debug(repuationScore);
}
}
}
return repuationScore;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

});

async function getProfilesOnAllSupportedChains(address: string) {
let profiles: Array<any> = [];
const supportedChainIds = [11155111];
for (let i = 0; i < supportedChainIds.length; i++) {
const chainProfiles = await ociService.getProfiles(address, supportedChainIds[i]);
profiles = profiles.concat(chainProfiles);
Comment on lines +71 to +72
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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.

}
return profiles;
}

function shouldProfilesExist(profiles: Array<any>) {
if (profiles.length < 0) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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');
    }
}

throw new ApiError(400, 'User has no any onchain profiles');
}
}

function shouldDynamicNftModuleExist(dynamicNftModule: HydratedDocument<IModule> | null) {
if (!dynamicNftModule) {
throw new ApiError(400, "There's not any assoicated dynamic nft module to the token Id");
}
}

function shouldPlatformExist(platform: HydratedDocument<IPlatform> | null) {
if (!platform) {
throw new ApiError(400, "There's not any platform connected for requested platform");
}
}

function shouldProfileExist(profile: any) {
if (!profile) {
throw new ApiError(400, "There's not any user oncahin profile for requested platform");
}
}
export default {
getReputationScore,
};

// const platform = await platformService.getPlatformByFilter({
// name: platforms[i],
// community: dynamicNftModule?.community,
// });
// const profile = profiles.find((p: any) => p.profile.provider === platform);
// // Do the Cypher || N/A ?? || drop-down for platforms
5 changes: 5 additions & 0 deletions src/routes/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import announcementRoute from './announcement.route';
import categoryRoute from './category.route';
import moduleRoute from './module.route';
import discourseRoute from './discourse.route';
import nftRoute from './nft.route';
const router = express.Router();

const defaultRoutes = [
Expand Down Expand Up @@ -63,6 +64,10 @@ const defaultRoutes = [
path: '/discourse',
route: discourseRoute,
},
{
path: '/nft',
route: nftRoute,
},
];

defaultRoutes.forEach((route) => {
Expand Down
14 changes: 14 additions & 0 deletions src/routes/v1/nft.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import express from 'express';
import { nftController } from '../../controllers';
import { nftValidation } from '../../validations';
import { validate } from '../../middlewares';
const router = express.Router();

// Routes
router.post(
'/:tokenId/:address/reputation-score',
validate(nftValidation.getReputationScore),
nftController.getReputationScore,
);
Comment on lines +8 to +12
Copy link

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 string
  • address: 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:

  1. Authentication middleware to prevent unauthorized access
  2. Rate limiting to prevent abuse
  3. 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


export default router;
2 changes: 2 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import googleService from './google';
import githubService from './github';
import notionService from './notion';
import discourseService from './discourse';
import ociService from './oci.service';
export {
userService,
authService,
Expand All @@ -31,4 +32,5 @@ export {
githubService,
notionService,
discourseService,
ociService,
};
35 changes: 35 additions & 0 deletions src/services/nft.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { HydratedDocument, Types } from 'mongoose';
import httpStatus from 'http-status';
import { Platform, IPlatform } from '@togethercrew.dev/db';
import ApiError from '../utils/ApiError';
import sagaService from './saga.service';
import discourseService from './discourse';
import { Snowflake } from 'discord.js';
import { analyzerAction, analyzerWindow } from '../config/analyzer.statics';
import { PlatformNames } from '@togethercrew.dev/db';

/**
* get reputation score
* @param {IPlatform} PlatformBody
* @returns {Promise<HydratedDocument<IPlatform>>}
*/
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);
Comment on lines +16 to +26
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling and input validation.

The function lacks proper error handling and input validation:

  1. Platform creation could fail silently
  2. No validation of required PlatformBody fields
  3. 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.

if (PlatformBody.name === PlatformNames.Discord) {
await sagaService.createAndStartFetchMemberSaga(platform._id);
}
return platform;
};

export default {
getReputationScore,
};
Comment on lines +1 to +35
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Integration verification reveals mismatched implementation

The service implementation doesn't align with its usage in the controller:

  • The controller expects to get reputation scores from Neo4j queries for Discord/Discourse platforms
  • However, the service creates a new platform and starts a saga, which isn't used in the controller flow
  • The service's getReputationScore function is never called from the controller

Locations needing attention:

  • src/services/nft.service.ts: Current implementation is unused
  • src/controllers/nft.controller.ts: Uses direct platform service instead of nft service
🔗 Analysis chain

Verify integration with related components.

Let's verify the integration with the controller, routes, and validation components mentioned in the summary.


Let me gather more information about the integration by checking the actual implementation in the route and controller files.


Let me check the contents of the controller and validation files to complete the verification.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify integration with related components

# Check for controller implementation
echo "Checking NFT controller implementation..."
rg -l "getReputationScore.*nft\.service" src/controllers/

# Check for route definition
echo "Checking route definition..."
rg -l "/:tokenId/:address/reputation-score" src/routes/

# Check for validation schema
echo "Checking validation schema..."
rg -l "reputation.*score.*validation" src/validations/

# Verify OCI_BACKEND_URL usage
echo "Checking OCI_BACKEND_URL usage..."
rg "OCI_BACKEND_URL" src/config/

Length of output: 697


Script:

#!/bin/bash
# Check the route implementation
echo "Route implementation:"
cat src/routes/v1/nft.route.ts

# Check for controller file
echo -e "\nSearching for NFT controller:"
fd "nft.controller" src/

# Check validation schema files
echo -e "\nLooking for validation files:"
fd "nft.*validation" src/

Length of output: 742


Script:

#!/bin/bash
# Check the controller implementation
echo "Controller implementation:"
cat src/controllers/nft.controller.ts

# Check the validation schema
echo -e "\nValidation schema:"
cat src/validations/nft.validation.ts

Length of output: 4749

29 changes: 29 additions & 0 deletions src/services/oci.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import fetch from 'node-fetch';
import config from '../config';
import { ApiError } from '../utils';
import parentLogger from '../config/logger';

const logger = parentLogger.child({ module: 'OciService' });

async function getProfiles(address: string, chainId: number) {
try {
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' },
});
Comment on lines +10 to +14
Copy link

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:

  1. The URL parameters should be properly encoded to prevent injection
  2. API version should be configurable
  3. 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.

Suggested change
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
});

if (response.ok) {
return await response.json();
} else {
const errorResponse = await response.text();
throw new Error(errorResponse);
}
} catch (error: any) {
logger.error(error, 'Failed to get profiles from oci backend');
throw new ApiError(590, 'Failed to get profiles from oci backend ');
}
Comment on lines +21 to +24
Copy link

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:

  1. The magic number 590 should be a named constant
  2. Error messages should include more context
  3. 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
}

}

export default {
getProfiles,
};
6 changes: 3 additions & 3 deletions src/services/platform.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HydratedDocument, Types } from 'mongoose';
import { HydratedDocument, Types, FilterQuery } from 'mongoose';
import httpStatus from 'http-status';
import { Platform, IPlatform } from '@togethercrew.dev/db';
import ApiError from '../utils/ApiError';
Expand Down Expand Up @@ -38,7 +38,7 @@ const createPlatform = async (PlatformBody: IPlatform): Promise<HydratedDocument
* @param {number} [options.limit] - Maximum number of results per page (default = 10)
* @param {number} [options.page] - Current page (default = 1)
*/
const queryPlatforms = async (filter: object, options: object) => {
const queryPlatforms = async (filter: FilterQuery<IPlatform>, options: object) => {
return Platform.paginate(filter, options);
};

Expand All @@ -47,7 +47,7 @@ const queryPlatforms = async (filter: object, options: object) => {
* @param {Object} filter - Mongo filter
* @returns {Promise<HydratedDocument<IPlatform> | null>}
*/
const getPlatformByFilter = async (filter: object): Promise<HydratedDocument<IPlatform> | null> => {
const getPlatformByFilter = async (filter: FilterQuery<IPlatform>): Promise<HydratedDocument<IPlatform> | null> => {
return Platform.findOne(filter);
};

Expand Down
3 changes: 3 additions & 0 deletions src/validations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import platformValidation from './platform.validation';
import announcementValidation from './announcement.validation';
import moduleValidation from './module.validation';
import discourseValidation from './discourse.validation';
import nftValidation from './nft.validation';

export {
authValidation,
guildValidation,
Expand All @@ -19,4 +21,5 @@ export {
announcementValidation,
moduleValidation,
discourseValidation,
nftValidation,
};
15 changes: 15 additions & 0 deletions src/validations/nft.validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Joi from 'joi';
import { PlatformNames } from '@togethercrew.dev/db';

Comment on lines +1 to +3
Copy link

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.

Suggested change
import Joi from 'joi';
import { PlatformNames } from '@togethercrew.dev/db';
import Joi from 'joi';

const getReputationScore = {
params: Joi.object().keys({
tokenId: Joi.string().required(),
address: Joi.string()
.regex(/^0x[a-fA-F0-9]{40}$/)
.required(),
}),
};

export default {
getReputationScore,
};