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

Task #217774 Resolve conflicts of AclHelper #1061

Open
wants to merge 2 commits into
base: feat-acl
Choose a base branch
from

Conversation

YoginiTayade
Copy link
Contributor

@YoginiTayade YoginiTayade commented Apr 26, 2024

I have ensured that following Pull Request Checklist is taken care of before sending this PR

  • Code is formatted as per format decided
  • Updated acceptance criteria in tracker
  • Updated test cases in test-cases-tracker
  • Updated new API endpoint if any in common postman collection
  • Updated DB changes in db-tracker if any

Summary by CodeRabbit

  • New Features

    • Added a new updateScholarshipId method in BeneficiariesController to handle scholarship information updates via POST requests.
    • Introduced new endpoint handlers in CampController for camp-related operations.
    • Enhanced the ObservationsModule for improved modular functionality.
  • Enhancements

    • Improved date range querying in attendance records by accepting additional parameters.
    • Enhanced filtering logic in various services and controllers to include new parameters like type.
  • Updates

    • Updated dependencies in package.json for enhanced performance and security.
  • Bug Fixes

    • Corrected user role handling in middleware to ensure accurate user identification and authorization.
  • Refactor

    • Refactored logic in PrepareCertificateHtmlCron to optimize certificate generation process.
  • Middleware

    • Introduced IpMiddleware for enhanced IP user ID validation across routes.

Copy link

coderabbitai bot commented Apr 26, 2024

Walkthrough

The project updates encompass a comprehensive overhaul aimed at enhancing system functionality. Changes include version upgrades for dependencies, introduction of new methods and endpoints, and logic refinements to boost performance and maintainability. These updates span across middleware, service, and controller layers, reflecting a concerted effort to improve the system's capabilities.

Changes

File Path Change Summary
src/package.json Updated versions for @nestjs/platform-express and axios.
src/app.module.ts, src/beneficiaries.module.ts, src/camp.module.ts, src/facilitator.module.ts Addition of new modules and middleware applications.
src/attendances/attendances.core.service.ts Enhanced function to accept additional date range parameters.
src/beneficiaries/..., src/camp/... Added new methods and refined query logic in services and controllers.
src/common/middlewares/... Adjustments and additions in middleware logic, including new IpMiddleware.
src/cron/... Modifications in cron jobs to refine logic and update processes.
src/enum/enum.json Added and updated type fields in various enum entries.
src/events/... Changes from GET to POST for event list fetching and additional method for event exam start.
src/facilitator/facilitator.service.ts Adjustments in status conditions and error messages.
src/helper/queryGenerator.service.ts Added new functionalities in the service.

Recent Review Details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits Files that changed from the base of the PR and between 31b92fa and 52a2f63.
Files selected for processing (1)
  • src/src/beneficiaries/beneficiaries.controller.ts (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • src/src/beneficiaries/beneficiaries.controller.ts

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?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

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 as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

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

Out of diff range and nitpick comments (15)
src/src/common/middlewares/cohort.middleware.ts (1)

Line range hint 32-54: Refactor to improve error handling and reduce redundancy.

- if (req?.headers?.['x-program-id']) {
-   req.mw_program_id = req.headers['x-program-id'];
-   const program_id = parseInt(req.mw_program_id, 10);
-   if (isNaN(program_id)) {
-     throw new BadRequestException('Invalid Program Id');
-   }
-   const hasAccess = await this.method.isUserHasAccessForProgram(req);
-   if (!hasAccess) {
-     return res.json({
-       success: false,
-       message: 'User does not have access to this Program Id',
-     });
-   }
- } else {
-   return res.status(403).send({
-     success: false,
-     message: 'Program Id is required',
-   });
- }
+ try {
+   const program_id = parseInt(req.headers['x-program-id'], 10);
+   if (isNaN(program_id)) throw new BadRequestException('Invalid Program Id');
+   const hasAccess = await this.method.isUserHasAccessForProgram(req);
+   if (!hasAccess) throw new BadRequestException('User does not have access to this Program Id');
+ } catch (error) {
+   return res.status(403).json({ success: false, message: error.message });
+ }

This refactoring simplifies the logic by using a try-catch block, improving readability and error handling.

src/src/helper/queryGenerator.service.ts (1)

Line range hint 351-423: Optimize the query method by modularizing the construction of filter conditions.

This method is quite complex due to the dynamic construction of the query string. Consider extracting the logic for building filter conditions into a separate function. This will not only improve readability but also make the method easier to test and maintain.

src/src/cron/prepareCertificateHtml.cron.ts (1)

10-10: Remove unused import json from 'stream/consumers'.

The json import does not appear to be used in this file. Removing unused imports can help keep the code clean and maintainable.

src/src/lms/lms.service.ts (1)

Line range hint 572-615: Consider using template literals for dynamic string construction to enhance readability and prevent potential errors in query construction.

- let filterQuery = [`context: {_eq: ${context}}`];
+ let filterQuery = [`context: {_eq: "${context}"}`];

This change ensures that the context value is correctly interpreted as a string within the query.

src/src/camp/camp.core.service.ts (1)

Line range hint 81-159: Ensure consistent use of template literals for string construction in GraphQL queries to enhance readability and prevent errors.

- let filterQueryArray = [`{group_users: {member_type: {_eq: "owner"}, group: {program_id: {_eq:${program_id}}, academic_year_id: {_eq:${academic_year_id}}},user:{program_faciltators:{parent_ip:{_eq:"${parent_ip_id}"}}}}}`];
+ let filterQueryArray = [`{group_users: {member_type: {_eq: "owner"}, group: {program_id: {_eq: "${program_id}"}, academic_year_id: {_eq: "${academic_year_id}"}}, user: {program_faciltators: {parent_ip: {_eq: "${parent_ip_id}"}}}}}`];

This change ensures that all dynamic values are correctly encapsulated as strings within the query.

src/src/modules/auth/auth.service.ts (1)

Line range hint 800-821: Optimize the data structure and query handling.

The method newCreate constructs a large query with many fields. Consider breaking down the query into smaller, more manageable parts or functions to improve readability and maintainability.

src/src/user/user.service.ts (1)

1789-1789: Consider adding a comment to describe the purpose of the role_slug field.

Adding a brief comment explaining the purpose of role_slug can improve code readability and maintainability.

src/src/userauth/userauth.service.ts (1)

153-155: Avoid commented-out code in production as it can lead to confusion and clutter.

Consider removing or replacing the commented-out console logs with proper logging mechanisms if needed.

src/src/observations/observations.service.ts (2)

473-560: Improve logging for debugging and monitoring.

Consider adding logging statements in the updateFields method to log key actions and decisions. This will help in debugging issues and monitoring the flow of data through the method, especially in production environments.


650-734: Use appropriate HTTP status codes.

In the updateFieldResponses method, consider using more appropriate HTTP status codes for different types of responses. For example, use 400 for bad requests, 404 for not found, and 500 for internal server errors. This helps clients handle responses more effectively.

src/src/camp/camp.service.ts (5)

39-39: Consider adding a comment describing the purpose of returnFieldscamps for better code readability.


52-52: Consider adding a comment describing the purpose of returnFieldsGroups for better code readability.


197-197: The type field is hardcoded as 'pcr'. If this is intended to be dynamic or configurable, consider passing it as a parameter or fetching it from a configuration file.


Line range hint 387-431: The GraphQL query inside campList method is complex and handles multiple conditions. Consider breaking it down into smaller, more manageable functions or queries to improve readability and maintainability.


Line range hint 2064-2126: The pagination logic in getConsentBenficiaries seems to be manually implemented. Consider using existing libraries or frameworks that provide pagination features to reduce boilerplate code and improve reliability.

Comment on lines 259 to 263
const { mw_roles } = header;
const authToken = header.header('authorization');
const decoded: any = jwt_decode(authToken);
let keycloak_id = decoded.sub;
console.log('keycloak_id', keycloak_id);

Copy link

Choose a reason for hiding this comment

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

Consider using a more secure method to extract the JWT token.

The current method directly accesses the header which can be prone to security risks. It's recommended to use a dedicated method or middleware to safely extract and verify the JWT token.

Comment on lines 503 to 504
!body.role_fields.academic_year_id ||
!body.role_fields.role_slug
Copy link

Choose a reason for hiding this comment

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

Validate the presence of required fields before proceeding.

The code checks for the presence of several fields but does not handle the case where these fields might be missing or null, which could lead to runtime errors. Consider adding null checks or default values.

Comment on lines 628 to 630
if (body.role_fields.role_slug) {
body.role_slug = body.role_fields.role_slug;
}
Copy link

Choose a reason for hiding this comment

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

Use consistent field handling for role-specific data.

The handling of role_fields is inconsistent. For example, role_slug is directly assigned without checking its existence, unlike other fields. Refactor to ensure consistent handling:

- body.role_slug = body.role_fields.role_slug;
+ if (body.role_fields.role_slug) {
+     body.role_slug = body.role_fields.role_slug;
+ }

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.

Suggested change
if (body.role_fields.role_slug) {
body.role_slug = body.role_fields.role_slug;
}
if (body.role_fields.role_slug) {
body.role_slug = body.role_fields.role_slug;
}

Comment on lines 1568 to 1583

if (req.mw_roles?.includes('program_owner')) {
req.parent_ip_id = req.mw_ip_user_id;
} else {
const user = await this.userService.ipUserInfo(req);
if (req.mw_roles?.includes('staff')) {
req.parent_ip_id =
user?.data?.program_users?.[0]?.organisation_id;
} else if (req.mw_roles?.includes('facilitator')) {
req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
}
}
if (!req.parent_ip_id) {
return resp.status(404).send({
success: false,
message: 'Invalid User',
message: 'Invalid Ip',
Copy link

Choose a reason for hiding this comment

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

Ensure proper role handling and error messaging for unauthorized access.

-			return resp.status(404).send({
+			return resp.status(403).send({
				success: false,
				message: 'Unauthorized access',
				data: {},
			});

This change ensures that the correct HTTP status code (403 Forbidden) is used for unauthorized access instead of 404 Not Found, which can be misleading as it typically indicates that a resource does not exist. The error message has also been updated for clarity.


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.

Suggested change
if (req.mw_roles?.includes('program_owner')) {
req.parent_ip_id = req.mw_ip_user_id;
} else {
const user = await this.userService.ipUserInfo(req);
if (req.mw_roles?.includes('staff')) {
req.parent_ip_id =
user?.data?.program_users?.[0]?.organisation_id;
} else if (req.mw_roles?.includes('facilitator')) {
req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
}
}
if (!req.parent_ip_id) {
return resp.status(404).send({
success: false,
message: 'Invalid User',
message: 'Invalid Ip',
if (req.mw_roles?.includes('program_owner')) {
req.parent_ip_id = req.mw_ip_user_id;
} else {
const user = await this.userService.ipUserInfo(req);
if (req.mw_roles?.includes('staff')) {
req.parent_ip_id =
user?.data?.program_users?.[0]?.organisation_id;
} else if (req.mw_roles?.includes('facilitator')) {
req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
}
}
if (!req.parent_ip_id) {
return resp.status(403).send({
success: false,
message: 'Unauthorized access',
data: {},
});

Comment on lines 686 to 696
if (req.mw_roles?.includes('program_owner')) {
req.parent_ip_id = req.mw_ip_user_id;
} else {
const user = await this.userService.ipUserInfo(req);
if (req.mw_roles?.includes('staff')) {
req.parent_ip_id =
user?.data?.program_users?.[0]?.organisation_id;
} else if (req.mw_roles?.includes('facilitator')) {
req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
}
}
Copy link

Choose a reason for hiding this comment

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

Consider refactoring the role-based assignment of parent_ip_id to a separate method to improve readability and maintainability.

- if (req.mw_roles?.includes('program_owner')) {
-   req.parent_ip_id = req.mw_ip_user_id;
- } else {
-   const user = await this.userService.ipUserInfo(req);
-   if (req.mw_roles?.includes('staff')) {
-     req.parent_ip_id = user?.data?.program_users?.[0]?.organisation_id;
-   } else if (req.mw_roles?.includes('facilitator')) {
-     req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
-   }
- }
+ req.parent_ip_id = this.determineParentIpId(req);

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.

Suggested change
if (req.mw_roles?.includes('program_owner')) {
req.parent_ip_id = req.mw_ip_user_id;
} else {
const user = await this.userService.ipUserInfo(req);
if (req.mw_roles?.includes('staff')) {
req.parent_ip_id =
user?.data?.program_users?.[0]?.organisation_id;
} else if (req.mw_roles?.includes('facilitator')) {
req.parent_ip_id = user?.data?.program_faciltators?.parent_ip;
}
}
req.parent_ip_id = this.determineParentIpId(req);

Comment on lines 448 to 455
@Post('admin/multiple_end_pcr')
@UseGuards(new AuthGuard())
multiplePcrCampEnd(
@Req() request: any,
@Body() body: any,
@Res() response: any,
) {
return this.campService.multiplePcrCampEnd(body, request, response);
Copy link

Choose a reason for hiding this comment

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

Ensure consistent use of AuthGuard instantiation.

The method multiplePcrCampEnd incorrectly uses new AuthGuard(). Update this to @UseGuards(AuthGuard) to align with NestJS best practices and ensure consistent guard handling.

Comment on lines 368 to 403
public async updateWithVariable(
id: number,
tableName: String,
item: Object,
onlyFields: any = [],
fields: any = [],
props: any = {},
) {
return this.getResponce(
await lastValueFrom(
this.httpService
.post(
this.url,

this.qgService.updateWithVariable(
id,
tableName,
item,
onlyFields,
fields,
props,
),

{
headers: {
'x-hasura-admin-secret':
process.env.HASURA_ADMIN_SECRET,
'Content-Type': 'application/json',
},
},
)
.pipe(map((res) => res.data)),
),
tableName,
);
}
Copy link

Choose a reason for hiding this comment

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

Ensure proper error handling in asynchronous operations.

The method updateWithVariable lacks proper error handling. Currently, it logs the error but does not handle it in a way that could be useful for the caller. Consider throwing an exception or returning a specific error response to allow the caller to handle the error appropriately.

Comment on lines 55 to 78
// if (roles.includes('program_owner')) {
// if (req?.headers && req?.headers?.['x-ip-org-id']) {
// userId = req.headers['x-ip-org-id'];
// const result = await this.userService.getIPuser(
// req,
// res,
// );
// const data = result?.data?.program_users?.[0];
// req.mw_userid = data?.user_id;
// } else if (['/users/ip_user_info'].includes(req.baseUrl)) {
// // const user = await this.userService.ipUserInfo(req);
// userId = await this.userService.getUserIdFromKeycloakId(
// keycloak_id,
// );
// req.mw_userid = userId;
// }
// req.mw_roles = roles; //pass role if x-ip-org-id is not send
// } else {
// const user = await this.userService.ipUserInfo(req);
userId = await this.userService.getUserIdFromKeycloakId(
keycloak_id,
);
req.mw_userid = userId;
// }
Copy link

Choose a reason for hiding this comment

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

Commented out role-based user ID handling logic.

The removal of this block of code might affect functionalities that depend on role-based access control. Verify that the new implementation covers all required functionalities and that no security loopholes have been introduced.

Comment on lines 182 to 276
updateWithVariable(
id: number,
tName: String,
item: any,
onlyFields: any = [],
fields: any = [],
props: any = {},
) {
let tableName = `update_${tName}`;
const keys = Object.keys(item);
let resultObject = {};
let params = '';
const { variable } = props;

if (Array.isArray(variable) && variable?.length > 0) {
params = `(${variable
.filter((key) => item[key.key])
.map((newD) => `$${newD.key}: ${newD?.type}`)
.join(', ')})`;

let vData = {};
variable.forEach((e) => {
if (item?.[e.key]) {
vData = { ...vData, [e.key]: item?.[e.key] };
}
});
resultObject = { ...resultObject, variables: vData };
}
const getObjStr = (item: any, type: String = '') => {
let str = `where: ${
props?.where ? props?.where : `{id: {_eq: ${id}}}`
}, _set: {`;
let strArr = [];
keys.forEach((e, index) => {
if (
e !== 'id' &&
(onlyFields.length < 1 || onlyFields.includes(e))
) {
const data = variable.map((e) => e.key).filter((e) => e);
if (data.includes(e)) {
strArr = [...strArr, `${e}:$${e}`];
} else {
strArr = [...strArr, `${e}:"${item[e]}"`];
}
}
});
str += strArr.join();
str += `}`;
return str;
};

resultObject = {
query: `mutation MyQuery${params} {
${tableName}(${getObjStr(item)}) {
affected_rows
returning {
${this.getParam(
fields && fields.length > 0
? fields
: onlyFields
? onlyFields
: keys,
)}
}
}
}
`,
...resultObject,
};

let coreQuery = `${tableName}(${getObjStr(item)}) {
affected_rows
returning {
${this.getParam(
fields && fields.length > 0
? fields
: onlyFields
? onlyFields
: keys,
)}
}
}`;
if (props?.isCore === true) {
return coreQuery;
}

resultObject = {
query: `mutation MyQuery {
${coreQuery}
}`,
...resultObject,
};

return resultObject;
}
Copy link

Choose a reason for hiding this comment

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

Refactor the updateWithVariable method to improve readability and reduce complexity.

Consider breaking down this method into smaller functions, particularly separating the construction of the GraphQL query string from the logic that handles the variables. This will make the code easier to maintain and understand.

Comment on lines 28 to 34
quit: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}, attendances: {status: {_neq: "present"},context:{_eq:"camp_days_activities_tracker"}}}, _set: {end_camp_marked_by: "quit", end_date: "${today}"}) {
affected_rows
}
}
system: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) {
affected_rows
}
}
Copy link

Choose a reason for hiding this comment

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

Consider optimizing the GraphQL mutation to reduce redundancy.

- let updateQuery = `mutation MyMutation {
-   quit: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}, attendances: {status: {_neq: "present"},context:{_eq:"camp_days_activities_tracker"}}}, _set: {end_camp_marked_by: "quit", end_date: "${today}"}) {
-     affected_rows
-   }
-   system: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) {
-     affected_rows
-   }
- }`;
+ let updateQuery = `mutation MyMutation {
+   update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) {
+     affected_rows
+   }
+ }`;

This change consolidates the two updates into a single operation, assuming that the differentiation between 'quit' and 'system' as end_camp_marked_by is not required based on the current logic. If differentiation is needed, consider adding a condition within the where clause.


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.

Suggested change
quit: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}, attendances: {status: {_neq: "present"},context:{_eq:"camp_days_activities_tracker"}}}, _set: {end_camp_marked_by: "quit", end_date: "${today}"}) {
affected_rows
}
}
system: update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) {
affected_rows
}
}
update_camp_days_activities_tracker(where: {start_date: {_gte: "${yesterdayStartTime}", _lte: "${yesterdayEndTime}"}, end_date: {_is_null: true}}, _set: {end_camp_marked_by: "system", end_date: "${today}"}) {
affected_rows
}

Copy link

sonarcloud bot commented Apr 30, 2024

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
No data about Duplication

See analysis details on SonarCloud

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.

1 participant