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

Adding Api for Plugin using smaple_data in api #3294

Conversation

MayankJha014
Copy link

@MayankJha014 MayankJha014 commented Feb 24, 2025

This is api for fethching Plugin from SampleData

Related to : PalisadoesFoundation/talawa-admin#3559

Summary by CodeRabbit

  • New Features

    • The GraphQL API now enables you to retrieve plugin details. You can query for an individual plugin by name or fetch a list of all plugins.
    • Plugin data now includes key metadata such as creator, description, and a list of organizations that have uninstalled the plugin.
    • A new mutation allows you to update the installation status of a plugin for a specific organization.
  • Documentation

    • Updated documentation to reflect the new Plugin type and associated queries in the GraphQL schema.
  • Tests

    • Enhanced schema tests ensure the new plugin queries and related validations perform as expected.

Copy link

coderabbitai bot commented Feb 24, 2025

Walkthrough

This pull request introduces a new GraphQL entity for plugins. A Plugin type is added with fields for creator, description, name, and a list of uninstalled organizations. Two new query fields (getPlugins and plugin) and an associated input type (QueryPluginInput) have been added, with input validation implemented using the Zod library. Additionally, sample plugin data is provided in a JSON file, and the corresponding GraphQL schema and test type definitions have been updated. The plugin queries also include authentication checks and structured error handling.

Changes

File(s) Change Summary
schema.graphql, test/routes/graphql/gql.tada.d.ts Added new Plugin type with fields pluginCreatedBy, pluginDesc, pluginName, and uninstalledOrgs; introduced new query fields getPlugins: [Plugin!] and plugin(input: QueryPluginInput!): Plugin; and added the QueryPluginInput input type.
src/graphql/inputs/QueryPluginInput.ts Introduced a new Zod schema and input reference for QueryPluginInput, enforcing a non-empty string for pluginName.
src/graphql/types/Plugins/Plugins.ts, src/graphql/types/Plugins/pluginData.json Defined a new Plugin type with a Zod validation schema and GraphQL builder reference; added a JSON file containing an array of sample plugin objects.
src/graphql/types/Query/index.ts, src/graphql/types/Query/plugin.ts Added an import of the new plugin module in the query index and implemented new query resolvers for getPlugins and plugin with authentication checks, input validation, and structured error handling.

Possibly related PRs

Suggested labels

ignore-sensitive-files-pr

Suggested reviewers

  • palisadoes
  • xoldd
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

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

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

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: 12

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2438e61 and 365a783.

📒 Files selected for processing (8)
  • schema.graphql (3 hunks)
  • src/graphql/inputs/QueryPluginInput.ts (1 hunks)
  • src/graphql/types/Plugins/Plugins.ts (1 hunks)
  • src/graphql/types/Plugins/pluginData.json (1 hunks)
  • src/graphql/types/Query/index.ts (1 hunks)
  • src/graphql/types/Query/plugin.ts (1 hunks)
  • test/routes/graphql/gql.tada-cache.d.ts (1 hunks)
  • test/routes/graphql/gql.tada.d.ts (3 hunks)
🔇 Additional comments (4)
src/graphql/types/Query/index.ts (1)

22-22: LGTM!

The new plugin import is correctly placed and follows the established pattern.

src/graphql/inputs/QueryPluginInput.ts (1)

1-18: LGTM!

The implementation is well-structured and follows best practices:

  • Uses Zod for input validation
  • Properly implements the builder pattern
  • Includes appropriate field description
  • Has correct type inference
test/routes/graphql/gql.tada.d.ts (1)

143-143: LGTM!

The type definitions are correctly implemented:

  • Plugin type with all required fields
  • QueryPluginInput type with proper validation
  • Updated Query type with new plugin-related fields

Also applies to: 153-153, 165-165

test/routes/graphql/gql.tada-cache.d.ts (1)

47-48: LGTM! Well-structured tag query.

The tag query is well-defined with proper type safety and follows GraphQL best practices by requesting only the necessary fields.

Copy link

codecov bot commented Feb 24, 2025

Codecov Report

Attention: Patch coverage is 46.44550% with 113 lines in your changes missing coverage. Please review.

Project coverage is 49.43%. Comparing base (4f56a53) to head (825fbb2).

Files with missing lines Patch % Lines
src/graphql/types/Query/plugin.ts 26.62% 113 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##           develop-postgres    #3294      +/-   ##
====================================================
- Coverage             49.45%   49.43%   -0.02%     
====================================================
  Files                   462      465       +3     
  Lines                 34776    34987     +211     
  Branches               1038     1043       +5     
====================================================
+ Hits                  17199    17297      +98     
- Misses                17577    17690     +113     

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

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

🔭 Outside diff range comments (1)
test/routes/graphql/gql.tada.d.ts (1)

143-167: 🧹 Nitpick (assertive)

Add documentation for the new Plugin types.

Consider adding JSDoc comments to document:

  • The purpose of the Plugin type
  • Field descriptions and constraints
  • Usage examples for the plugin queries
+/**
+ * Represents a plugin in the system
+ * @field pluginCreatedBy - The creator of the plugin
+ * @field pluginDesc - Description of the plugin's functionality
+ * @field pluginName - Unique identifier for the plugin
+ * @field uninstalledOrgs - List of organizations where the plugin is not installed
+ */
'Plugin': { kind: 'OBJECT'; name: 'Plugin'; fields: {
♻️ Duplicate comments (2)
schema.graphql (2)

2731-2743: 🧹 Nitpick (assertive)

Ensure Consistent Non-null Constraints for Plugin Type Fields

While the new Plugin type correctly captures the intended fields, previous review comments suggested making required fields non-null to enforce type safety at the schema level. Consider updating the fields (especially pluginCreatedBy, pluginDesc, and pluginName) to use non-null types if they are always expected to have a value. For example:

-type Plugin {
-  """Creator of the plugin"""
-  pluginCreatedBy: String
-
-  """Description of the plugin"""
-  pluginDesc: String
-
-  """Name of the plugin"""
-  pluginName: String
-
-  """List of organization IDs where plugin is not installed"""
-  uninstalledOrgs: [String!]
-}
+type Plugin {
+  """Creator of the plugin"""
+  pluginCreatedBy: String!
+
+  """Description of the plugin"""
+  pluginDesc: String!
+
+  """Name of the plugin"""
+  pluginName: String!
+
+  """List of organization IDs where plugin is not installed"""
+  uninstalledOrgs: [String!]
+}

This adjustment improves type safety and data clarity in your GraphQL schema.


2890-2893: 🧹 Nitpick (assertive)

Add Description for the plugin Query Field

For consistency and better API documentation, add a description to the plugin query field. This is in line with previous suggestions. For example, add a description such as:

-  plugin(input: QueryPluginInput!): Plugin
+  """Query field to fetch a specific plugin by name"""
+  plugin(input: QueryPluginInput!): Plugin

This will help maintain consistency with the description already provided for the plugins query.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 365a783 and 57f0f51.

📒 Files selected for processing (3)
  • schema.graphql (3 hunks)
  • src/graphql/types/Query/index.ts (1 hunks)
  • test/routes/graphql/gql.tada.d.ts (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (2)
src/graphql/types/Query/index.ts (1)

22-22: LGTM!

The new plugin import follows the established pattern and is correctly ordered alphabetically.

test/routes/graphql/gql.tada.d.ts (1)

153-153: LGTM! Query type updates are well-structured.

The new plugin-related query fields are correctly integrated into the Query type:

  • plugin: Returns a single Plugin
  • plugins: Returns a list of non-null Plugin objects

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: 3

🔭 Outside diff range comments (1)
src/graphql/types/Query/plugin.ts (1)

1-110: 💡 Verification agent

🧩 Analysis chain

Run the formatter to fix pipeline failures.

Please address the pipeline error: "Formatter would have printed the following content: Code formatting issues detected. Please run the formatter to fix." Make sure to run the project's official formatting command or tool to pass the pipeline checks.


🏁 Script executed:

#!/bin/bash
# Attempt to fix code formatting issues by running the project's formatter
npm run format || yarn format || echo "Please manually run the correct formatter command for this project."

Length of output: 874


Action Required: Update Formatter Configuration

It appears that running the formatter is currently failing because there isn’t a "format" script defined in the project's package.json, and when attempting to use Yarn, Corepack isn’t enabled (resulting in a version mismatch). To address the pipeline formatting errors, please do one (or both) of the following:

  • Add a formatting script: Define a "format" script in package.json (e.g. using Prettier or your project’s formatting tool) so that the command (e.g. npm run format) runs successfully.
  • Enable Corepack: If you are using Yarn with Corepack, run corepack enable to activate the appropriate package manager version, then run the correct formatter command.

Once configured, please re-run the formatter to ensure that all code formatting issues are resolved and the pipeline passes.

🧰 Tools
🪛 GitHub Actions: Pull request workflow

[error] 6-6: Formatter would have printed the following content: Code formatting issues detected. Please run the formatter to fix.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1284926 and 5a86586.

📒 Files selected for processing (1)
  • src/graphql/types/Query/plugin.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/graphql/types/Query/plugin.ts (5)
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:6-11
Timestamp: 2025-02-24T17:27:31.612Z
Learning: In the Plugins module of talawa-api, field names in Plugin type maintain consistency with sample data format, using prefixes like 'pluginName', 'pluginCreatedBy', and 'pluginDesc'.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Query/plugin.ts:7-35
Timestamp: 2025-02-24T17:13:27.833Z
Learning: The GraphQL builder in Talawa API doesn't support Relay-style cursor-based pagination methods like `connectionType` and `resolveArrayConnection`. Any pagination implementation should use offset-based pagination with skip/limit arguments instead.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Query/plugin.ts:7-35
Timestamp: 2025-02-24T17:13:27.833Z
Learning: The Talawa API uses Pothos with Relay plugin for GraphQL schema building. Pagination should be implemented using Relay-style connections with the `t.connection()` method, which automatically adds standard Relay connection arguments and types.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:25-40
Timestamp: 2025-02-24T17:28:19.020Z
Learning: In the Talawa API GraphQL schema, field descriptions follow a concise pattern - simple fields use brief, clear descriptions while only complex fields or types receive more detailed documentation when necessary.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:25-40
Timestamp: 2025-02-24T17:28:19.020Z
Learning: In the Talawa API codebase, GraphQL type field descriptions follow a simple and concise pattern, avoiding overly detailed descriptions to maintain consistency across types.
🪛 GitHub Actions: Pull request workflow
src/graphql/types/Query/plugin.ts

[error] 6-6: Formatter would have printed the following content: Code formatting issues detected. Please run the formatter to fix.

🔇 Additional comments (2)
src/graphql/types/Query/plugin.ts (2)

43-46: Caching approach looks good.

Initializing a pluginCache improves performance by removing the need to traverse pluginsData each time. Do keep in mind that if the plugin data is dynamically updated, you’ll need an appropriate invalidation or refresh mechanism. For static data, this is a solid approach.


47-109: Robust validation and error handling for single plugin query.

The checks for empty plugin names and missing plugins are handled properly. Including clear invalid_arguments and arguments_associated_resources_not_found error codes improves maintainability and user feedback. Great job keeping the flow straightforward and consistent with the existing error-handling patterns.

Comment on lines +1 to +5
import { builder } from "~/src/graphql/builder";
import { QueryPluginInput } from "~/src/graphql/inputs/QueryPluginInput";
import { TalawaGraphQLError } from "~/src/utilities/TalawaGraphQLError";
import { PluginRef } from "../Plugins/Plugins";
import pluginsData from "../Plugins/pluginData.json" assert { type: "json" };
Copy link

@coderabbitai coderabbitai bot Feb 24, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Imports look fine, but please address formatting issues.

All imports appear valid and necessary. However, according to the pipeline failures, the codebase's formatting checks are failing on this file. Please ensure you run the project's formatter to resolve any style inconsistencies.

Copy link
Author

Choose a reason for hiding this comment

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

can you format it and i do change in my code

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's retrieve the full content of the file
cat src/graphql/types/Query/plugin.ts

# Check if there are specific formatting tools/commands used in the project
ls -la package.json
cat package.json | grep -E "format|lint" | grep -v "ignore"

Length of output: 4501


🏁 Script executed:

#!/bin/bash
# Check for common formatter/linter config files
ls -la .eslintrc* .prettierrc* .editorconfig* tsconfig.json

# Check package.json scripts to find format commands
jq '.scripts' package.json

# Try to find formatting-related dependencies
jq '.devDependencies' package.json | grep -E 'eslint|prettier|format'

Length of output: 2107


🏁 Script executed:

#!/bin/bash
# Check for biome configuration file
ls -la biome.json

# If biome configuration exists, display it
if [ -f biome.json ]; then
  cat biome.json
fi

# Try to run the project's formatter command on the file
pnpm fix_code_quality src/graphql/types/Query/plugin.ts || npm run fix_code_quality src/graphql/types/Query/plugin.ts

Length of output: 432


@MayankJha014 I ran the project's formatter command (pnpm fix_code_quality) on your file, but it reported "No fixes applied", which suggests the file is already correctly formatted according to the project's standards.

import { builder } from "~/src/graphql/builder";
import { QueryPluginInput } from "~/src/graphql/inputs/QueryPluginInput";
import { TalawaGraphQLError } from "~/src/utilities/TalawaGraphQLError";
import { PluginRef } from "../Plugins/Plugins";
import pluginsData from "../Plugins/pluginData.json" assert { type: "json" };

builder.queryField("getPlugins", (t) =>
	t.field({
		type: [PluginRef],
		description: "Fetch all available plugins",
		resolve: async (_parent, _args, ctx) => {
			try {
				// Check authentication
				if (!ctx.currentClient.isAuthenticated) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "unauthenticated",
						},
					});
				}

				return pluginsData;
			} catch (error) {
				if (error instanceof TalawaGraphQLError) {
					throw error;
				}
				throw new TalawaGraphQLError({
					extensions: {
						code: "unexpected",
					},
				});
			}
		},
	}),
);

// Create a cache based on plugin ID
const pluginCacheById = new Map(
	pluginsData.map((plugin) => [plugin.id, plugin]),
);

// Create a cache based on plugin name (case insensitive)
const pluginCacheByName = new Map(
	pluginsData.map((plugin) => [plugin.pluginName.toLowerCase(), plugin]),
);

builder.queryField("plugin", (t) =>
	t.field({
		type: PluginRef,
		args: {
			input: t.arg({
				type: QueryPluginInput,
				required: true,
			}),
		},
		resolve: async (_parent, { input }, ctx) => {
			try {
				// Check authentication
				if (!ctx.currentClient.isAuthenticated) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "unauthenticated",
						},
					});
				}

				// Validate input (either ID or pluginName must be provided)
				if (!input.pluginName?.trim() && !input.id?.trim()) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "invalid_arguments",
							issues: [
								{
									argumentPath: ["input"],
									message: "Either plugin ID or plugin name must be provided",
								},
							],
						},
					});
				}
				let plugin: (typeof pluginsData)[0] | undefined;

				// Lookup by ID first if provided, otherwise by plugin name
				if (input.id) {
					plugin = pluginCacheById.get(input.id);
				} else if (input.pluginName) {
					plugin = pluginCacheByName.get(input.pluginName.toLowerCase());
				}

				if (!plugin) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "arguments_associated_resources_not_found",
							issues: [
								{
									argumentPath: ["input"],
								},
							],
						},
					});
				}

				return plugin;
			} catch (error) {
				if (error instanceof TalawaGraphQLError) {
					throw error;
				}
				throw new TalawaGraphQLError({
					extensions: {
						code: "unexpected",
					},
				});
			}
		},
	}),
);

builder.mutationField("updatePluginStatus", (t) =>
	t.field({
		type: PluginRef,
		description: "Toggle plugin installation status for an organization",
		args: {
			pluginId: t.arg.string({ required: true }),
			orgId: t.arg.string({ required: true }),
		},
		resolve: async (_parent, { pluginId, orgId }, ctx) => {
			try {
				// Check authentication
				if (!ctx.currentClient.isAuthenticated) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "unauthenticated",
						},
					});
				}

				// Find plugin
				const plugin = pluginCacheById.get(pluginId);
				if (!plugin) {
					throw new TalawaGraphQLError({
						extensions: {
							code: "arguments_associated_resources_not_found",
							issues: [
								{
									argumentPath: ["pluginId"],
								},
							],
						},
					});
				}

				// Check if the org is already in uninstalledOrgs
				const isUninstalled = plugin.uninstalledOrgs.includes(orgId);

				if (isUninstalled) {
					// Remove from uninstalledOrgs (installing the plugin)
					plugin.uninstalledOrgs = plugin.uninstalledOrgs.filter(
						(org) => org !== orgId,
					);
				} else {
					// Add to uninstalledOrgs (uninstalling the plugin)
					plugin.uninstalledOrgs.push(orgId);
				}

				// Update cache
				pluginCacheById.set(pluginId, plugin);

				return plugin;
			} catch (error) {
				if (error instanceof TalawaGraphQLError) {
					throw error;
				}
				throw new TalawaGraphQLError({
					extensions: {
						code: "unexpected",
					},
				});
			}
		},
	}),
);

The code looks well-structured with consistent indentation and proper spacing. If you're still experiencing pipeline failures, it might be related to:

  1. Line ending issues (CRLF vs LF)
  2. Different formatting rules in CI vs local
  3. Other files that might need formatting

You can try running the full project formatter with pnpm fix_code_quality without specifying files to fix all formatting issues across the codebase.

@palisadoes
Copy link
Contributor

  1. Please make coderabbit.ai approves your work.
  2. Make sure all tests pass and are valid.
  3. Ensure the test code coverage for your patch reaches as close to 100% as possible.

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: 6

♻️ Duplicate comments (3)
src/graphql/types/Plugins/Plugins.ts (1)

15-21: 🧹 Nitpick (assertive)

Add maximum length constraints to string fields.

While minimum length validation is implemented, consider adding maximum length constraints to prevent potential issues with extremely long strings.

 export const pluginSchema = z.object({
 	id: z.string().uuid(),
-	pluginName: z.string().min(1),
-	pluginCreatedBy: z.string().min(1),
-	pluginDesc: z.string().min(1),
+	pluginName: z.string().min(1).max(100),
+	pluginCreatedBy: z.string().min(1).max(50),
+	pluginDesc: z.string().min(1).max(500),
 	uninstalledOrgs: z.array(z.string()),
 });
schema.graphql (2)

2734-2749: 🧹 Nitpick (assertive)

Add non-null constraints to required fields.

The Plugin type fields should be marked as non-null for required fields to ensure type safety at the GraphQL schema level.

 type Plugin {
   """Unique identifier for the plugin"""
   id: String
 
   """Creator of the plugin"""
-  pluginCreatedBy: String
+  pluginCreatedBy: String!
 
   """Description of the plugin"""
-  pluginDesc: String
+  pluginDesc: String!
 
   """Name of the plugin"""
-  pluginName: String
+  pluginName: String!
 
   """List of organization IDs where the plugin is not installed"""
   uninstalledOrgs: [String!]
 }

2899-2899: 🧹 Nitpick (assertive)

Add description for the plugin query field.

While the plugins query has a description, the plugin query lacks one. Add a description to maintain consistency and improve API documentation.

-  plugin(input: QueryPluginInput!): Plugin
+  """Query field to fetch a specific plugin by name or ID"""
+  plugin(input: QueryPluginInput!): Plugin
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a86586 and 5a1253d.

📒 Files selected for processing (6)
  • schema.graphql (4 hunks)
  • src/graphql/inputs/QueryPluginInput.ts (1 hunks)
  • src/graphql/types/Plugins/Plugins.ts (1 hunks)
  • src/graphql/types/Plugins/pluginData.json (1 hunks)
  • src/graphql/types/Query/plugin.ts (1 hunks)
  • test/routes/graphql/gql.tada.d.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
src/graphql/types/Plugins/pluginData.json (1)
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/pluginData.json:5-5
Timestamp: 2025-02-24T17:00:18.335Z
Learning: The plugin data in `src/graphql/types/Plugins/pluginData.json` is sample data taken from the develop branch and should not be modified for style/formatting improvements.
src/graphql/types/Plugins/Plugins.ts (2)
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:6-11
Timestamp: 2025-02-24T17:27:31.612Z
Learning: In the Plugins module of talawa-api, field names in Plugin type maintain consistency with sample data format, using prefixes like 'pluginName', 'pluginCreatedBy', and 'pluginDesc'.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:25-40
Timestamp: 2025-02-24T17:28:19.020Z
Learning: In the Talawa API codebase, GraphQL type field descriptions follow a simple and concise pattern, avoiding overly detailed descriptions to maintain consistency across types.
src/graphql/types/Query/plugin.ts (3)
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:6-11
Timestamp: 2025-02-24T17:27:31.612Z
Learning: In the Plugins module of talawa-api, field names in Plugin type maintain consistency with sample data format, using prefixes like 'pluginName', 'pluginCreatedBy', and 'pluginDesc'.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Query/plugin.ts:7-35
Timestamp: 2025-02-24T17:13:27.833Z
Learning: The GraphQL builder in Talawa API doesn't support Relay-style cursor-based pagination methods like `connectionType` and `resolveArrayConnection`. Any pagination implementation should use offset-based pagination with skip/limit arguments instead.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Query/plugin.ts:7-35
Timestamp: 2025-02-24T17:13:27.833Z
Learning: The Talawa API uses Pothos with Relay plugin for GraphQL schema building. Pagination should be implemented using Relay-style connections with the `t.connection()` method, which automatically adds standard Relay connection arguments and types.
test/routes/graphql/gql.tada.d.ts (2)
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:6-11
Timestamp: 2025-02-24T17:27:31.612Z
Learning: In the Plugins module of talawa-api, field names in Plugin type maintain consistency with sample data format, using prefixes like 'pluginName', 'pluginCreatedBy', and 'pluginDesc'.
Learnt from: MayankJha014
PR: PalisadoesFoundation/talawa-api#3294
File: src/graphql/types/Plugins/Plugins.ts:25-40
Timestamp: 2025-02-24T17:28:19.020Z
Learning: In the Talawa API codebase, GraphQL type field descriptions follow a simple and concise pattern, avoiding overly detailed descriptions to maintain consistency across types.
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run tests for talawa api
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (4)
src/graphql/inputs/QueryPluginInput.ts (1)

12-25: Implementation looks consistent.
The builder pattern correctly maps the Zod schema to a GraphQL input type, keeping id optional and pluginName required. No issues observed here.

src/graphql/types/Plugins/pluginData.json (1)

1-31: Acknowledged - no changes needed.
Since this file is sample data taken from the develop branch and should not be modified, we will not suggest any styling or data updates.

test/routes/graphql/gql.tada.d.ts (2)

153-153: Query additions look good.
The new query fields getPlugins and plugin properly reference the Plugin type. This aligns with the intended design for fetching and inspecting plugin data.


167-167: Input type is consistent with the Zod schema.
This matches the new QueryPluginInput schema defined in src/graphql/inputs/QueryPluginInput.ts, ensuring parity between GraphQL and runtime validation logic.

@@ -140,6 +140,7 @@ export type introspection_types = {
'OrganizationVenuesConnectionEdge': { kind: 'OBJECT'; name: 'OrganizationVenuesConnectionEdge'; fields: { 'cursor': { name: 'cursor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'node': { name: 'node'; type: { kind: 'OBJECT'; name: 'Venue'; ofType: null; } }; }; };
'PageInfo': { kind: 'OBJECT'; name: 'PageInfo'; fields: { 'endCursor': { name: 'endCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'hasNextPage': { name: 'hasNextPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'hasPreviousPage': { name: 'hasPreviousPage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'startCursor': { name: 'startCursor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; };
'PhoneNumber': unknown;
'Plugin': { kind: 'OBJECT'; name: 'Plugin'; fields: { 'id': { name: 'id'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'pluginCreatedBy': { name: 'pluginCreatedBy'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'pluginDesc': { name: 'pluginDesc'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'pluginName': { name: 'pluginName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'uninstalledOrgs': { name: 'uninstalledOrgs'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; } }; }; };
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider making essential fields non-null.
Fields like id, pluginName, and pluginCreatedBy could potentially be defined as NON_NULL for stronger type safety if they are always expected to be present.

@@ -0,0 +1,45 @@
import { z } from "zod";
// src/graphql/types/Plugin/Plugin.ts
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Fix incorrect file path in the comment.

The comment references src/graphql/types/Plugin/Plugin.ts (singular) but the actual path uses the plural form Plugins.

-// src/graphql/types/Plugin/Plugin.ts
+// src/graphql/types/Plugins/Plugins.ts
📝 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
// src/graphql/types/Plugin/Plugin.ts
// src/graphql/types/Plugins/Plugins.ts

Comment on lines +118 to +181
builder.mutationField("updatePluginStatus", (t) =>
t.field({
type: PluginRef,
description: "Toggle plugin installation status for an organization",
args: {
pluginId: t.arg.string({ required: true }),
orgId: t.arg.string({ required: true }),
},
resolve: async (_parent, { pluginId, orgId }, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}

// Find plugin
const plugin = pluginCacheById.get(pluginId);
if (!plugin) {
throw new TalawaGraphQLError({
extensions: {
code: "arguments_associated_resources_not_found",
issues: [
{
argumentPath: ["pluginId"],
},
],
},
});
}

// Check if the org is already in uninstalledOrgs
const isUninstalled = plugin.uninstalledOrgs.includes(orgId);

if (isUninstalled) {
// Remove from uninstalledOrgs (installing the plugin)
plugin.uninstalledOrgs = plugin.uninstalledOrgs.filter(
(org) => org !== orgId,
);
} else {
// Add to uninstalledOrgs (uninstalling the plugin)
plugin.uninstalledOrgs.push(orgId);
}

// Update cache
pluginCacheById.set(pluginId, plugin);

return plugin;
} catch (error) {
if (error instanceof TalawaGraphQLError) {
throw error;
}
throw new TalawaGraphQLError({
extensions: {
code: "unexpected",
},
});
}
},
}),
);
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Add persistence mechanism for plugin changes.

The current implementation modifies in-memory data with the updatePluginStatus mutation. These changes will be lost on server restart since they're not persisted to a database or file system.

Consider implementing a persistence mechanism to save plugin installation status changes:

  1. Use an existing database connection if available in the project
  2. Implement file-based persistence to update the JSON file
  3. Add a comment indicating this is demo-only functionality if persistence is out of scope

🏁 Script executed:

#!/bin/bash
# Check for database or persistence implementations in the codebase
echo "Searching for database connection configurations..."
rg -l "mongoose|prisma|typeorm|sequelize|knex" --type ts

echo -e "\nChecking if sample data is meant to be persisted or just for demo..."
grep -r "sampleData" --include="*.ts" --include="*.js" .

Length of output: 654


Ensure persistent storage for plugin status changes

The mutation implemented in src/graphql/types/Query/plugin.ts (lines 118-181) currently updates the plugin's installation status solely in-memory. Our repository search did not reveal any database connection configurations (e.g., mongoose, prisma, typeorm, sequelize, knex) that could be used for persistence, aside from sample data handling in ./scripts/dbManagement/helpers.ts. This means that any changes made via the updatePluginStatus mutation will be lost upon server restart.

To address this, please consider one of the following actions:

  • Integrate a persistence layer: Utilize an existing database connection or add a new one to store these changes persistently.
  • Implement file-based persistence: Update a JSON file or similar storage mechanism to retain the plugin status.
  • Clarify demo-only behavior: If persistence is intentionally out of scope, add a comment in the code to explicitly state that the changes are temporary.

Comment on lines +126 to +136
resolve: async (_parent, { pluginId, orgId }, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}

Copy link

@coderabbitai coderabbitai bot Feb 25, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add authorization checks for the updatePluginStatus mutation.

Currently, the mutation only checks if the user is authenticated but doesn't verify if they have permission to update the plugin status for a specific organization. Consider adding role-based authorization checks.

 resolve: async (_parent, { pluginId, orgId }, ctx) => {
 	try {
 		// Check authentication
 		if (!ctx.currentClient.isAuthenticated) {
 			throw new TalawaGraphQLError({
 				extensions: {
 					code: "unauthenticated",
 				},
 			});
 		}
+
+		// Check authorization - verify the user has admin rights for the organization
+		const isAdmin = await ctx.dataSource.userRoleForOrganization(
+			ctx.currentClient.userId, 
+			orgId
+		) === 'ADMIN';
+		
+		if (!isAdmin) {
+			throw new TalawaGraphQLError({
+				extensions: {
+					code: "unauthorized",
+					message: "Only organization administrators can update plugin installation status"
+				},
+			});
+		}

 		// Find plugin
📝 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
resolve: async (_parent, { pluginId, orgId }, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}
resolve: async (_parent, { pluginId, orgId }, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}
// Check authorization - verify the user has admin rights for the organization
const isAdmin = await ctx.dataSource.userRoleForOrganization(
ctx.currentClient.userId,
orgId
) === 'ADMIN';
if (!isAdmin) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthorized",
message: "Only organization administrators can update plugin installation status"
},
});
}
// Find plugin

Copy link
Author

Choose a reason for hiding this comment

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

in ctx dataSource is not available

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Let's first look at how the context object is used in the PR
rg "ctx\." src/graphql/types/Query/plugin.ts -A 2 -B 2

# Look for authorization patterns in mutation resolvers
rg "authorization|isAuthorized|hasPermission|isAdmin|userRole" src/graphql/types/ -A 5 -B 5

# Look at imports in the plugin.ts file to understand what's available
head -20 src/graphql/types/Query/plugin.ts

# Check how roles and permissions are typically checked in the codebase
rg "userRoleForOrganization|getUserRole|role.*organization" src/ -A 3 -B 3

Length of output: 9359


🏁 Script executed:

#!/bin/bash
# Let's look at the GraphQL context definition to understand its structure
rg -g "*.ts" "GraphQLContext|context" src/graphql --include="*.d.ts" --include="context.ts" -A 10 -B 10

# Look for examples of organization role checks in mutation resolvers
rg -g "*.ts" "organization.*role|isAdmin" src/graphql/types/Mutation -A 5 -B 5

# Let's check if there are examples of fetching organization memberships
rg -g "*.ts" "organizationMemberships" src/graphql/types/ -A 5 -B 5

# Look at how plugins and organization permissions are handled elsewhere
rg -g "*.ts" "plugin.*organization|organization.*plugin" src/ -A 5 -B 5

Length of output: 75586


@MayankJha014 You're right, the dataSource property isn't available in the context. Let me provide a correct implementation that follows the codebase's patterns for authorization checks:

// Check authorization - verify the user has admin rights for the organization
const currentUserId = ctx.currentClient.user.id;
const currentUser = await ctx.drizzleClient.query.usersTable.findFirst({
  with: {
    organizationMembershipsWhereMember: {
      columns: {
        role: true,
      },
      where: (fields, operators) =>
        operators.eq(fields.organizationId, orgId),
    },
  },
  where: (fields, operators) => operators.eq(fields.id, currentUserId),
});

const isGlobalAdmin = currentUser.role === "administrator";
const currentUserOrganizationMembership = 
  currentUser.organizationMembershipsWhereMember[0];
const isOrgAdmin = currentUserOrganizationMembership?.role === "administrator";

if (!isGlobalAdmin && !isOrgAdmin) {
  throw new TalawaGraphQLError({
    extensions: {
      code: "unauthorized_action",
      message: "Only organization administrators can update plugin installation status"
    },
  });
}

// Find plugin

This follows the authentication pattern used throughout the codebase, checking if the user is either a global admin or an admin of the specific organization.

Comment on lines +7 to +35
builder.queryField("getPlugins", (t) =>
t.field({
type: [PluginRef],
description: "Fetch all available plugins",
resolve: async (_parent, _args, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}

return pluginsData;
} catch (error) {
if (error instanceof TalawaGraphQLError) {
throw error;
}
throw new TalawaGraphQLError({
extensions: {
code: "unexpected",
},
});
}
},
}),
);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Implement pagination for the getPlugins query.

Currently, the query returns all plugins without pagination, which could be problematic as the number of plugins grows. Consider implementing pagination for better scalability.

 builder.queryField("getPlugins", (t) =>
 	t.field({
 		type: [PluginRef],
 		description: "Fetch all available plugins",
+		args: {
+			skip: t.arg.int({
+				description: "Number of records to skip",
+				required: false,
+				defaultValue: 0
+			}),
+			limit: t.arg.int({
+				description: "Number of records to fetch",
+				required: false,
+				defaultValue: 10
+			})
+		},
 		resolve: async (_parent, _args, ctx) => {
 			try {
 				// Check authentication
 				if (!ctx.currentClient.isAuthenticated) {
 					throw new TalawaGraphQLError({
 						extensions: {
 							code: "unauthenticated",
 						},
 					});
 				}
 
-				return pluginsData;
+				const { skip = 0, limit = 10 } = _args;
+				return pluginsData.slice(skip, skip + limit);
 			} catch (error) {
 				if (error instanceof TalawaGraphQLError) {
 					throw error;
 				}
📝 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
builder.queryField("getPlugins", (t) =>
t.field({
type: [PluginRef],
description: "Fetch all available plugins",
resolve: async (_parent, _args, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}
return pluginsData;
} catch (error) {
if (error instanceof TalawaGraphQLError) {
throw error;
}
throw new TalawaGraphQLError({
extensions: {
code: "unexpected",
},
});
}
},
}),
);
builder.queryField("getPlugins", (t) =>
t.field({
type: [PluginRef],
description: "Fetch all available plugins",
args: {
skip: t.arg.int({
description: "Number of records to skip",
required: false,
defaultValue: 0
}),
limit: t.arg.int({
description: "Number of records to fetch",
required: false,
defaultValue: 10
})
},
resolve: async (_parent, _args, ctx) => {
try {
// Check authentication
if (!ctx.currentClient.isAuthenticated) {
throw new TalawaGraphQLError({
extensions: {
code: "unauthenticated",
},
});
}
const { skip = 0, limit = 10 } = _args;
return pluginsData.slice(skip, skip + limit);
} catch (error) {
if (error instanceof TalawaGraphQLError) {
throw error;
}
throw new TalawaGraphQLError({
extensions: {
code: "unexpected",
},
});
}
},
}),
);

@Cioppolo14
Copy link
Contributor

@MayankJha014 Please fix the first comment so that each issue listed automatically closes. The PR_GUIDELINES.md file has details.

Please also fill in the template for the pull request as completely as you can. Please make sure all checks are passed.

@MayankJha014
Copy link
Author

ok sir

@MayankJha014
Copy link
Author

started working on it

@tasneemkoushar
Copy link
Contributor

tasneemkoushar commented Mar 3, 2025

@MayankJha014 this is no longer the requirement. Closing this PR & please go through the project idea list once. you will get the intent of the project. We are trying to change the whole architecture of the plugin in talawa. This apis adheres to the old design which is not very streamlines.

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.

4 participants