Display properties - STD fields#6786
Display properties - STD fields#6786naman-agrawal-shipsy wants to merge 134 commits intomakeplane:previewfrom
Conversation
fixed filter search issue
Fix for the redirecting to set password page issue
…y_properties--std-fields
|
|
WalkthroughThis pull request introduces extensive enhancements across both backend and frontend components. New commands and port declarations have been added in Dockerfiles, while numerous API endpoints, serializers, URL routes, and permission classes have been updated to support additional custom properties for issues. Database migrations add new models and fields for issue custom properties with revised constraints. In addition, configuration files, Next.js headers, and other settings have been modified. Frontend types and components now include new filtering and display options for these properties, and various authentication and workspace management flows have been refined. Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 44
🔭 Outside diff range comments (1)
aio/nginx.conf (1)
15-15: 🛠️ Refactor suggestionPotential conflict with new client_max_body_size setting.
Line 8 sets a global client_max_body_size of 50M while this line uses a variable ${FILE_SIZE_LIMIT}. This could lead to unexpected behavior if they differ.
Consider consolidating these settings by:
- Using the variable consistently in both places, or
- Removing the global setting and keeping only the server-specific one
- client_max_body_size 50M; + client_max_body_size ${FILE_SIZE_LIMIT};
🧹 Nitpick comments (92)
apiserver/plane/db/models/__init__.py (1)
44-44: New model imports added for custom propertiesAdded imports for
IssueCustomPropertyandIssueTypeCustomPropertymodels, which will make these models available throughout the application for handling custom properties for issues and issue types.According to the static analysis, these imported models may not be used directly. Consider either:
- Adding
__all__to explicitly list exported modules- Verifying these imports are actually needed
+# List of public symbols for star imports +__all__ = [ + "AnalyticView", + "APIActivityLog", + # ...include all other models + "IssueCustomProperty", + "IssueTypeCustomProperty", +]Also applies to: 115-115
🧰 Tools
🪛 Ruff (0.8.2)
44-44:
.issue.IssueCustomPropertyimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
web/app/[workspaceSlug]/(projects)/header.tsx (1)
3-3: Remove or restore the commented-out imports
Since these imports are now commented out, confirm they are unused. If they are indeed unnecessary, removing them entirely is cleaner than leaving commented code.-// import Image from "next/image"; -// import githubBlackImage from "/public/logos/github-black.png"; -// import githubWhiteImage from "/public/logos/github-white.png"; -// import { GITHUB_REDIRECTED } from "@/constants/event-tracker";Also applies to: 7-8, 14-14
apiserver/plane/db/models/base.py (1)
46-48: Minor: Extra newlines addedExtra newlines have been added at the end of the file. While this doesn't affect functionality, it's worth ensuring it follows the project's style guidelines.
Consider maintaining a consistent style for end-of-file newlines across the project.
docker-compose-local.yml (2)
198-198: Unused volume definition.The
rabbitmq_datavolume is still defined but no longer used since the plane-mq service has been removed.Consider removing the unused
rabbitmq_datavolume definition:volumes: redisdata: uploads: pgdata: - rabbitmq_data:
1-173:Details
❓ Verification inconclusive
RabbitMQ service (plane-mq) has been removed.
The RabbitMQ message queue service has been completely removed from the docker-compose configuration. This suggests a significant architectural change in how the application handles messaging.
This change may require corresponding updates in the application code if it previously relied on RabbitMQ for message processing. Consider reviewing the application's message processing logic to ensure it's compatible with this change.
🏁 Script executed:
#!/bin/bash # Check for RabbitMQ references in the codebase that might need updating echo "Checking for RabbitMQ references in the codebase..." rg -i "rabbitmq|amqp" --type py ./apiserver/Length of output: 1057
Action Required: Update Messaging Configuration
The removal of the RabbitMQ (plane-mq) service from the Docker Compose file indicates a significant shift in how messaging is handled. However, our grep shows that RabbitMQ settings are still present in the application configuration (notably in
apiserver/plane/settings/common.py). Please verify whether these settings should be removed or updated to match the new messaging architecture. If you're transitioning to a different messaging broker, update the corresponding environment variables and connection logic accordingly; otherwise, ensure that any external RabbitMQ services required by these settings are appropriately managed.apiserver/plane/utils/constants.py (1)
37-48: Consider a more scalable approach for workspace-specific propertiesThe implementation uses a hardcoded mapping between workspace name ("heineken") and allowed custom properties. While this works for the immediate need, it has some limitations:
- Hardcoding client names in the codebase creates tight coupling between code and specific customers
- Adding properties for new workspaces requires code changes
- May not scale well if many workspaces need different custom properties
Consider implementing one of these more flexible approaches:
- Store this configuration in the database instead of code
- Create an admin interface for managing custom properties per workspace
- Use a configuration file that can be updated without code changes
apiserver/plane/authentication/adapter/base.py (1)
175-175: Unnecessary blank line addedThere's an extra blank line added that doesn't appear to serve any purpose.
- +apiserver/plane/api/serializers/project.py (1)
12-12: Remove unused importThe
IssueTypeSerializeris imported but not used anywhere in this file. Removing unused imports helps maintain clean code and prevents confusion about dependencies.-from .issue_type import IssueTypeSerializer🧰 Tools
🪛 Ruff (0.8.2)
12-12:
.issue_type.IssueTypeSerializerimported but unusedRemove unused import:
.issue_type.IssueTypeSerializer(F401)
web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx (2)
40-40: Consider defining these filters in a constants fileThe
additionalFiltersarray contains what appear to be business-specific filter keys. Consider moving this to a dedicated constants file to improve maintainability and reusability across the application.
139-145: Implement consistent error handling for missing valuesThe conditional rendering for additional properties doesn't have the same null/empty array checks that other filter types have. Consider adding similar validation as done in lines 69-70 to ensure consistent behavior.
{additionalFilters?.includes(filterKey) && ( + Array.isArray(value) && value.length > 0 && ( <AppliedAdditionalPropertiesFilters editable={isEditingAllowed} handleRemove={(val) => handleRemoveFilter(filterKey, val)} values={value} /> + ) )}web/core/components/issues/issue-layouts/filters/applied-filters/additional-properties.tsx (1)
14-33: Consider adding a check for empty values arrayThe component renders filter items but doesn't handle the case when
valuesmight be empty. Consider adding a check to handle this case more gracefully, though this might be handled by the parent component.Also, note that the
editableprop is defined in the interface but not used in the component implementation. Either use it to conditionally render the remove button or remove it from the Props interface.export const AppliedAdditionalPropertiesFilters: React.FC<Props> = observer((props) => { - const { handleRemove, values } = props; + const { handleRemove, values, editable } = props; return ( <> {values.map((element) => ( <div key={element} className="flex items-center gap-1 rounded bg-custom-background-80 p-1 text-xs"> {element} + {editable && ( <button type="button" className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(element)} > <X size={10} strokeWidth={2} /> </button> + )} </div> ))} </> ); });apiserver/plane/app/views/base.py (1)
123-124: Improved debugging output for excessive database queries.Adding detailed query logging when query count exceeds 20 is good for debugging performance issues, but consider using a logging framework instead of print statements for better control.
if len(connection.queries) > 20: - print(connection.queries) + import logging + logger = logging.getLogger(__name__) + logger.debug(f"Excessive queries detected: {len(connection.queries)}") + for i, query in enumerate(connection.queries): + logger.debug(f"Query {i+1}: {query['sql']} (took: {query['time']}s)")apiserver/plane/api/serializers/webhook.py (1)
18-69: Refactor duplicate hostname resolution and validation logic.Both
create()and the beginning part ofupdate()perform domain resolution checks. Extracting this logic into a shared helper method would reduce duplication and ease future maintenance.def _validate_hostname(self, url, request=None): # common logic for hostname extraction, resolution, loopback checks, domain restrictions def create(self, validated_data): url = validated_data.get("url", None) - # Extract the hostname, resolve and validate, etc. + self._validate_hostname(url, self.context.get("request")) return Webhook.objects.create(**validated_data) def update(self, instance, validated_data): url = validated_data.get("url", None) if url: - # Repeated logic + self._validate_hostname(url, self.context.get("request")) return super().update(instance, validated_data)apiserver/plane/settings/storage.py (3)
16-17: Ensure these class variables are properly documentedAdding these class variables could change the behavior of file storage. The
file_overwritesetting prevents files with the same name from being overwritten, while the emptylocationsets the base storage directory to root.Consider adding a docstring explaining these settings and their implications for the storage behavior.
49-49: Forced HTTPS scheme might cause issues in certain environmentsAlways using
https://for the endpoint URL regardless of the original request scheme could cause problems in development or test environments that use HTTP.Consider preserving the original request scheme:
- f"https://{request.get_host()}" + f"{request.scheme}://{request.get_host()}"
91-92: Remove unnecessary blank linesThese added blank lines within the
generate_presigned_postmethod don't serve a functional purpose and affect code style consistency.try: - # Generate a presigned URL for the S3 object response = self.s3_client.generate_presigned_post( ... ) -Also applies to: 100-100
apiserver/plane/api/serializers/issue_type.py (2)
1-2: Unused import detectedThe
serializersimport fromrest_frameworkisn't directly used.-from rest_framework import serializers +from rest_framework import serializers # TODO: Remove if unusedOr remove it completely if not needed.
🧰 Tools
🪛 Ruff (0.8.2)
1-1:
rest_framework.serializersimported but unusedRemove unused import:
rest_framework.serializers(F401)
26-44: Check indentation consistency in IssueTypeCustomPropertySerializerThere's inconsistent indentation in the Meta class (line 39 has extra spaces).
class Meta: model = IssueTypeCustomProperty fields = "__all__" read_only_fields = [ "id", "issue_type", "created_at", "updated_at", "created_by", "updated_by", "deleted_at" ] - + def create(self, validated_data):apiserver/plane/bgtasks/webhook_task.py (1)
405-405: Remove unnecessary blank lineThis added blank line doesn't serve a functional purpose and affects code style consistency.
if event == "issue_comment": webhooks = webhooks.filter(issue_comment=True) - for webhook in webhooks:web/core/services/workspace.service.ts (1)
292-300: Fix the extra space in parameter definitionThere's an unnecessary space after
projectId: stringin the parameter list.- async getIssueAdditionalProperties(workspaceSlug: string, projectId: string , field: any): Promise<any> { + async getIssueAdditionalProperties(workspaceSlug: string, projectId: string, field: any): Promise<any> {apiserver/plane/middleware/api_log_middleware.py (1)
22-32: Remove or implement commented-out codeThis commented-out
process_viewmethod seems to be a work in progress or abandoned functionality. Either fully implement it or remove it to keep the codebase clean.apiserver/plane/app/permissions/project.py (2)
49-49: Remove debug print statementThis print statement appears to be for debugging purposes and should not be included in production code.
- print("Project ID:: %s" % view.project_id)
119-152: Potential duplication with ProjectEntityPermission classThe new
ProjectEntityGuestPermissionclass is nearly identical toProjectEntityPermission. Consider if this duplication is necessary or if the existing class could be modified to handle both cases.You could potentially refactor these classes to share common code or use a base class with specific permission implementations.
demo.Jenkinsfile (2)
158-228: Consider adding rollback or error-handling logic for failed deployments.
When deploying multiple services in parallel, a single failure might leave the system in a partially updated state. Incorporating rollback or clear error-handling processes will ensure more robust deploys.
231-237: Slack post-build notification is fine, but verify environment label.
Currently labeled "prod"; you might prefer something aligned with "demo" for consistency, or confirm that "prod" is intended.apiserver/plane/db/models/issue_type.py (2)
62-70: Fix typo in the related_name field.The
related_namein theissue_typeforeign key contains a typographical error - "custom_propery_issue_type" should be "custom_property_issue_type".issue_type = models.ForeignKey( "db.IssueType", on_delete=models.CASCADE, - related_name="custom_propery_issue_type", + related_name="custom_property_issue_type", )
71-83: Fix typo in database table name.The
db_tablein the Meta class has a typographical error - "isssue_type_custom_properties" (with three 's') should be "issue_type_custom_properties".class Meta: unique_together = ["issue_type", "name", "deleted_at"] constraints = [ models.UniqueConstraint( fields=["issue_type", "name"], condition=models.Q(deleted_at__isnull=True), name="issue_type_custom_property_unique_type_name_when_deleted_at_null", ) ] verbose_name = "Issue Type Custom Property" verbose_name_plural = "Issue Type Custom Properties" - db_table = "isssue_type_custom_properties" + db_table = "issue_type_custom_properties" ordering = ("-created_at",)apiserver/plane/api/middleware/api_authentication.py (3)
27-37: Enhance the validate_api_token method documentation.The method has been updated to accept an additional parameter, but lacks documentation explaining the purpose and usage of this parameter. Additionally, there's a need to explain the new behavior when using the static API token.
def validate_api_token(self, token, assume_role_value=None): + """ + Validate the API token and return the associated user + + Args: + token: The API token to validate + assume_role_value: Optional username to assume that user's role when using a static token + + Returns: + A tuple of (user, token) + """ # Check if the token matches the static token from settings User = get_user_model() if token == settings.STATIC_API_TOKEN: if assume_role_value: user = User.objects.filter(username=assume_role_value).first() else: user = User.objects.filter(is_superuser=True).first() self.rewite_project_id_in_url() return (user, token)
54-57: Fix method name typo and remove debugging code.There's a typo in the method name ("rewite" → "rewrite") and a commented-out debugging statement that should be removed.
-def rewite_project_id_in_url(self): +def rewrite_project_id_in_url(self): pass - # import pdb;pdb.set_trace()Also, update the reference to this method in
validate_api_token:if token == settings.STATIC_API_TOKEN: if assume_role_value: user = User.objects.filter(username=assume_role_value).first() else: user = User.objects.filter(is_superuser=True).first() - self.rewite_project_id_in_url() + self.rewrite_project_id_in_url() return (user, token)
64-66: Remove debugging print statement.Debugging print statements should not be present in production code as they can expose sensitive information in logs.
# Validate the API token assume_role_value = request.headers.get(self.assume_header_role, None) - print("assume_role",assume_role_value)apiserver/plane/settings/common.py (3)
33-35: Remove debugging print statement and use standard .env filename.Debugging print statements should not be in production code. Also, use the standard ".env" filename instead of "file.env".
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -print(f"BASE_DIR: {BASE_DIR}") -load_dotenv(os.path.join(BASE_DIR, "file.env")) +load_dotenv(os.path.join(BASE_DIR, ".env"))
365-368: Clean up commented-out code and clarify session cookie settings.There are commented-out settings that create confusion about the intended behavior. Clean up the commented code and ensure the settings are clear.
-# SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = secure_origins -# SESSION_COOKIE_SAMESITE = "None" +SESSION_COOKIE_SAMESITE = "None" if secure_origins else "Lax" SESSION_COOKIE_HTTPONLY = True
382-385: Clean up commented-out code and clarify CSRF cookie settings.Similar to the session cookie settings, the CSRF cookie settings have commented-out code that creates confusion. Clean up the commented code and ensure the settings are clear.
-# CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = secure_origins -# CSRF_COOKIE_SAMESITE = "None" +CSRF_COOKIE_SAMESITE = "None" if secure_origins else "Lax" CSRF_COOKIE_HTTPONLY = Falseweb/core/constants/issue.ts (2)
113-120: Typo in constant name: "whileListedCustomProperties"The constant name appears to have a typo. It should probably be
whiteListedCustomPropertiesinstead ofwhileListedCustomPropertiesto better represent its purpose as a list of approved custom properties.-const whileListedCustomProperties = [ +const whiteListedCustomProperties = [ { key: "hub_code", title: "Hub Code" }, { key: "customer_code", title: "Customer Code" }, { key: "worker_code", title: "Worker Code" }, { key: "vendor_code", title: "Vendor Code" }, { key: "trip_reference_number", title: "Trip Reference Number" }, { key: "reference_number", title: "Reference Number" }, ];
456-464: Remove duplicate "custom_properties" entry in filtersThere are duplicate entries for
custom_propertiesin the filter list (lines 456 and 463). This redundancy could lead to confusion and potentially issues when applying these filters.filters: [ "priority", "state", "cycle", "module", "assignees", "mentions", "created_by", "labels", "start_date", "target_date", "issue_type", "custom_properties", "hub_code", "customer_code", "worker_code", "vendor_code", "trip_reference_number", "reference_number", - "custom_properties" ],apiserver/plane/api/urls/issue.py (1)
9-10: Remove unused import: IssueTypeAPIEndpointThe
IssueTypeAPIEndpointis imported but not used anywhere in this file.from plane.api.views import ( IssueAPIEndpoint, LabelAPIEndpoint, IssueLinkAPIEndpoint, IssueCommentAPIEndpoint, IssueActivityAPIEndpoint, WorkspaceIssueAPIEndpoint, IssueAttachmentV2Endpoint, - IssueTypeAPIEndpoint )🧰 Tools
🪛 Ruff (0.8.2)
10-10:
plane.api.views.IssueTypeAPIEndpointimported but unusedRemove unused import:
plane.api.views.IssueTypeAPIEndpoint(F401)
apiserver/plane/api/views/__init__.py (6)
1-4: Remove or use unused imports.
ProjectAPIEndpointandProjectArchiveUnarchiveAPIEndpointare not referenced in this file. If these imports are intended for re-export or future use, consider adding them to__all__. Otherwise, removing them will help maintain a clean and concise import structure.-from .project import ( - ProjectAPIEndpoint, - ProjectArchiveUnarchiveAPIEndpoint -) +from .project import ( + ProjectAPIEndpoint, + ProjectArchiveUnarchiveAPIEndpoint, +) # Remove or utilize these imports if unneeded🧰 Tools
🪛 Ruff (0.8.2)
2-2:
.project.ProjectAPIEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
3-3:
.project.ProjectArchiveUnarchiveAPIEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
15-15: Remove or use unused import.
IssueAttachmentEndpointis imported but never used. Remove the import or add it to__all__if you plan to re-export it.- IssueAttachmentEndpoint🧰 Tools
🪛 Ruff (0.8.2)
15-15:
.issue.IssueAttachmentEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
17-17: Remove or use unused imports.
IssueTypeAPIEndpointandIssueTypeCustomPropertyAPIEndpointare unused imports. Removing them helps avoid confusion and clutter.-from .issue_type import IssueTypeAPIEndpoint,IssueTypeCustomPropertyAPIEndpoint🧰 Tools
🪛 Ruff (0.8.2)
17-17:
.issue_type.IssueTypeAPIEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
17-17:
.issue_type.IssueTypeCustomPropertyAPIEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
18-18: Remove or use unused import.
IssueAttachmentV2Endpointis imported but not used in this file. Either remove this import or use it.-from .attachment import IssueAttachmentV2Endpoint🧰 Tools
🪛 Ruff (0.8.2)
18-18:
.attachment.IssueAttachmentV2Endpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
36-36: Remove or use unused import.
GlobalSearchEndpointis imported but never referenced. Consider removing or re-exporting it if desired.-from .search import GlobalSearchEndpoint🧰 Tools
🪛 Ruff (0.8.2)
36-36:
.search.GlobalSearchEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
38-41: Remove or use unused webhook imports.
WebhookEndpoint,WebhookLogsEndpoint, andWebhookSecretRegenerateEndpointare not referenced. Consider removing them or adding them to__all__if they are being re-exported.-from .webhook import ( - WebhookEndpoint, WebhookLogsEndpoint, - WebhookSecretRegenerateEndpoint -)🧰 Tools
🪛 Ruff (0.8.2)
39-39:
.webhook.WebhookEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
39-39:
.webhook.WebhookLogsEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
40-40:
.webhook.WebhookSecretRegenerateEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
apiserver/plane/api/views/base.py (2)
78-78: Avoid printing exceptions directly in production code.Printing
exccan clutter logs or leak sensitive info. Consider using a structured logger or removing it.- print(exc) + # If needed, log it properly or remove.
89-89: Separate multiple statements for clarity.Writing
import traceback; traceback.print_exc()in one line decreases readability and can trigger style warnings.- import traceback; traceback.print_exc() + import traceback + traceback.print_exc()🧰 Tools
🪛 Ruff (0.8.2)
89-89: Multiple statements on one line (semicolon)
(E702)
web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx (2)
248-259: Add a unique key to the<div>element in the map.React recommends placing the
keyprop on the top-level element of the mapped array. Currently, you passkey={prop.key}to<FilterAdditionalProperties>, but the lint warns about the<div>lacking a key. Consider movingkey={prop.key}to the<div>instead or making the<div>the direct child.-{ISSUE_ADDITIONAL_PROPERTIES.map((prop: any) => ( - <div className="py-2"> - <FilterAdditionalProperties - key={prop.key} +{ISSUE_ADDITIONAL_PROPERTIES.map((prop: any) => ( + <div key={prop.key} className="py-2"> + <FilterAdditionalProperties appliedFilters={filters[prop.key as keyof IIssueFilterOptions] ?? null} ...🧰 Tools
🪛 Biome (1.9.4)
[error] 249-249: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.(lint/correctness/useJsxKeyInIterable)
261-271: Validate commented-out code.Line 267 shows a commented-out handler. If it’s no longer needed, remove it to keep the codebase clean. Otherwise, consider a clarifying comment about its intended usage.
- // handleUpdate={(val) => handleFiltersUpdate("custom_properties", null, val)} + // TODO: Clarify or remove if no longer neededapiserver/plane/authentication/provider/credentials/magic_code.py (3)
87-88: Avoid hard-coding the domain.
Hard-coding"@plane-shipsy.com"limits the provider to a single domain. For a multi-tenant or more flexible setup, consider extracting the domain from configuration or parsing the incoming email domain more dynamically.
117-123: Avoid hard-coding the domain again.
As with lines 87–88, replacing"@plane-shipsy.com"directly might cause domain-related limitations. Consider using a configuration-driven or generic approach.
131-133: Remove sensitive debug prints.
Printing all Redis keys may reveal sensitive or private information (keys, emails, tokens). Consider removing or limiting these debug statements, or use a structured logger with masking.apiserver/plane/api/views/issue_type.py (3)
6-19: Remove unused imports.
The importsCase,CharField,Exists(here used below for annotation, so confirm usage),F,Func,Max,OuterRef,Q,Value,When, andSubqueryappear to be mostly unused. As flagged by static analysis, removing unused imports helps maintain clarity and reduce confusion.-from django.db.models import ( - Case, - CharField, - Exists, - F, - Func, - Max, - OuterRef, - Q, - Value, - When, - Subquery, -) +from django.db.models import Exists, OuterRef # Retain only the needed imports🧰 Tools
🪛 Ruff (0.8.2)
8-8:
django.db.models.Caseimported but unusedRemove unused import
(F401)
9-9:
django.db.models.CharFieldimported but unusedRemove unused import
(F401)
10-10:
django.db.models.Existsimported but unusedRemove unused import
(F401)
11-11:
django.db.models.Fimported but unusedRemove unused import
(F401)
12-12:
django.db.models.Funcimported but unusedRemove unused import
(F401)
13-13:
django.db.models.Maximported but unusedRemove unused import
(F401)
14-14:
django.db.models.OuterRefimported but unusedRemove unused import
(F401)
15-15:
django.db.models.Qimported but unusedRemove unused import
(F401)
16-16:
django.db.models.Valueimported but unusedRemove unused import
(F401)
17-17:
django.db.models.Whenimported but unusedRemove unused import
(F401)
18-18:
django.db.models.Subqueryimported but unusedRemove unused import
(F401)
140-141: Remove or use these variables.
requested_dataandcurrent_instanceare assigned but never used, indicating possible leftover debugging code or incomplete logic. Removing them will improve clarity and reduce warnings.- requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) - current_instance = json.dumps(IssueTypeSerializer(issue_comment).data, cls=DjangoJSONEncoder)🧰 Tools
🪛 Ruff (0.8.2)
140-140: Local variable
requested_datais assigned to but never usedRemove assignment to unused variable
requested_data(F841)
141-141: Local variable
current_instanceis assigned to but never usedRemove assignment to unused variable
current_instance(F841)
200-200: Remove unusedworkspacevariable.
workspaceis fetched but never used. Either remove the variable or use it if needed for further validations.- workspace = Workspace.objects.get(slug=slug)🧰 Tools
🪛 Ruff (0.8.2)
200-200: Local variable
workspaceis assigned to but never usedRemove assignment to unused variable
workspace(F841)
apiserver/Dockerfile.api (1)
63-65: Consider moving migrations to container startupRunning migrations at build time may not be ideal as the database state might change between image build and container startup.
Consider moving migrations to the CMD section instead of RUN, so they execute at container startup:
-RUN if [ "${ENV_TYPE}" = "apiserver" ]; then \ - python manage.py migrate --noinput; \ - fi CMD if [ "${ENV_TYPE}" = "apiserver" ]; then \ + python manage.py migrate --noinput && \ gunicorn -w 2 -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:8000 --max-requests 1200 --max-requests-jitter 1000 --access-logfile -; \web/core/components/issues/issue-layouts/filters/header/filters/custom-properties.tsx (5)
4-5: Remove commented-out importThe unused import for sortBy should be removed to keep the code clean.
- // import sortBy from "lodash/sortBy"; import { observer } from "mobx-react";
10-10: Remove commented-out importsUnused imports should be removed for code cleanliness.
- // import { Loader, StateGroupIcon } from "@plane/ui";
40-71: Improve error handling and loading stateThe component only logs errors to the console without informing the user of potential issues. Additionally, there's no loading state indication.
Add a loading state and better error handling:
const [groupedProperties, setGroupedProperties] = useState<Record<string, any[]>>({}); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState<string | null>(null); const appliedFiltersCount = appliedFilters?.length ?? 0; // ... React.useEffect(() => { const fetchCustomProperties = async (): Promise<void> => { + setIsLoading(true); + setError(null); try { const data = await workspaceService.getIssuesCustomProperties(workspaceSlug.toString()); // ... existing code ... } catch (error) { console.error("Error fetching custom properties:", error); + setError("Failed to load custom properties"); + } finally { + setIsLoading(false); } }; fetchCustomProperties(); }, [workspaceSlug]);Then in the render section, add loading and error states:
return ( <> <FilterHeader title={`Custom Properties${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={mainPreviewEnabled} handleIsPreviewEnabled={() => setMainPreviewEnabled(!mainPreviewEnabled)} /> + {isLoading && <div className="px-4 py-2 text-sm text-custom-text-200">Loading...</div>} + {error && <div className="px-4 py-2 text-sm text-red-500">{error}</div>} {mainPreviewEnabled && !isLoading && !error && ( // ... existing render code ... )} </> );
121-121: Simplify boolean expressionThe ternary expression returning true/false is unnecessary.
- isChecked={appliedFilters?.includes(`${groupKey}:${property}`) ? true : false} + isChecked={Boolean(appliedFilters?.includes(`${groupKey}:${property}`))}Or even simpler:
- isChecked={appliedFilters?.includes(`${groupKey}:${property}`) ? true : false} + isChecked={!!appliedFilters?.includes(`${groupKey}:${property}`)}🧰 Tools
🪛 Biome (1.9.4)
[error] 121-121: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
126-132: Improve "View all/View less" button accessibilityThe current button lacks proper styling and accessibility features.
Enhance the button:
{properties.length > 5 ? <button type="button" className="ml-8 text-xs font-medium text-custom-primary-100" + aria-expanded={renderMoreGroupItems[groupKey] ? "true" : "false"} + aria-controls={`${groupKey}-properties`} onClick={() => handleViewToggle(groupKey)} > {renderMoreGroupItems[groupKey] ? "View less" : "View all"} </button> : null}Also consider adding unique IDs to the property lists for better accessibility:
<div> {properties .slice(0, renderMoreGroupItems[groupKey] ? properties.length : 5) + .map((property, index) => ( - .map((property) => ( <FilterOption + id={`${groupKey}-property-${index}`} key={`${groupKey}:${property}`} isChecked={!!appliedFilters?.includes(`${groupKey}:${property}`)} onClick={() => handleUpdate(`${groupKey}:${property}`)} title={property} /> ))}apiserver/plane/app/views/view/base.py (2)
3-3: Unnecessary import.The import
django.db.models.Countis not used in this file.- from django.db.models import Count🧰 Tools
🪛 Ruff (0.8.2)
3-3:
django.db.models.Countimported but unusedRemove unused import:
django.db.models.Count(F401)
209-306: Modified get_queryset to handle custom property filtering.The query now takes a filters parameter and applies custom property filtering through a dynamic query expression. The implementation looks correct but could be refactored slightly for better readability.
Consider extracting the custom property filtering logic into a separate method for better readability:
def _apply_custom_property_filters(self, queryset, custom_properties): if not custom_properties: return queryset filters = [ Q(id__in=IssueCustomProperty.objects.filter( issue_id=OuterRef("id"), key=group_key, value__in=values ).values("issue_id")) for group_key, values in custom_properties.items() ] return queryset.filter(*filters) def get_queryset(self, filters): custom_properties = filters.get("custom_properties", {}) queryset = ( Issue.issue_objects.annotate( # ... existing annotations ) # ... existing filters and prefetches ) return self._apply_custom_property_filters(queryset, custom_properties)apiserver/plane/api/views/member.py (2)
271-271: Remove debug print statement.This print statement should be removed before production deployment.
- print(created_bys)
286-302: Remove commented code in the filter method.The commented-out code isn't necessary and should be removed for cleaner production code.
def filter_created_by_username(params, issue_filter, method, prefix=""): if method == "GET": created_bys = [ item for item in params.get("created_by_username").split(",") if item != "null" ] if len(created_bys) and "" not in created_bys: issue_filter[f"{prefix}created_by__username__in"] = created_bys - # else: - # if ( - # params.get("created_by", None) - # and len(params.get("created_by")) - # and params.get("created_by") != "null" - # ): - # issue_filter[f"{prefix}created_by__in"] = params.get("created_by") return issue_filterapiserver/plane/utils/issue_filters.py (2)
6-6: Remove unused import.The
Qimport fromdjango.db.modelsis not being used in the active code.-from django.db.models import Q🧰 Tools
🪛 Ruff (0.8.2)
6-6:
django.db.models.Qimported but unusedRemove unused import:
django.db.models.Q(F401)
563-591: Remove commented code and debug print statement.The implementation contains commented-out code and a debug print statement that should be removed before production deployment.
def filter_custom_properties(params, issue_filter, method, prefix=""): if method == "GET": custom_properties = [ item for item in params.get("custom_properties").split(",") if item != "null" ] - # query = Q() - # for row in custom_properties: - # key, value = row.split(":") - # query &= Q( - # Q(custom_properties__project_custom_property_id=key) & - # Q(custom_properties__value=value) - # ) - - # issue_filter['base'] = query groupedCustomProperties = {} for row in custom_properties: key, value = row.split(':') if key not in groupedCustomProperties: groupedCustomProperties[key] = [] groupedCustomProperties[key].append(value) issue_filter['custom_properties'] = groupedCustomProperties - print(issue_filter) return issue_filterapiserver/plane/api/views/attachment.py (3)
9-9: Remove unused import.The import
allow_permissionat line 9 is never used. Removing it helps keep the codebase clean.- from plane.app.permissions import ProjectEntityPermission, allow_permission + from plane.app.permissions import ProjectEntityPermission🧰 Tools
🪛 Ruff (0.8.2)
9-9:
plane.app.permissions.allow_permissionimported but unusedRemove unused import:
plane.app.permissions.allow_permission(F401)
26-38: Validate file type more robustly.Currently, this check only compares the MIME type to a predefined list. It might be beneficial to handle edge cases (e.g., empty strings or missing file type). Consider adding fallback checks or error messages that explicitly guide the user to pass a valid file type.
106-121: Consider returning JSON for unuploaded attachments.Instead of returning a 400 with a static "The asset is not uploaded." message, consider including metadata about why the asset is not available or when it might become available. This can help front-end consumers provide more context to users.
apiserver/plane/api/serializers/issue.py (3)
36-42: Remove assignment to unused variable inis_uuid().The variable
uuid_objis never used. You can remove the assignment and rely on the exception check alone.def is_uuid(value): try: - uuid_obj = uuid.UUID(str(value)) + uuid.UUID(str(value)) return True except (ValueError, TypeError): return False🧰 Tools
🪛 Ruff (0.8.2)
38-38: Local variable
uuid_objis assigned to but never usedRemove assignment to unused variable
uuid_obj(F841)
179-179: Remove debugging print statements.There is a
print(data)call at line 176 (visible in your snippet). Such debug prints in production code can clutter logs. Consider removing or using proper logging.
273-314: Retaining older custom properties vs. overwriting.Here, existing
IssueCustomPropertyentries are deleted before new ones are created. If partial updates are expected, consider a patch-like approach to avoid discarding unmodified entries.apiserver/plane/bgtasks/issue_activities_task.py (1)
370-380: Graceful handling of unknown user IDs.
set([str(asg) for asg in ...])might raise an error if theadded_asigneeordropped_assigneedoes not exist as a valid user. Consider safeguarding with a try-except or an existence check to avoid unhandled exceptions.apiserver/plane/app/views/issue/base.py (7)
60-60: Remove unused import
This import is not referenced anywhere and should be removed.- from plane.utils.constants import ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAP🧰 Tools
🪛 Ruff (0.8.2)
60-60:
plane.utils.constants.ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAPimported but unusedRemove unused import:
plane.utils.constants.ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAP(F401)
71-75: Unused imports
ProjectEntityPermission,ProjectLitePermission, andProjectMemberPermissionappear unused in this file.- from plane.app.permissions import ( - ProjectEntityPermission, - ProjectLitePermission, - ProjectMemberPermission, - )🧰 Tools
🪛 Ruff (0.8.2)
72-72:
plane.app.permissions.ProjectEntityPermissionimported but unusedRemove unused import
(F401)
73-73:
plane.app.permissions.ProjectLitePermissionimported but unusedRemove unused import
(F401)
74-74:
plane.app.permissions.ProjectMemberPermissionimported but unusedRemove unused import
(F401)
235-285: Custom property filtering logic
These subqueries correctly filter issues by matching each custom property. However, for large queries or numerous filters, it might become expensive. Consider adding database indexes on(issue_id, key, value)or exploring more efficient filtering mechanisms.
439-439: Potential debug leftover
Printing serializer data can clutter logs. Remove this statement before production.- print(serializer.data)
499-499: Potential debug leftover
Printing the issue is likely for debugging. Consider removing to keep logs clean.- print(issue)
1218-1260: SearchAPIEndpoint
The logic is correct, but constructing a set in memory may be costly for large datasets. Consider using database-level distinct operations or excluding nulls in the query to reduce memory usage and improve scalability.- values = Issue.objects.filter( - workspace__slug=slug, project_id=project_id, - ).values_list(field, flat=True) - unique_values = list(set(filter(None, values))) + unique_values_qs = Issue.objects.filter( + workspace__slug=slug, project_id=project_id + ).exclude(**{field + "__isnull": True}).values_list(field, flat=True).distinct() + unique_values = list(unique_values_qs)
1261-1297: SearchSingleValueAPI
Combining exact matches with partial matches is logical. For very large datasets, consider additional database filtering or.distinct()usage to avoid potentially large in-memory sets.apiserver/plane/api/views/webhook.py (1)
32-38: Catching IntegrityError
Relying on the substring"already exists"in the exception message can be fragile. When possible, detect uniqueness violations using constraint names or a more robust logic that doesn't depend on specific database error strings.apiserver/plane/api/views/issue.py (3)
36-36: Remove unused import
IssueTypeSerializeris never referenced; consider removing it.- from plane.api.serializers import IssueTypeSerializer🧰 Tools
🪛 Ruff (0.8.2)
36-36:
plane.api.serializers.IssueTypeSerializerimported but unusedRemove unused import:
plane.api.serializers.IssueTypeSerializer(F401)
55-55: Remove unused import
IssueTypeis not used in this file and can be removed.- from plane.db.models import IssueType🧰 Tools
🪛 Ruff (0.8.2)
55-55:
plane.db.models.IssueTypeimported but unusedRemove unused import:
plane.db.models.IssueType(F401)
151-151: Debug logging
print(query)looks like a leftover debug statement. Consider removing it for production.- print(query)apiserver/plane/api/views/search.py (6)
37-49: Consider limiting or paginating workspace search results.
If your dataset grows large, returning all matching workspaces could degrade performance and increase payload size. Paginating or limiting the query would enhance responsiveness at scale.
67-100: Evaluate uniform result size limits for issues.
Here, you limit issues to 100 results ([:100]) but other filter methods return an unbounded list. You may want consistent limits or pagination if you anticipate large datasets.
78-78: Confirm that searching within custom properties is indexed.
Performingicontainsqueries for JSON-like fields (custom_properties__value) can be expensive in PostgreSQL if not indexed properly. Creating functional or GIN indexes on JSON fields can improve performance.
126-150: Improve readability of filter construction.
Repetitive or logic-heavy query building can obscure intent. Consider extracting common filter-building code into a helper method to keep your code DRY.
205-229: Offer a consistent user experience for view searches.
Similar to other filters, consider adding a limit or pagination for views if your user base grows; it will keep responses predictable and efficient.
230-269: Add optional searching of workspace and project results.
Currently, theMODELS_MAPPERonly searches issues. If you want users to search across other entities (like workspaces, projects, cycles, etc.), uncomment or expand theMODELS_MAPPERto include them.apiserver/plane/authentication/views/app/magic.py (3)
88-101: Separate workspace logic from sign-in flow.
Adding users to a workspace in the middle of the authentication flow can obscure responsibilities, complicating maintenance. Consider a post-sign-in hook or dedicated function to handle workspace membership.
290-290: Remove unused variableis_app.
Static analysis indicates it’s never referenced. Reducing unused variables maintains lean, clean code.- is_app = request.POST.get("is_app", False)🧰 Tools
🪛 Ruff (0.8.2)
290-290: Local variable
is_appis assigned to but never usedRemove assignment to unused variable
is_app(F841)
186-261: Use consistent error handling for sign-in logic.
The exception flow is partly handled via redirects, but repeated blocks of code can be prone to duplication. Centralizing such logic or employing a shared exception handler can reduce maintenance overhead.apiserver/plane/db/models/issue.py (1)
142-147: Optional fields may need indexing if frequently queried.
The newly introduced fieldshub_code,vendor_code,customer_code,worker_code,reference_number, andtrip_reference_numberare allCharFields. If you plan to query them regularly, consider adding indexes for efficiency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (105)
admin/Dockerfile.admin(1 hunks)admin/ce/components/common/upgrade-button.tsx(1 hunks)admin/core/components/admin-sidebar/help-section.tsx(0 hunks)aio/nginx.conf(1 hunks)apiserver/Dockerfile.api(2 hunks)apiserver/plane/api/middleware/api_authentication.py(3 hunks)apiserver/plane/api/serializers/__init__.py(1 hunks)apiserver/plane/api/serializers/issue.py(11 hunks)apiserver/plane/api/serializers/issue_type.py(1 hunks)apiserver/plane/api/serializers/project.py(2 hunks)apiserver/plane/api/serializers/webhook.py(1 hunks)apiserver/plane/api/urls/__init__.py(2 hunks)apiserver/plane/api/urls/issue.py(2 hunks)apiserver/plane/api/urls/issue_type.py(1 hunks)apiserver/plane/api/urls/project.py(2 hunks)apiserver/plane/api/urls/search.py(1 hunks)apiserver/plane/api/urls/state.py(1 hunks)apiserver/plane/api/urls/webhook.py(1 hunks)apiserver/plane/api/views/__init__.py(3 hunks)apiserver/plane/api/views/attachment.py(1 hunks)apiserver/plane/api/views/base.py(6 hunks)apiserver/plane/api/views/issue.py(11 hunks)apiserver/plane/api/views/issue_type.py(1 hunks)apiserver/plane/api/views/member.py(4 hunks)apiserver/plane/api/views/project.py(3 hunks)apiserver/plane/api/views/search.py(1 hunks)apiserver/plane/api/views/state.py(1 hunks)apiserver/plane/api/views/webhook.py(1 hunks)apiserver/plane/app/permissions/__init__.py(1 hunks)apiserver/plane/app/permissions/project.py(7 hunks)apiserver/plane/app/serializers/issue.py(4 hunks)apiserver/plane/app/urls/issue.py(2 hunks)apiserver/plane/app/urls/views.py(1 hunks)apiserver/plane/app/views/__init__.py(1 hunks)apiserver/plane/app/views/base.py(1 hunks)apiserver/plane/app/views/issue/base.py(14 hunks)apiserver/plane/app/views/view/base.py(6 hunks)apiserver/plane/authentication/adapter/base.py(2 hunks)apiserver/plane/authentication/provider/credentials/magic_code.py(3 hunks)apiserver/plane/authentication/utils/redirection_path.py(1 hunks)apiserver/plane/authentication/views/app/magic.py(5 hunks)apiserver/plane/bgtasks/issue_activities_task.py(4 hunks)apiserver/plane/bgtasks/webhook_task.py(1 hunks)apiserver/plane/db/migrations/0084_projectcustomproperty_issuecustomproperty_and_more.py(1 hunks)apiserver/plane/db/migrations/0085_remove_issuecustomproperty_project_custom_property_and_more.py(1 hunks)apiserver/plane/db/migrations/0086_issuetypecustomproperty_delete_projectcustomproperty_and_more.py(1 hunks)apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py(1 hunks)apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py(1 hunks)apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py(1 hunks)apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py(1 hunks)apiserver/plane/db/migrations/0091_issue_customer_code_issue_hub_code_and_more.py(1 hunks)apiserver/plane/db/models/__init__.py(3 hunks)apiserver/plane/db/models/base.py(1 hunks)apiserver/plane/db/models/issue.py(4 hunks)apiserver/plane/db/models/issue_type.py(1 hunks)apiserver/plane/middleware/api_log_middleware.py(2 hunks)apiserver/plane/settings/common.py(7 hunks)apiserver/plane/settings/redis.py(1 hunks)apiserver/plane/settings/storage.py(4 hunks)apiserver/plane/utils/constants.py(1 hunks)apiserver/plane/utils/grouper.py(1 hunks)apiserver/plane/utils/issue_filters.py(6 hunks)apiserver/requirements/base.txt(3 hunks)apiserver/supervisord.conf(1 hunks)apiserver/templates/emails/exports/analytics.html(1 hunks)demo.Jenkinsfile(1 hunks)deploy/selfhost/install.sh(1 hunks)docker-compose-local.yml(3 hunks)docker-compose.yml(1 hunks)packages/types/src/issues/issue.d.ts(1 hunks)packages/types/src/view-props.d.ts(2 hunks)space/core/components/issues/filters/selection.tsx(1 hunks)space/next.config.js(1 hunks)web/Dockerfile.web(1 hunks)web/app/[workspaceSlug]/(projects)/header.tsx(1 hunks)web/app/provider.tsx(2 hunks)web/app/workspace-invitations/page.tsx(1 hunks)web/ce/constants/project/settings/features.tsx(1 hunks)web/core/components/issues/custom-properties.tsx(1 hunks)web/core/components/issues/index.ts(1 hunks)web/core/components/issues/issue-detail/sidebar.tsx(6 hunks)web/core/components/issues/issue-layouts/filters/applied-filters/additional-properties.tsx(1 hunks)web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx(3 hunks)web/core/components/issues/issue-layouts/filters/applied-filters/index.ts(1 hunks)web/core/components/issues/issue-layouts/filters/header/filters/additional-properties.tsx(1 hunks)web/core/components/issues/issue-layouts/filters/header/filters/custom-properties.tsx(1 hunks)web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx(2 hunks)web/core/components/issues/issue-layouts/filters/header/filters/index.ts(1 hunks)web/core/components/issues/issue-layouts/spreadsheet/columns/index.ts(1 hunks)web/core/components/issues/issue-layouts/spreadsheet/columns/standard-property-column.tsx(1 hunks)web/core/components/issues/issue-layouts/spreadsheet/issue-column.tsx(1 hunks)web/core/components/issues/peek-overview/properties.tsx(6 hunks)web/core/components/workspace/sidebar/dropdown.tsx(0 hunks)web/core/components/workspace/sidebar/help-section.tsx(1 hunks)web/core/components/workspace/sidebar/projects-list-item.tsx(0 hunks)web/core/constants/analytics.ts(1 hunks)web/core/constants/dashboard.ts(1 hunks)web/core/constants/issue.ts(8 hunks)web/core/constants/spreadsheet.ts(4 hunks)web/core/hooks/use-group-dragndrop.ts(1 hunks)web/core/services/workspace.service.ts(1 hunks)web/core/store/issue/helpers/issue-filter-helper.store.ts(3 hunks)web/core/store/issue/issue-details/issue.store.ts(1 hunks)web/helpers/issue.helper.ts(1 hunks)web/next.config.js(1 hunks)
💤 Files with no reviewable changes (3)
- admin/core/components/admin-sidebar/help-section.tsx
- web/core/components/workspace/sidebar/dropdown.tsx
- web/core/components/workspace/sidebar/projects-list-item.tsx
🧰 Additional context used
🧬 Code Definitions (38)
apiserver/plane/app/permissions/__init__.py (1)
apiserver/plane/app/permissions/project.py (1) (1)
ProjectEntityGuestPermission(119-152)
apiserver/plane/db/models/__init__.py (2)
apiserver/plane/db/models/issue.py (1) (1)
IssueCustomProperty(726-747)apiserver/plane/db/models/issue_type.py (2) (2)
IssueType(10-32)IssueTypeCustomProperty(62-83)
apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py (3)
apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py (1) (1)
Migration(7-24)apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py (1) (1)
Migration(6-18)apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py (1) (1)
Migration(7-19)
apiserver/plane/api/serializers/__init__.py (1)
apiserver/plane/api/serializers/issue_type.py (2) (2)
IssueTypeSerializer(7-24)IssueTypeCustomPropertySerializer(26-44)
apiserver/plane/app/urls/views.py (1)
apiserver/plane/app/views/view/base.py (1) (1)
WorkspaceViewIssuesViewSet(208-467)
apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py (3)
apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py (1) (1)
Migration(6-18)apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py (1) (1)
Migration(7-19)apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py (1) (1)
Migration(6-18)
web/core/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx (1)
web/core/components/issues/issue-layouts/filters/applied-filters/additional-properties.tsx (1) (1)
AppliedAdditionalPropertiesFilters(14-33)
apiserver/plane/api/urls/issue_type.py (1)
apiserver/plane/api/views/issue_type.py (2) (2)
IssueTypeAPIEndpoint(43-196)IssueTypeCustomPropertyAPIEndpoint(198-240)
apiserver/plane/api/urls/search.py (1)
apiserver/plane/api/views/search.py (1) (1)
GlobalSearchEndpoint(29-268)
apiserver/plane/api/views/__init__.py (4)
apiserver/plane/api/views/issue_type.py (1) (1)
IssueTypeAPIEndpoint(43-196)apiserver/plane/api/views/attachment.py (1) (1)
IssueAttachmentV2Endpoint(18-174)apiserver/plane/api/views/search.py (1) (1)
GlobalSearchEndpoint(29-268)apiserver/plane/api/views/webhook.py (1) (1)
WebhookEndpoint(16-107)
apiserver/plane/api/serializers/project.py (1)
apiserver/plane/api/serializers/issue_type.py (1) (1)
IssueTypeSerializer(7-24)
apiserver/plane/authentication/provider/credentials/magic_code.py (1)
apiserver/plane/authentication/adapter/base.py (1) (1)
set_user_data(44-45)
apiserver/plane/api/views/issue_type.py (3)
apiserver/plane/api/serializers/issue_type.py (2) (2)
IssueTypeSerializer(7-24)IssueTypeCustomPropertySerializer(26-44)apiserver/plane/app/permissions/project.py (1) (1)
ProjectLitePermission(155-168)apiserver/plane/db/models/issue_type.py (2) (2)
IssueType(10-32)IssueTypeCustomProperty(62-83)
apiserver/plane/api/urls/issue.py (3)
apiserver/plane/api/views/attachment.py (1) (1)
IssueAttachmentV2Endpoint(18-174)apiserver/plane/api/views/issue_type.py (1) (1)
IssueTypeAPIEndpoint(43-196)apiserver/plane/api/views/issue.py (1) (1)
IssueAPIEndpoint(122-577)
web/core/components/issues/issue-layouts/filters/header/filters/custom-properties.tsx (1)
web/core/services/workspace.service.ts (1) (1)
WorkspaceService(20-301)
apiserver/plane/settings/common.py (2)
apiserver/plane/app/views/analytic/base.py (3) (3)
get(33-216)get(247-272)get(349-501)apiserver/plane/app/views/issue/base.py (4) (4)
get(92-211)get(804-809)get(839-853)get(1030-1123)
apiserver/plane/api/urls/state.py (1)
apiserver/plane/api/views/state.py (1) (1)
StateAPIEndpoint(16-162)
web/core/components/issues/issue-layouts/filters/header/filters/additional-properties.tsx (1)
web/core/services/workspace.service.ts (1) (1)
WorkspaceService(20-301)
web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx (4)
web/core/constants/issue.ts (1) (1)
ISSUE_ADDITIONAL_PROPERTIES(531-541)web/core/components/issues/issue-layouts/filters/header/filters/additional-properties.tsx (1) (1)
FilterAdditionalProperties(18-80)packages/types/src/view-props.d.ts (1) (1)
IIssueFilterOptions(94-116)web/core/components/issues/issue-layouts/filters/header/filters/custom-properties.tsx (1) (1)
FilterCustomProperty(19-142)
web/core/constants/spreadsheet.ts (1)
web/core/components/issues/issue-layouts/spreadsheet/columns/standard-property-column.tsx (1) (1)
SpreadsheetStandardPropertyColumn(11-21)
apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py (5)
apiserver/plane/db/migrations/0085_remove_issuecustomproperty_project_custom_property_and_more.py (1) (1)
Migration(6-22)apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py (1) (1)
Migration(7-24)apiserver/plane/db/migrations/0086_issuetypecustomproperty_delete_projectcustomproperty_and_more.py (1) (1)
Migration(9-48)apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py (1) (1)
Migration(6-18)apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py (1) (1)
Migration(6-18)
apiserver/plane/app/serializers/issue.py (2)
apiserver/plane/db/models/issue.py (4) (4)
IssueCustomProperty(726-747)Meta(190-194)Meta(279-283)Meta(311-323)apiserver/plane/api/serializers/issue.py (13) (13)
IssueCustomPropertySerializer(43-54)Meta(44-54)Meta(88-100)Meta(363-370)Meta(374-386)Meta(390-402)Meta(446-457)Meta(464-478)Meta(501-506)Meta(512-515)Meta(521-524)Meta(528-534)Meta(544-555)
apiserver/plane/api/views/project.py (4)
apiserver/plane/api/views/base.py (1) (1)
BaseAPIView(40-226)apiserver/plane/db/models/project.py (1) (1)
ProjectMember(215-259)apiserver/plane/api/serializers/project.py (1) (1)
create(66-88)apiserver/plane/db/models/issue.py (1) (1)
IssueUserProperty(533-561)
web/core/components/issues/issue-detail/sidebar.tsx (2)
web/core/constants/issue.ts (1) (1)
ISSUE_ADDITIONAL_PROPERTIES(531-541)web/core/components/issues/custom-properties.tsx (1) (1)
CustomProperties(13-33)
web/core/constants/issue.ts (1)
packages/types/src/issues/issue.d.ts (1) (1)
TIssue(52-73)
apiserver/plane/utils/issue_filters.py (3)
apiserver/plane/api/views/issue.py (7) (7)
get(97-120)get(163-308)get(658-676)get(739-763)get(886-904)get(1050-1081)get(1156-1161)apiserver/plane/app/views/issue/base.py (6) (6)
get(92-211)get(804-809)get(839-853)get(1030-1123)get(1221-1259)get(1265-1296)apiserver/plane/api/views/project.py (1) (1)
get(202-240)
apiserver/plane/app/views/__init__.py (1)
apiserver/plane/app/views/issue/base.py (2) (2)
SearchAPIEndpoint(1218-1259)SearchSingleValueAPI(1261-1296)
apiserver/plane/api/urls/webhook.py (1)
apiserver/plane/api/views/webhook.py (3) (3)
WebhookEndpoint(16-107)WebhookLogsEndpoint(121-129)WebhookSecretRegenerateEndpoint(110-118)
apiserver/plane/api/serializers/issue.py (3)
apiserver/plane/db/models/issue.py (17) (17)
IssueCustomProperty(726-747)Meta(190-194)Meta(279-283)Meta(311-323)Meta(339-351)Meta(367-379)Meta(393-397)Meta(427-431)Meta(477-481)Meta(522-526)Meta(545-557)Meta(572-576)Meta(592-596)Meta(609-621)Meta(638-650)Meta(669-681)Meta(704-720)apiserver/plane/app/views/view/base.py (2) (2)
IssueCustomPropertySerializer(63-74)create(652-659)apiserver/plane/api/serializers/issue_type.py (1) (1)
create(40-44)
apiserver/plane/api/views/attachment.py (3)
apiserver/plane/api/serializers/issue.py (3) (3)
IssueAttachmentSerializer(445-457)create(181-268)create(419-427)apiserver/plane/settings/storage.py (2) (2)
S3Storage(14-166)generate_presigned_post(66-106)apiserver/plane/db/models/base.py (1) (1)
save(25-42)
apiserver/plane/api/views/search.py (2)
apiserver/plane/api/views/base.py (3) (3)
fields(211-217)BaseAPIView(40-226)project_id(199-208)apiserver/plane/db/models/issue.py (1) (1)
Issue(108-268)
apiserver/plane/app/urls/issue.py (1)
apiserver/plane/app/views/issue/base.py (2) (2)
SearchAPIEndpoint(1218-1259)SearchSingleValueAPI(1261-1296)
apiserver/plane/app/views/view/base.py (5)
apiserver/plane/db/models/issue.py (9) (9)
IssueCustomProperty(726-747)Meta(190-194)Meta(279-283)Meta(311-323)Meta(339-351)Meta(367-379)Meta(393-397)Meta(427-431)Meta(477-481)apiserver/plane/utils/issue_filters.py (1) (1)
issue_filters(611-649)apiserver/plane/app/views/issue/base.py (10) (10)
IssueCustomPropertySerializer(77-88)Meta(78-88)get_queryset(235-286)get_queryset(857-900)get(92-211)get(804-809)get(839-853)get(1030-1123)get(1221-1259)get(1265-1296)apiserver/plane/app/views/base.py (2) (2)
fields(148-154)fields(253-259)apiserver/plane/app/views/workspace/draft.py (1) (1)
get_queryset(50-103)
apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py (5)
apiserver/plane/db/migrations/0085_remove_issuecustomproperty_project_custom_property_and_more.py (1) (1)
Migration(6-22)apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py (1) (1)
Migration(7-24)apiserver/plane/db/migrations/0086_issuetypecustomproperty_delete_projectcustomproperty_and_more.py (1) (1)
Migration(9-48)apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py (1) (1)
Migration(7-19)apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py (1) (1)
Migration(6-18)
apiserver/plane/api/views/webhook.py (1)
apiserver/plane/api/serializers/webhook.py (2) (2)
WebhookSerializer(15-128)WebhookLogSerializer(131-135)
apiserver/plane/api/views/issue.py (4)
apiserver/plane/api/serializers/issue_type.py (1) (1)
IssueTypeSerializer(7-24)apiserver/plane/db/models/issue_type.py (1) (1)
IssueType(10-32)apiserver/plane/utils/issue_filters.py (1) (1)
issue_filters(611-649)apiserver/plane/api/views/base.py (1) (1)
project_id(199-208)
apiserver/plane/app/views/issue/base.py (2)
apiserver/plane/app/permissions/project.py (3) (3)
ProjectEntityPermission(83-116)ProjectLitePermission(155-168)ProjectMemberPermission(47-80)apiserver/plane/api/serializers/issue.py (1) (1)
IssueCustomPropertySerializer(43-54)
apiserver/plane/authentication/views/app/magic.py (3)
apiserver/plane/api/serializers/project.py (2) (2)
ProjectSerializer(15-88)create(66-88)apiserver/plane/api/views/base.py (1) (1)
BaseAPIView(40-226)apiserver/plane/api/views/project.py (1) (1)
create_project(34-122)
🪛 Ruff (0.8.2)
apiserver/plane/app/permissions/__init__.py
14-14: .project.ProjectEntityGuestPermission imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
apiserver/plane/db/models/__init__.py
44-44: .issue.IssueCustomProperty imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
71-71: .project.ProjectPublicMember imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
115-115: .issue_type.IssueType imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
115-115: .issue_type.IssueTypeCustomProperty imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
apiserver/plane/api/serializers/__init__.py
14-14: .issue_type.IssueTypeSerializer imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
14-14: .issue_type.IssueTypeCustomPropertySerializer imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
apiserver/plane/api/views/__init__.py
2-2: .project.ProjectAPIEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
3-3: .project.ProjectArchiveUnarchiveAPIEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
15-15: .issue.IssueAttachmentEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
17-17: .issue_type.IssueTypeAPIEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
17-17: .issue_type.IssueTypeCustomPropertyAPIEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
18-18: .attachment.IssueAttachmentV2Endpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
36-36: .search.GlobalSearchEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
39-39: .webhook.WebhookEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
39-39: .webhook.WebhookLogsEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
40-40: .webhook.WebhookSecretRegenerateEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
apiserver/plane/api/serializers/project.py
12-12: .issue_type.IssueTypeSerializer imported but unused
Remove unused import: .issue_type.IssueTypeSerializer
(F401)
apiserver/plane/api/views/base.py
89-89: Multiple statements on one line (semicolon)
(E702)
apiserver/plane/bgtasks/webhook_task.py
407-407: Local variable event_data is assigned to but never used
Remove assignment to unused variable event_data
(F841)
408-408: Local variable actor_data is assigned to but never used
Remove assignment to unused variable actor_data
(F841)
apiserver/plane/api/views/issue_type.py
8-8: django.db.models.Case imported but unused
Remove unused import
(F401)
9-9: django.db.models.CharField imported but unused
Remove unused import
(F401)
10-10: django.db.models.Exists imported but unused
Remove unused import
(F401)
11-11: django.db.models.F imported but unused
Remove unused import
(F401)
12-12: django.db.models.Func imported but unused
Remove unused import
(F401)
13-13: django.db.models.Max imported but unused
Remove unused import
(F401)
14-14: django.db.models.OuterRef imported but unused
Remove unused import
(F401)
15-15: django.db.models.Q imported but unused
Remove unused import
(F401)
16-16: django.db.models.Value imported but unused
Remove unused import
(F401)
17-17: django.db.models.When imported but unused
Remove unused import
(F401)
18-18: django.db.models.Subquery imported but unused
Remove unused import
(F401)
25-25: rest_framework.parsers.MultiPartParser imported but unused
Remove unused import
(F401)
25-25: rest_framework.parsers.FormParser imported but unused
Remove unused import
(F401)
35-35: plane.bgtasks.issue_activities_task.issue_activity imported but unused
Remove unused import: plane.bgtasks.issue_activities_task.issue_activity
(F401)
140-140: Local variable requested_data is assigned to but never used
Remove assignment to unused variable requested_data
(F841)
141-141: Local variable current_instance is assigned to but never used
Remove assignment to unused variable current_instance
(F841)
142-142: Undefined name issue_comment
(F821)
162-162: Undefined name issue_comment
(F821)
200-200: Local variable workspace is assigned to but never used
Remove assignment to unused variable workspace
(F841)
236-236: Undefined name ValidationError
(F821)
apiserver/plane/api/serializers/issue_type.py
1-1: rest_framework.serializers imported but unused
Remove unused import: rest_framework.serializers
(F401)
apiserver/plane/api/urls/issue.py
10-10: plane.api.views.IssueTypeAPIEndpoint imported but unused
Remove unused import: plane.api.views.IssueTypeAPIEndpoint
(F401)
apiserver/plane/api/views/project.py
253-253: Undefined name user
(F821)
apiserver/plane/utils/issue_filters.py
6-6: django.db.models.Q imported but unused
Remove unused import: django.db.models.Q
(F401)
apiserver/plane/app/views/__init__.py
135-135: .issue.base.SearchAPIEndpoint imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
136-136: .issue.base.SearchSingleValueAPI imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
apiserver/plane/api/serializers/issue.py
38-38: Local variable uuid_obj is assigned to but never used
Remove assignment to unused variable uuid_obj
(F841)
apiserver/plane/api/views/attachment.py
9-9: plane.app.permissions.allow_permission imported but unused
Remove unused import: plane.app.permissions.allow_permission
(F401)
apiserver/plane/app/views/view/base.py
3-3: django.db.models.Count imported but unused
Remove unused import: django.db.models.Count
(F401)
apiserver/plane/api/views/issue.py
36-36: plane.api.serializers.IssueTypeSerializer imported but unused
Remove unused import: plane.api.serializers.IssueTypeSerializer
(F401)
55-55: plane.db.models.IssueType imported but unused
Remove unused import: plane.db.models.IssueType
(F401)
apiserver/plane/app/views/issue/base.py
60-60: plane.utils.constants.ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAP imported but unused
Remove unused import: plane.utils.constants.ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAP
(F401)
72-72: plane.app.permissions.ProjectEntityPermission imported but unused
Remove unused import
(F401)
73-73: plane.app.permissions.ProjectLitePermission imported but unused
Remove unused import
(F401)
74-74: plane.app.permissions.ProjectMemberPermission imported but unused
Remove unused import
(F401)
apiserver/plane/authentication/views/app/magic.py
290-290: Local variable is_app is assigned to but never used
Remove assignment to unused variable is_app
(F841)
🪛 Biome (1.9.4)
web/core/components/issues/issue-layouts/filters/header/filters/custom-properties.tsx
[error] 121-121: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx
[error] 249-249: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🔇 Additional comments (125)
apiserver/plane/app/urls/issue.py (1)
27-28: New imports added for search functionalityThe new imports for
SearchAPIEndpointandSearchSingleValueAPIalign with the additions to the URL patterns below. These endpoints will enhance search capabilities for issue-related data.apiserver/plane/app/views/__init__.py (1)
135-136:Details
❓ Verification inconclusive
Check for unused imports
The static analysis indicates that
SearchAPIEndpointandSearchSingleValueAPIare imported but not used within this file. If these imports are meant to be re-exported for use in other modules, consider adding them to an__all__list. Otherwise, they should be removed to prevent cluttering the namespace.
🏁 Script executed:
#!/bin/bash # Check if these imports are actually used elsewhere in the codebase echo "Checking for imports of SearchAPIEndpoint and SearchSingleValueAPI from plane.app.views" rg -A 2 -B 2 "from plane.app.views import.*SearchAPIEndpoint|from plane.app.views import.*SearchSingleValueAPI" --type pyLength of output: 308
Verify Unused Imports in apiserver/plane/app/views/init.py
The static analysis flagged that
SearchAPIEndpointandSearchSingleValueAPIare imported but not used within this file. Our initial search for their usage via grep produced no results. This suggests that they may be unused—unless these imports are intentionally meant for re-export in which case they should be added to an explicit__all__list. Please manually verify if these symbols are indeed utilized elsewhere (for example, by consumers of theplane.app.viewsmodule) or if they can be safely removed.
- Location:
apiserver/plane/app/views/__init__.py(Lines 135-136)- Action Items:
- If these imports are intended for global access, add them to an
__all__list.- Otherwise, remove the imports to reduce namespace clutter.
🧰 Tools
🪛 Ruff (0.8.2)
135-135:
.issue.base.SearchAPIEndpointimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
136-136:
.issue.base.SearchSingleValueAPIimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
web/Dockerfile.web (1)
105-107: LGTM - Docker configuration improvedThe addition of the
EXPOSE 3000directive and the CMD instruction properly configures the container to expose its port and specifies how to start the application. This is a good practice for Docker containers.admin/Dockerfile.admin (1)
86-88: Docker container configuration improvements addedThe addition of
EXPOSE 3000andCMD node admin/server.js admininstructions properly configures the Docker container by:
- Explicitly exposing the port used by the application
- Defining the default command to start the admin server
These changes improve the container's self-sufficiency and follow Docker best practices.
deploy/selfhost/install.sh (1)
185-185:Details
❓ Verification inconclusive
Repository URL changed from original project to a fork
The code changes the clone URL from
makeplane/planetoshipsy/plane, which points to a fork instead of the original repository. This may cause divergence from upstream if not properly maintained.Is this repository change intentional? If it's for organization-specific customizations, please ensure you have a process to keep the fork updated with upstream changes.
#!/bin/bash # Verify repository URL usage across the repository echo "Checking the repository URL in deploy/selfhost/install.sh..." grep -R "REPO=https://github.com/shipsy/plane.git" deploy/selfhost/install.sh echo "Searching for references to the original repository (makeplane/plane)..." rg "github.com/makeplane/plane" -n . echo "Searching for references to the new repository (shipsy/plane)..." rg "github.com/shipsy/plane.git" -n .Repository URL Change Verification – ACTION REQUIRED
Indeploy/selfhost/install.sh(line 185), the repository URL has been updated tohttps://github.com/shipsy/plane.git. This change replaces the original repository (github.com/makeplane/plane) with a fork. Please confirm if this change was intentional. If this is an organization-specific customization, ensure that you have a process to keep the fork updated with upstream changes to prevent divergence.web/ce/constants/project/settings/features.tsx (1)
87-88: Time Tracking feature now available and enabled for all usersThe Time Tracking feature has been changed from a premium feature (
isPro: true) to a standard feature (isPro: false) and is now enabled by default (isEnabled: true). This change makes time tracking functionality accessible to all users regardless of their subscription plan.apiserver/plane/api/urls/project.py (2)
20-21: Consistent update to string type parameterThis change from
<uuid:project_id>to<str:project_id>is consistent with the previous pattern change. This standardization across URL patterns improves consistency in the API's handling of project identifiers.
15-16:Details
❓ Verification inconclusive
Verify the impact of changing UUID to string type parameter
The URL pattern has been modified to accept a string type (
<str:pk>) instead of a UUID type (<uuid:pk>). While this change aligns with similar modifications in other URL patterns, we should consider the validation implications.
🏁 Script executed:
#!/bin/bash # Check if the view has proper validation for string-based project IDs # Look for validation in ProjectAPIEndpoint rg -A 10 "class ProjectAPIEndpoint" apiserver/plane/api/views/ # Check if there are any UUID validation functions rg -A 3 "def is_uuid" apiserverLength of output: 1084
Subject: Confirm Behavior of
<str:pk>in ProjectAPIEndpointIt appears that while the URL pattern change to
<str:pk>aligns with similar patterns elsewhere, the current implementation ofProjectAPIEndpointdoes not include any explicit validation for ensuring that the suppliedpkconforms to a UUID format. Although we do have anis_uuidhelper defined inapiserver/plane/api/serializers/issue.py, its usage is not evident within the endpoint for projects.
- Verify that the
ProjectSerializeror model layer properly handles conversion or raises meaningful errors if an invalid UUID string is provided.- Consider reusing existing validation (e.g., the
is_uuidfunction) if stricter UUID format enforcement is required.- Confirm through tests that the API behaves as expected with both valid and malformed IDs.
web/core/hooks/use-group-dragndrop.ts (1)
64-65: Good defensive programming by explicit type conversionConverting
moduleKeyandcycleKeyto strings before checking if they exist inObject.keys(data)is a good practice. This ensures correct type comparison regardless of the underlying type of the keys.This change prevents potential type mismatches when comparing keys, enhancing the robustness of the code.
apiserver/plane/db/migrations/0087_issuecustomproperty_issue_type_and_more.py (2)
19-23: Increased flexibility for the value fieldModifying the
valuefield to allow blank and null values with a max length of 255 characters provides more flexibility for custom properties. This is appropriate for fields that may not always have a value.
14-18:Details
✅ Verification successful
Note multiple subsequent migrations modifying this field
This migration adds a new
issue_typefield as a foreign key to theissuetypemodel. Be aware that subsequent migrations (0088, 0089, 0090) further modify this field by changing its reference and name.
🏁 Script executed:
#!/bin/bash # Check the sequence of migrations affecting this field # List all migrations that modify the issue_type field rg -A 3 -B 3 "issue_type" apiserver/plane/db/migrations/*.py # Check the current model definition rg -A 5 "class IssueCustomProperty" apiserver/plane/db/modelsLength of output: 28089
Attention: Multiple Sequential Migrations Impacting
issue_typeFieldThis migration (0087) adds the
issue_typefield as a foreign key on theIssueCustomPropertymodel pointing todb.issuetype. Note that subsequent migrations further modify this field:
- 0088_alter_issuecustomproperty_issue_type.py: Alters the field to reference
db.issuetypecustompropertyand updates therelated_nametoissue_type_custom_property.- 0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py: Renames the field from
issue_typetoissue_type_custom_proerty.- 0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py: Renames the field from
issue_type_custom_proertytoissue_type_custom_property.Please verify that these sequential modifications are intentional and correctly reflect the desired evolution of the data model.
apiserver/plane/db/models/base.py (1)
36-38:Details
❓ Verification inconclusive
Important behavior change in created_by assignment
The updated logic now only sets
created_byif it's currentlyNone. This preserves any existing value rather than always overwriting with the current user. This is a significant behavior change that may affect how created_by is handled throughout the application.
🏁 Script executed:
#!/bin/bash # Check for places that might rely on the previous behavior # Look for code that explicitly sets created_by before saving rg "created_by.*save" --glob "*.py" apiserver/ # Look for tests that may verify created_by behavior rg "created_by.*assert" --glob "*test*.py" apiserver/Length of output: 331
Attention: Confirm the Updated
created_byBehaviorThe change now only sets
created_bywhen it isn’t already defined, preserving any preexisting value. Our quick search identified comments in the following files that reference updatingcreated_by(which might imply an expectation of the previous behavior):
- apiserver/plane/api/views/issue_type.py
- apiserver/plane/api/views/issue.py
No test assertions directly verifying the old unconditional assignment of
created_bywere found. Please manually verify that this change doesn’t break any workflows or assumptions in these parts of the application.web/core/components/workspace/sidebar/help-section.tsx (2)
56-56: Layout alignment has changed in non-collapsed sidebar.The CSS class changed from "justify-evenly" to "justify-end ml-auto", which will align the controls to the right end of the sidebar instead of distributing them evenly across the space.
43-69:Details
❓ Verification inconclusive
Significant simplification of the sidebar help section.
The component has been heavily simplified by removing multiple elements:
- Removed CustomMenu component and its associated options (documentation, messaging support, etc.)
- Removed WorkspaceEditionBadge component
- Removed various help resources and links
This change streamlines the UI but potentially limits quick access to help resources for users.
🏁 Script executed:
#!/bin/bash # Check if there are any other help resources available elsewhere in the application rg -B 2 -A 2 "documentation|support|help" --type tsx --type jsx ./web/Length of output: 100
Next Steps: Verify Additional Help References
The simplified sidebar help section removes several help elements (CustomMenu, WorkspaceEditionBadge, and associated links). However, we must confirm that no alternate help resources (e.g. documentation, support links) are available elsewhere in the codebase. The initial search command failed due to incorrect file type handling. Please run the following updated shell script to search all *.tsx and *.jsx files for any help-related references:
#!/bin/bash # Search for references to documentation, support, or help resources in TSX/JSX files rg -g "*.{tsx,jsx}" -B2 -A2 "documentation|support|help" ./web/If this search produces no output, it confirms that these help resources are not used elsewhere in the application. Otherwise, please review any findings to ensure that the removal of the help elements does not unintentionally limit user access.
web/app/workspace-invitations/page.tsx (2)
7-7: Removed Star icon import.The Star icon import has been removed as it's no longer used in the component.
114-115: GitHub star link has been removed.The EmptySpaceItem component with the Star icon that provided a link to star the project on GitHub has been removed. This appears to be part of a broader pattern of simplifying the UI and removing external promotion links.
apiserver/plane/api/urls/state.py (2)
12-12:Details
✅ Verification successful
API URL pattern changed: project_id type changed from UUID to string.
Similar to line 7, the URL pattern for accessing a specific state has been modified to accept
project_idas a string rather than a UUID, while maintainingstate_idas UUID.This change should be verified against the database model to ensure that string project IDs are properly handled throughout the application.
🏁 Script executed:
#!/bin/bash # Check the State model definition to see how project_id is defined rg -A 10 "class State" ./apiserver/Length of output: 6648
Action required: Validate consistency of project_id as a string across the codebase
After verifying the code:
- The URL pattern now accepts
project_idas a string, as seen in:
- The URL in
apiserver/plane/api/urls/state.py.- Serializer definitions (e.g., in
apiserver/plane/app/serializers/state.py).- View filtering (e.g., in
apiserver/plane/app/views/state/base.py).- The usage of
project_idas a string is consistent with how the endpoints extract and filter data. Although the State model (likely inheriting project_id from a base model) wasn’t directly inspected here, all observable interactions correctly expect a string.No further modifications are required.
7-7:Details
❓ Verification inconclusive
API URL pattern changed: project_id type changed from UUID to string.
The URL pattern for accessing states has been modified to accept
project_idas a string rather than a UUID. This change increases flexibility but may impact type validation.
🏁 Script executed:
#!/bin/bash # Check if this change is consistent across other URL patterns in the codebase echo "Checking for other URL patterns with project_id..." rg -A 1 "projects/<(uuid|str):project_id>" --type py ./apiserver/ # Check if the StateAPIEndpoint view properly handles string project_ids echo "Checking StateAPIEndpoint implementation..." rg -A 5 "def get_queryset" ./apiserver/plane/api/views/state.py # Check for any type validation or conversion of project_id in the view rg "project_id\s*=" ./apiserver/plane/api/views/state.pyLength of output: 29609
Action Required: Verify Consistency of project_id Type Change
The state API URL now accepts a string for project_id, and the corresponding view (StateAPIEndpoint) processes it directly without converting it to a UUID. While this change increases flexibility for state endpoints, the verification shows that most other endpoints continue to use
<uuid:project_id>. Please review the following:
- File:
apiserver/plane/api/urls/state.py
- The URL patterns changed to
"workspaces/<str:slug>/projects/<str:project_id>/states/"and its detailed state endpoint still uses<uuid:state_id>.- File:
apiserver/plane/api/views/state.py
- The
get_querysetmethod filters using the raw value ofproject_idfromkwargswithout any type conversion.- Observation:
- Numerous other API endpoints in the codebase continue to expect
project_idas a UUID. This inconsistency may introduce type validation issues or unexpected behavior if downstream components expect a UUID.Please verify whether this divergence is intentional. If so, consider whether additional type validation or conversion is needed for the state endpoints—or if similar changes should be made in other parts of the API for consistency.
docker-compose-local.yml (3)
23-24: Exposing MinIO console port.Added port mapping to expose MinIO console (9090) to the host, which allows direct access to the MinIO web interface for managing storage during local development.
38-39: PostgreSQL port exposed to host.Added port mapping to expose PostgreSQL (5432) to the host, allowing direct database access from the host machine using database clients.
115-116: Interactive terminal enabled for API service.Added
stdin_open: trueandtty: trueconfigurations to enable interactive terminal access for the API service. This improves development experience by allowing direct interaction with the container.web/core/constants/spreadsheet.ts (4)
14-14: New import for the Tags icon added.The Tags icon from lucide-react has been added to support the new property icons.
36-36: SpreadsheetStandardPropertyColumn now used for standard properties.Using the existing
SpreadsheetStandardPropertyColumncomponent is appropriate for the new standard properties.
181-234: New standard properties added to SPREADSHEET_PROPERTY_DETAILS.The implementation follows the established pattern for property details, with appropriate titles, sort options, and icons. This provides consistent UI handling for the new properties.
252-257: New property keys added to SPREADSHEET_PROPERTY_LIST.All new standard properties have been properly added to the property list, ensuring they'll be available for display in the spreadsheet view.
web/core/constants/dashboard.ts (1)
261-293: Sidebar menu items commented out rather than removed.The PR has commented out "projects", "active-cycles", and "analytics" menu items while leaving "all-issues" active. This approach preserves the code for potential future reactivation but might lead to confusion about which features are currently supported.
Was this change intentional? If these menu items are no longer needed, consider removing the code entirely rather than commenting it out for cleaner maintenance. If this is a temporary change for testing, please add a TODO comment explaining the intent.
docker-compose.yml (3)
10-13: Added volume mappings for development.The volume mappings allow mounting local directories into the container, which is useful for development but should be reviewed before production deployment.
13-15: Changed command and commented out dependencies.Replacing the Node.js server command with
tail -fand commenting out dependencies suggests this is intended for development use only.This configuration will keep the container running but won't start the actual web service. Ensure this change is only meant for development and not accidentally committed for production use.
17-183: Commented out most services in docker-compose.Most services and volumes have been commented out rather than removed.
Is this change intended for development purposes only? If so, consider creating a separate
docker-compose.dev.ymlfile instead of modifying the main configuration file. If these services are no longer needed, it would be cleaner to remove them entirely rather than commenting them out.web/core/components/issues/index.ts (1)
15-15: Added export for custom properties module.This change exports all entities from the custom properties module, making them available to consumers of the issues components.
web/core/components/issues/issue-layouts/spreadsheet/columns/index.ts (1)
15-15: New component export looks good.The addition of the standard-property-column export follows the existing pattern and convention of the file.
apiserver/templates/emails/exports/analytics.html (1)
1-7: Formatting improvements look good.The HTML formatting changes improve readability while maintaining the same content and functionality.
web/core/components/issues/issue-layouts/filters/applied-filters/index.ts (1)
12-12: New additional-properties export looks good.The addition of this export maintains consistency with the existing pattern in the file and integrates well with the filter system.
apiserver/plane/api/serializers/__init__.py (1)
14-14: Exposing new serializers for issue type functionality.The addition of these serializers aligns with the PR's objectives to enhance issue custom properties support. They will be available for import by other modules that need to serialize or deserialize issue types and their custom properties.
🧰 Tools
🪛 Ruff (0.8.2)
14-14:
.issue_type.IssueTypeSerializerimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
14-14:
.issue_type.IssueTypeCustomPropertySerializerimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
apiserver/plane/app/permissions/__init__.py (1)
14-14: Addition of guest permission for enhanced access control.This change exposes the
ProjectEntityGuestPermissionclass, which allows guest users to view project entities - an important enhancement for supporting the new display properties functionality while maintaining proper access controls.🧰 Tools
🪛 Ruff (0.8.2)
14-14:
.project.ProjectEntityGuestPermissionimported but unused; consider removing, adding to__all__, or using a redundant alias(F401)
apiserver/plane/db/migrations/0090_rename_issue_type_custom_proerty_issuecustomproperty_issue_type_custom_property.py (1)
1-18: Fixing field name typo in the database schema.This migration correctly addresses a typo in the field name by renaming
issue_type_custom_proertytoissue_type_custom_property. This is a good fix to ensure consistency and proper naming in the database schema.web/core/constants/analytics.ts (1)
57-57: Fixed array syntax by removing trailing comma.This small syntax correction improves code quality by replacing the trailing comma with a proper closing brace for the last object in the
ANALYTICS_X_AXIS_VALUESarray.apiserver/plane/authentication/utils/redirection_path.py (1)
9-10:Details
❓ Verification inconclusive
Redirection to onboarding flow has been removed.
The code that previously redirected users to the onboarding path when they were not onboarded has been commented out. This change alters the user flow after authentication, allowing all users to bypass the onboarding step.
🏁 Script executed:
#!/bin/bash # Check if there are any other references to onboarding flows that might be affected echo "Checking for other references to onboarding flows..." rg "is_onboarded" --type py -A 2 -B 2Length of output: 6766
Action Required: Verify Consistency of Onboarding Flow Changes
The redirection logic in
apiserver/plane/authentication/utils/redirection_path.py(lines 9-10) that previously sent users to the onboarding process whenprofile.is_onboardedwas false is now commented out. This change means users will no longer be redirected to onboarding after authentication. However, several other parts of the codebase still reference and update theis_onboardedstatus (e.g., inapiserver/plane/api/views/member.py,apiserver/plane/app/views/user/base.py,apiserver/plane/authentication/views/app/magic.py, and even within migration scripts).
- Confirm that bypassing the onboarding step here is an intentional part of the overall workflow change.
- Verify that any related logic in the other files handling the
is_onboardedstate is consistent with this update.- Ensure that the removal of redirection does not leave orphaned or contradictory onboarding logic elsewhere in the application, especially considering updates in the database migrations.
web/core/components/issues/issue-layouts/filters/header/filters/index.ts (1)
14-14: New additional-properties filter component has been integrated.This export statement makes the additional-properties filter components available in the filters module, expanding the filtering options for issues.
apiserver/plane/utils/grouper.py (1)
97-102:Details
❓ Verification inconclusive
New shipping-related fields added to issue results.
Several new fields have been added to the
required_fieldslist for issue results: hub_code, vendor_code, customer_code, worker_code, reference_number, and trip_reference_number. These logistics-related fields enable new grouping and filtering capabilities for issues.
🏁 Script executed:
#!/bin/bash # Check if these fields are defined in the Issue model echo "Checking Issue model for new fields..." rg -A 3 -B 3 "class Issue" --type py # Check if the fields are being used in serializers echo "Checking serializers for new fields..." rg "hub_code|vendor_code|customer_code|worker_code|reference_number|trip_reference_number" --type py --glob "*serializers*.py"Length of output: 51022
Below is a new verification shell script to re-check the usage of the new shipping-related fields across the codebase (including within files that contain “serializer” in their filename). Please run the script and manually inspect the output to confirm that these fields are properly integrated in the Issue model and related serializers.
#!/bin/bash echo "Searching for new shipping fields in all Python files..." rg "hub_code|vendor_code|customer_code|worker_code|reference_number|trip_reference_number" --type py echo "Searching for new shipping fields in files with 'serializer' in the filename..." rg "hub_code|vendor_code|customer_code|worker_code|reference_number|trip_reference_number" --type py --glob "*serializer*.py"
Rewritten Review Comment – New Shipping Fields Integration
The new shipping-related fields (hub_code, vendor_code, customer_code, worker_code, reference_number, and trip_reference_number) have been added to the
required_fieldslist inapiserver/plane/utils/grouper.py(lines 97–102) to enable enhanced grouping and filtering for issue results.Action Items:
- Please verify that these new fields are correctly defined or mapped in the Issue model (e.g., in
apiserver/plane/db/models/issue.py).- Confirm that any necessary serializers (look in files with “serializer” in their names) are updated to handle these fields if the use case requires. Note that the previous search for these fields in serializer files returned no matches—this may be due to filtering issues, so manual verification is advised.
Once the above points are verified, we can be confident that the integration of the new fields is complete.
apiserver/plane/db/migrations/0089_rename_issue_type_issuecustomproperty_issue_type_custom_proerty.py (1)
13-17: Typo in field name will need a follow-up correction.The field is being renamed to
issue_type_custom_proertywhich contains a typo (missing 'p' in 'property').I see in the relevant code snippets that this is addressed in migration 0090, which correctly renames it to
issue_type_custom_property. This approach of fixing typos through sequential migrations is appropriate for production deployments.apiserver/plane/db/migrations/0088_alter_issuecustomproperty_issue_type.py (1)
14-18: ForeignKey relationship target has been changed.The migration changes the
issue_typefield to referenceissuetypecustompropertymodel instead ofissuetype(as defined in migration 0087). The related_name is also changed from'custom_issue_type'to'issue_type_custom_property'.This change appears to be part of a broader restructuring of the data model to better support custom properties for issues. The change is well structured with appropriate null/blank handling and SET_NULL for the on_delete behavior.
apiserver/requirements/base.txt (2)
13-13: New dependency added.The
python-dotenvpackage has been added, which is useful for loading environment variables from .env files.This is a good addition that improves configuration management by allowing environment variables to be loaded from .env files.
33-33: Package version update.The
django-storagespackage has been updated from 1.14.2 to 1.14.4.This is a minor version update that likely includes bug fixes or small improvements.
web/core/components/issues/issue-layouts/spreadsheet/issue-column.tsx (1)
45-47: Appropriate enhancement passing property information to Column componentPassing the property key to the Column component is a good improvement that allows the component to be aware of which property it's rendering. This enables more flexible and property-specific behavior inside the Column component.
web/helpers/issue.helper.ts (1)
310-315: LGTM: Additional properties implementation looks goodThe addition of these standard properties (hub_code, customer_code, etc.) follows the existing pattern for display properties, ensuring consistency with the rest of the codebase.
web/core/components/issues/issue-layouts/filters/applied-filters/additional-properties.tsx (1)
8-12: LGTM: Props interface is well-definedThe Props interface clearly defines the expected types for the component parameters, making the component's API clear and easy to understand.
apiserver/plane/settings/storage.py (1)
22-26: Review the file overwrite implementationThe method conditionally bypasses the parent's
get_available_namemethod whenfile_overwriteis True, which could lead to unintentional file overwrites.Verify that this behavior is desired and that there are controls elsewhere to prevent critical files from being accidentally overwritten.
apiserver/plane/api/serializers/issue_type.py (1)
7-25: Well-structured serializer implementationThe
IssueTypeSerializercorrectly handles workspace context and defines appropriate read-only and excluded fields.web/core/services/workspace.service.ts (1)
281-290: LGTM: Adds method to fetch custom properties for issuesThis method follows the established pattern in the service, providing a clean interface to retrieve custom properties for issues at the workspace level.
apiserver/plane/middleware/api_log_middleware.py (1)
12-17:Details
❓ Verification inconclusive
Rewriting request path and updating resolver match may have unintended side effects
The code is modifying the request path and resolver match after the response has already been generated. This means the modification won't affect the current request processing but might impact subsequent middleware or request handling. Is this intentional?
🏁 Script executed:
#!/bin/bash # Check if this middleware is being executed in the expected order fd --type f --extension py "middleware" | xargs grep -l "APITokenLogMiddleware" | xargs catLength of output: 3066
Attention: Confirm Intentional Post-Response Request Modification
It appears that the middleware intentionally rewrites the request path and updates the resolver match after calling
get_response(). This design means:
- The modifications are not used to influence the response since they occur post-response generation.
- The changes are applied primarily for logging purposes via the subsequent
process_requestcall.- If any later middleware or future logic depends on the original request state, this post-response modification could lead to unintended side effects.
Please verify that these modifications are indeed intended only for enriching the log data and that no downstream processing relies on the original request path or resolver match.
apiserver/plane/app/permissions/project.py (2)
18-19: LGTM: Bypass permission checks for superusersAdding superuser permission bypasses is a standard pattern that simplifies administration tasks.
Also applies to: 53-54, 88-89, 124-125, 160-161
33-33: LGTM: Added Guest role to permission checksExtending permissions to include the Guest role in appropriate filters is a good improvement for flexibility in access control.
Also applies to: 68-68, 77-77, 113-113, 149-149
apiserver/plane/api/urls/__init__.py (1)
4-4: No issues found for the import ofissue_type_patterns.demo.Jenkinsfile (2)
1-1: Pin your Jenkins library reference to a stable version instead of usingmaster.
This helps avoid unexpected breakages from upstream changes.- @Library('jenkins-library@master') _ + @Library('jenkins-library@v1.0.0') _
93-127: Parallel Docker builds look good.
No immediate issues with the approach or arguments for building images.apiserver/plane/db/migrations/0086_issuetypecustomproperty_delete_projectcustomproperty_and_more.py (1)
24-24: Validate and handle large JSON payloads in thevaluefield.
JSONField can store arbitrarily large objects, so ensure upstream components prevent oversized or malicious data.web/core/constants/issue.ts (1)
531-541: LGTM! Consistent implementation of additional propertiesThe implementation of
ISSUE_ADDITIONAL_PROPERTIESaligns well with the other constants in the file and properly defines the key-value mapping for the new custom properties.web/core/components/issues/custom-properties.tsx (2)
3-7: LGTM! Well-structured type definitionThe type definition for
CustomPropertyis clear and concise, properly capturing the necessary properties for custom property objects.
13-33: Well-implemented component with proper edge case handlingThe component correctly checks if the input is a valid array and has content before rendering, preventing potential rendering issues with invalid data. The UI structure with appropriate styling enhances readability.
apiserver/plane/api/urls/issue.py (2)
74-78: LGTM! Good addition of attachment endpointAdding the new endpoint for issue attachments with a specific ID is a good addition for more granular control over attachments.
20-28:Details
❓ Verification inconclusive
Verify handling of string project IDs throughout the codebase
The change from
<uuid:project_id>to<str:project_id>is significant and might affect the behavior of existing endpoints. Ensure that the view functions (such asIssueAPIEndpoint) are updated to handle string project IDs correctly.
🏁 Script executed:
#!/bin/bash # Description: Check if IssueAPIEndpoint handles string project IDs correctly # Check how project_id is handled in the IssueAPIEndpoint view rg -n "project_id" --type py "apiserver/plane/api/views/issue.py" | grep -B 5 -A 5 "IssueAPIEndpoint" # Check for any type conversions or validations of project_id rg -n "project_id.*uuid|uuid.*project_id" --type py "apiserver/plane/api/views/issue.py"Length of output: 186
I'm not seeing any output from the initial verification, so I’d like to dig a bit deeper into how the
IssueAPIEndpoint(in the fileapiserver/plane/api/views/issue.py) handles theproject_idparameter. The following shell script will:• Identify the file(s) where
IssueAPIEndpointis defined.
• Search those files for any references toproject_idto check if there's any explicit handling, conversion, or validation.Please run the script below and share its output for further review:
Once we have the output from this script, we can better verify if there’s any logic (or lack thereof) for handling string project IDs.
[request_verification]
🏁 Script executed:
#!/bin/bash # Step 1: Identify the file(s) where IssueAPIEndpoint is defined issue_files=$(rg -l "class IssueAPIEndpoint" --type py) echo "IssueAPIEndpoint defined in:" echo "$issue_files" echo "" # Step 2: For each file that defines IssueAPIEndpoint, search for references to 'project_id' for file in $issue_files; do echo "---- Searching in file: $file ----" rg -n "project_id" --type py "$file" echo "" doneLength of output: 4860
Action Required: Verify consistent handling of string project IDs in IssueAPIEndpoint
The recent changes switching from
<uuid:project_id>to<str:project_id>require careful validation. Inapiserver/plane/api/views/issue.py, we observed that some endpoints explicitly convertproject_idusingstr(project_id)(e.g., lines 367, 422, 476, etc.) while others, such as those invokingProject.objects.get(pk=project_id)(e.g., lines 311 and 377), pass the parameter directly. Please ensure that:
- Database lookups and any associated logic properly handle project IDs as strings.
- There is consistent and intentional conversion or validation of
project_idwhere required.- The behavior of endpoints aligns with the intended change, avoiding any regression or unexpected results from the modified URL converter.
Kindly review and verify that all endpoints process the string project ID correctly according to the new design.
web/core/components/issues/peek-overview/properties.tsx (3)
33-34: LGTM! Clean import statementsThe import of
IssueWorklogPropertyandISSUE_ADDITIONAL_PROPERTIESare appropriate and follow the project's import structure conventions.
306-316: Well-structured rendering of additional propertiesThe code properly maps through
ISSUE_ADDITIONAL_PROPERTIESand conditionally renders each property when it exists in the issue object. The UI structure is consistent with other property displays in the component.
318-318: Good integration of the CustomProperties componentThe integration of the
CustomPropertiescomponent at the end of the properties section ensures a clean separation between standard and custom properties while providing a consistent UI experience.apiserver/plane/api/views/base.py (5)
11-11: Imports look correct.The new imports
ProjectandUserfromplane.db.modelsare appropriately used in the new helper methods below. No concerns here.
115-118: Confirm intended return flow within dispatch.After handling
kwargs, user creation, and workspace checks, the function callssuper().dispatch, then returns the response. Ensure that if an exception occurs later, the logic gracefully returns an appropriate response rather than an exception object.
129-144: Review user assignment logic.The
check_kwargsfunction automatically adds an admin user to a workspace if aslugis present. Verify that this approach is safe and intentional, especially around data integrity and potential side effects of forcibly adding a superuser to every referenced workspace.
145-164: Validate user creation from headers.While
X-Assume-Roleis a common design pattern for role impersonation, automatically creating a new user based on an arbitrary header could lead to unauthorized user entries in the database. Confirm that additional authentication checks (e.g. verifying the header’s authenticity) are performed elsewhere.
166-176: Check membership addition.
ensure_member_in_workspaceautomatically adds an authenticated user to the workspace ifslugis present. Confirm that this membership addition is validated to prevent unauthorized workspace access.web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx (3)
19-19: Good addition of additional property filters.Using
FilterAdditionalPropertiessignificantly enhances the filter capabilities. No issues found.
25-25: Logical usage ofISSUE_ADDITIONAL_PROPERTIES.Importing and mapping over these properties is a clean approach for dynamic filters.
30-30: Custom property filter import is well-structured.
FilterCustomPropertycomplements the additional properties usage.apiserver/plane/authentication/provider/credentials/magic_code.py (4)
47-52: Reintroduce or handle missing SMTP configuration check.
By commenting out the SMTP configuration check, the code no longer raises an exception whenEMAIL_HOSTis undefined. This may lead to unforeseen issues if the application tries to send emails without a valid SMTP configuration. Consider reintroducing the check or adding a fallback to avoid email-related failures.
111-114: Confirm the reduced expiry from 600 to 30 seconds.
The token expiry has been shortened to 30 seconds for an existing key, while newly created keys still have a 600-second expiry. Verify that this mismatch is intentional, as it can create inconsistent user experiences.
138-138: Username retrieval looks correct.
Retrievingusernamefrom the Redis data is consistent with the newly introduced logic. This appears fine.
145-150: Verify the default password auto-set flag.
Theis_password_autosetfield has changed toFalse. Ensure this is the intended default and that it does not disrupt password onboarding flows.apiserver/plane/api/views/project.py (1)
34-122: General creation logic looks good.
The newcreate_projecthelper cleanly organizes project creation steps (member assignment, default states, etc.). This separation can simplify testing and maintenance.apiserver/Dockerfile.api (4)
33-34: Great addition of Flower for Celery monitoring!Adding Flower provides valuable visibility into Celery task execution and performance.
51-52: Properly exposing ports for API and FlowerGood practice to document both services' ports in the EXPOSE directive.
54-61: Good approach for environment file configurationUsing build arguments to specify the environment file path provides flexibility for different deployment scenarios.
67-77: Well-structured conditional CMD handlingThe CMD instruction effectively handles different environment types with appropriate commands for each service type. The error handling for unknown environment types is also a good practice.
packages/types/src/view-props.d.ts (1)
109-115: Consistent property additionsThe new filter properties align well with the TIssueParams type additions.
packages/types/src/issues/issue.d.ts (1)
61-67: Good addition of custom property fieldsAdding typed fields for custom properties enhances the extensibility of the issue model while maintaining type safety.
web/core/store/issue/helpers/issue-filter-helper.store.ts (3)
85-85: Added support for custom properties filtering in issues.This change adds the ability to filter issues by custom properties, aligning with the new custom properties feature being implemented.
107-112: Additional filter properties for domain-specific fields.These additional filter properties (hub_code, worker_code, customer_code, etc.) enhance the filtering capabilities for domain-specific information.
154-159: Additional filter properties for computedFilters.Consistent implementation of the same properties in the computedFilters method, ensuring they are properly handled throughout the filtering pipeline.
apiserver/plane/app/views/view/base.py (5)
43-43: Added import for IssueCustomProperty model.This import is required for the new custom properties functionality.
51-51: Added import for ALLOWED_CUSTOM_PROPERTY_WORKSPACE_MAP constant.This import is used to determine which custom properties are allowed for each workspace.
63-74: New serializer for IssueCustomProperty.The serializer correctly defines the necessary fields and read-only attributes for the custom property model.
314-328: New method for listing custom properties.This method retrieves and groups custom properties based on allowed keys for the workspace. It's a useful addition for the frontend to know what custom properties are available.
Just ensure that this endpoint is properly documented in your API documentation.
332-338: Handle custom properties filtering in the list method.This change properly isolates the custom_properties filter from other filters to avoid passing it directly to the Django ORM.
apiserver/plane/app/serializers/issue.py (5)
35-35: Added import for IssueCustomProperty model.This import is required for the new custom properties serializer.
667-678: New serializer for IssueCustomProperty.This serializer properly defines the fields and read-only properties for the custom property model, consistent with other serializers in the file.
711-716: Added new fields to the IssueSerializer.These new fields (hub_code, customer_code, reference_number, etc.) enhance the issue model with domain-specific information.
Make sure these fields are properly documented and their purpose is clear to other developers.
754-754: Added custom_properties field to IssueDetailSerializer.This allows the API to return custom properties when retrieving issue details.
760-760: Updated fields list with custom_properties.Ensuring custom_properties is included in the serialized output.
apiserver/plane/db/migrations/0084_projectcustomproperty_issuecustomproperty_and_more.py (1)
1-69: Well-structured migration for custom properties models.The migration properly creates two new models (
ProjectCustomPropertyandIssueCustomProperty) with appropriate fields, relationships, and constraints. The design follows Django best practices with timestamp tracking, UUID primary keys, and clear foreign key relationships.apiserver/plane/api/views/member.py (5)
70-71: Good simplification of error message.Removed the requirement for
display_namein the error message which aligns with the validation logic.
94-94: Improved case handling for email.Converting email to lowercase ensures case-insensitive matching when querying users, which is a good security practice.
117-143: Code refactoring improves maintainability.Extracting user and member creation into separate methods improves code organization and readability.
150-162: Well-implemented user creation method.The method properly handles user creation with appropriate default values for required fields.
165-180: Good extraction of member creation logic.Breaking down workspace and project member creation into separate methods enhances code modularity.
web/core/components/issues/issue-detail/sidebar.tsx (3)
57-57: Good default handling for custom properties.Using the nullish coalescing operator to default to an empty array prevents potential runtime errors.
304-316: Well-implemented dynamic rendering of additional properties.The code cleanly maps through the additional properties array, checking for existence before rendering, which prevents displaying empty fields.
318-320: Clean integration of custom properties component.Ensuring the custom properties array is properly validated before passing it to the component prevents unexpected behavior.
apiserver/plane/utils/issue_filters.py (4)
286-302: Clean implementation of username filtering.The method properly handles filtering issues by the creator's username, supporting comma-separated values.
593-609: Good implementation of character field filtering.The function efficiently filters issues by predefined character fields using case-insensitive matching.
624-641: Proper integration of new filter functions.The new filter functions are correctly added to the
ISSUE_FILTERdictionary, making them available for use in the filtering system.
648-648: Good addition of character field filtering.Adding a direct call to
filter_character_fieldsensures these filters are always applied, regardless of the query parameters.apiserver/plane/api/serializers/issue.py (1)
84-85: Custom properties integration looks good.Introducing
custom_propertiesin the serializer is a clean way to manage extended fields for issues. This approach simplifies future scalability.apiserver/plane/bgtasks/issue_activities_task.py (1)
602-614: Dynamic verb assignment is well done.Allowing
track_assigneesto receive averbparameter makes this utility more flexible. This is a good enhancement that encourages code reuse for both "created" and "updated" actions.apiserver/plane/app/views/issue/base.py (6)
8-8: Libraries imported for pagination
No issues found; this import is used for the custom search endpoints.
38-38: BaseSerializer import
Looks good; it's properly used by the new custom property serializer.
51-51: IssueCustomProperty model import
Essential for the newly added serializer and subquery-based filtering.
77-88: IssueCustomPropertySerializer definition
The serializer fields and read-only settings match the model as expected.
299-300: Filtering approach
Separating custom properties from other filters is a clean and readable strategy.
303-303: Chained filtering
Applying both the custom property subquery and the remaining filters is well-structured.apiserver/plane/api/views/webhook.py (1)
1-130: Overall new file
The webhook CRUD endpoints and secret regeneration are well-structured, with clear permission checks and validations.apiserver/plane/api/views/issue.py (2)
42-42: Guest-level permissions
Switching toProjectEntityGuestPermissionis appropriate for allowing controlled guest access.
493-497: State field rename
Renaming'state'to'state_id'in partial updates is a pragmatic approach that aligns with the model’s fields.apiserver/plane/api/views/search.py (3)
50-66: Ensure consistent filtering across all returned projects.
The filtering logic is solid; however, because you filter on multiple fields with an OR (q |= Q(...)), you might retrieve more results than intended if user input is partial. Verify this aligns with your expected user experience.
101-125: Check for cyclical usage confusion.
All cycles are returned if noproject_idis specified. Ensure this matches your intended behavior, as large sets of cycles might hamper performance if multiple projects exist.
151-204: Validate that module associations in pages are handled correctly.
You annotate pages with project IDs and project identifiers. If a page belongs to multiple projects, confirm that updated references in lines 167–182 don’t break page–project relations unexpectedly.apiserver/plane/db/models/issue.py (1)
79-84: Confirm necessity of always-visible codes.
The new display properties (hub_code,customer_code, etc.) default toTrue. Verify whether these fields should be visible by default if they’re optional in the data model.
| path( | ||
| "workspaces/<str:slug>/projects/<str:project_id>/filed_search", | ||
| SearchSingleValueAPI.as_view(), | ||
| name="key-codes" | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Multiple issues with URL pattern definition
This URL pattern has several issues:
- It uses the same name "key-codes" as the previous pattern, which can cause URL reversing conflicts
- Missing trailing slash inconsistent with Django conventions
- Uses
<str:project_id>instead of<uuid:project_id>like other patterns
- path(
- "workspaces/<str:slug>/projects/<str:project_id>/filed_search",
- SearchSingleValueAPI.as_view(),
- name="key-codes"
- )
+ path(
+ "workspaces/<str:slug>/projects/<uuid:project_id>/filed_search/",
+ SearchSingleValueAPI.as_view(),
+ name="field-search"
+ )📝 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.
| path( | |
| "workspaces/<str:slug>/projects/<str:project_id>/filed_search", | |
| SearchSingleValueAPI.as_view(), | |
| name="key-codes" | |
| ) | |
| path( | |
| "workspaces/<str:slug>/projects/<uuid:project_id>/filed_search/", | |
| SearchSingleValueAPI.as_view(), | |
| name="field-search" | |
| ) |
| path( | ||
| "workspaces/<str:slug>/projects/<str:project_id>/search", | ||
| SearchAPIEndpoint.as_view(), | ||
| name="key-codes", | ||
| ), | ||
| path( | ||
| "workspaces/<str:slug>/projects/<str:project_id>/filed_search", | ||
| SearchSingleValueAPI.as_view(), | ||
| name="key-codes" | ||
| ) |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify search functionality against implementation
Based on the included code snippets, these new endpoints provide search functionality for specific fields like hub_code, worker_code, reference_number, etc. However, the route names ("key-codes") don't clearly reflect their purpose as search endpoints.
🏁 Script executed:
#!/bin/bash
# Check both implementations to verify their purpose
echo "SearchAPIEndpoint implementation:"
rg -A 10 "class SearchAPIEndpoint" --glob "apiserver/plane/app/views/issue/base.py"
echo "\nSearchSingleValueAPI implementation:"
rg -A 10 "class SearchSingleValueAPI" --glob "apiserver/plane/app/views/issue/base.py"
# Check if these fields are defined in the Issue model
echo "\nIssue model fields:"
rg "class Issue\(" -A 30 --glob "apiserver/plane/db/models/**/*.py" | grep -E "hub_code|worker_code|reference_number|trip_reference_number"Length of output: 2434
Action Required: Refactor URL Route Names for Clarity
The implementations of both SearchAPIEndpoint and SearchSingleValueAPI clearly show that these endpoints are designed to perform search operations on issues, filtering based on fields such as hub_code, worker_code, reference_number, trip_reference_number, customer_code, and vendor_code. However, using the route name "key-codes" for both endpoints does not adequately communicate their search functionality. Please update the URL route names to reflect their purpose more clearly (for example, "search-issues" and "field-search-issues") to improve code clarity and maintainability.
| path( | ||
| "workspaces/<str:slug>/projects/<str:project_id>/search", | ||
| SearchAPIEndpoint.as_view(), | ||
| name="key-codes", | ||
| ), |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Parameter type inconsistency detected
The URL pattern uses <str:project_id> while most other patterns in this file use <uuid:project_id>. This inconsistency could lead to compatibility issues if the same parameter is expected to be a UUID in the view implementation.
🏁 Script executed:
#!/bin/bash
# Check if the SearchAPIEndpoint implementation expects project_id as UUID or string
rg -A 3 "def get\(.*?slug, project_id\)" --glob "apiserver/plane/app/views/issue/base.py"
# Check if other places in the codebase handle project_id as UUID
rg -A 2 "project_id\s*=\s*.*?UUID" --type pyLength of output: 2199
Update URL pattern to use UUID converter
The URL for SearchAPIEndpoint still uses <str:project_id>, while the majority of URL patterns in this file use <uuid:project_id>. This inconsistency can lead to situations where the view ends up receiving a string instead of a UUID, which may cause type mismatches (especially when interacting with models or functions that expect a UUID). To maintain consistency and ensure proper type conversion, please update the URL pattern as follows:
- Change
to
"workspaces/<str:slug>/projects/<str:project_id>/search""workspaces/<str:slug>/projects/<uuid:project_id>/search"
| <a></a> | ||
| // <a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}> | ||
| // Available on One | ||
| // <SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" /> | ||
| // </a> |
There was a problem hiding this comment.
Component rendering an empty anchor element
The upgrade button functionality has been commented out, resulting in an empty <a> tag being rendered. This creates an empty DOM element without any user-visible or functional purpose.
Consider one of the following approaches instead:
- If the feature is intentionally disabled, return null instead of an empty anchor:
- <a></a>
- // <a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}>
- // Available on One
- // <SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" />
- // </a>
+ null- If the feature will be reenabled later, use a conditional rendering approach instead of commenting out code:
- <a></a>
- // <a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}>
- // Available on One
- // <SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" />
- // </a>
+ {false && (
+ <a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}>
+ Available on One
+ <SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" />
+ </a>
+ )}📝 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.
| <a></a> | |
| // <a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}> | |
| // Available on One | |
| // <SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" /> | |
| // </a> | |
| null |
| ] | ||
|
|
||
| def get_queryset(self): | ||
| print(self.kwargs.get("project_id")) |
There was a problem hiding this comment.
Remove debugging print statement
There's a print statement that appears to have been added for debugging purposes. This should be removed before pushing to production as it will unnecessarily clutter logs with project_id values.
- print(self.kwargs.get("project_id"))📝 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.
| print(self.kwargs.get("project_id")) |
| export const FilterAdditionalProperties: React.FC<Props> = observer((props) => { | ||
| const { workspaceSlug, projectId } = useParams(); | ||
| const { appliedFilters, handleUpdate, searchQuery, additionalPropertyTitle, additionalPropertyKey } = props; | ||
| const workspaceService = new WorkspaceService(API_BASE_URL); | ||
|
|
||
| const [options, setOptions] = useState<{ label: string; value: string }[]>([]); | ||
| const [previewEnabled, setPreviewEnabled] = useState(true); | ||
| const [visibleOptions, setVisibleOptions] = useState(5); | ||
|
|
||
| useEffect(() => { | ||
| const fetchOptions = async () => { | ||
| try { | ||
| const response = await workspaceService.getIssueAdditionalProperties(workspaceSlug.toString(), projectId.toString(), props.additionalPropertyKey); | ||
| const formattedOptions = response.data.map((item: string) => ({ label: item, value: item })) || []; | ||
| setOptions(formattedOptions); | ||
| } catch (error) { | ||
| console.error(`Error fetching ${additionalPropertyKey} options:`, error); | ||
| } | ||
| }; | ||
|
|
||
| fetchOptions(); | ||
| }, [workspaceSlug, projectId, additionalPropertyKey]); | ||
|
|
||
| const filteredOptions = searchQuery | ||
| ? options.filter((option: any) => option?.label.toLowerCase().includes(searchQuery.toLowerCase())) | ||
| : options; | ||
|
|
||
| return ( | ||
| <> | ||
| <FilterHeader | ||
| title={`${additionalPropertyTitle} ${appliedFilters?.length ? `(${appliedFilters.length})` : ""}`} | ||
| isPreviewEnabled={previewEnabled} | ||
| handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} | ||
| /> | ||
| {previewEnabled && ( | ||
| <div> | ||
| {filteredOptions.length > 0 ? ( | ||
| <> | ||
| {filteredOptions.slice(0, visibleOptions).map(({ label, value }) => ( | ||
| <FilterOption | ||
| key={value} | ||
| isChecked={appliedFilters?.includes(value) ?? false} | ||
| onClick={() => handleUpdate(value)} | ||
| title={label} | ||
| /> | ||
| ))} | ||
| {filteredOptions.length > 5 && ( | ||
| <button | ||
| className="text-blue-500 text-xs mt-2" | ||
| onClick={() => setVisibleOptions(visibleOptions === 5 ? filteredOptions.length : 5)} | ||
| > | ||
| {visibleOptions === 5 ? "View More" : "View Less"} | ||
| </button> | ||
| )} | ||
| </> | ||
| ) : ( | ||
| <p className="text-xs italic text-custom-text-400">No Matches Found</p> | ||
| )} | ||
| </div> | ||
| )} | ||
| </> | ||
| ); | ||
| }); No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion
New component for filtering by additional properties.
The FilterAdditionalProperties component provides UI for filtering issues by custom properties. The implementation follows React best practices with proper state management and rendering.
However, there are a few issues to address:
- Missing dependency in useEffect:
- }, [workspaceSlug, projectId, additionalPropertyKey]);
+ }, [workspaceSlug, projectId, additionalPropertyKey, workspaceService, props.additionalPropertyKey]);- Missing type safety for URL parameters:
- const response = await workspaceService.getIssueAdditionalProperties(workspaceSlug.toString(), projectId.toString(), props.additionalPropertyKey);
+ const response = await workspaceService.getIssueAdditionalProperties(
+ workspaceSlug?.toString() || "",
+ projectId?.toString() || "",
+ props.additionalPropertyKey
+ );- Consider adding a loading state indicator while fetching data:
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
const [previewEnabled, setPreviewEnabled] = useState(true);
const [visibleOptions, setVisibleOptions] = useState(5);
+ const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchOptions = async () => {
+ setIsLoading(true);
try {
const response = await workspaceService.getIssueAdditionalProperties(workspaceSlug.toString(), projectId.toString(), props.additionalPropertyKey);
const formattedOptions = response.data.map((item: string) => ({ label: item, value: item })) || [];
setOptions(formattedOptions);
} catch (error) {
console.error(`Error fetching ${additionalPropertyKey} options:`, error);
+ } finally {
+ setIsLoading(false);
}
};
fetchOptions();
}, [workspaceSlug, projectId, additionalPropertyKey]);- Add a loading state in the component rendering:
{previewEnabled && (
<div>
+ {isLoading ? (
+ <p className="text-xs text-custom-text-400">Loading...</p>
+ ) : (
{filteredOptions.length > 0 ? (
<>
{filteredOptions.slice(0, visibleOptions).map(({ label, value }) => (
<FilterOption
key={value}
isChecked={appliedFilters?.includes(value) ?? false}
onClick={() => handleUpdate(value)}
title={label}
/>
))}
{filteredOptions.length > 5 && (
<button
className="text-blue-500 text-xs mt-2"
onClick={() => setVisibleOptions(visibleOptions === 5 ? filteredOptions.length : 5)}
>
{visibleOptions === 5 ? "View More" : "View Less"}
</button>
)}
</>
) : (
<p className="text-xs italic text-custom-text-400">No Matches Found</p>
)}
+ )}
</div>
)}📝 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.
| export const FilterAdditionalProperties: React.FC<Props> = observer((props) => { | |
| const { workspaceSlug, projectId } = useParams(); | |
| const { appliedFilters, handleUpdate, searchQuery, additionalPropertyTitle, additionalPropertyKey } = props; | |
| const workspaceService = new WorkspaceService(API_BASE_URL); | |
| const [options, setOptions] = useState<{ label: string; value: string }[]>([]); | |
| const [previewEnabled, setPreviewEnabled] = useState(true); | |
| const [visibleOptions, setVisibleOptions] = useState(5); | |
| useEffect(() => { | |
| const fetchOptions = async () => { | |
| try { | |
| const response = await workspaceService.getIssueAdditionalProperties(workspaceSlug.toString(), projectId.toString(), props.additionalPropertyKey); | |
| const formattedOptions = response.data.map((item: string) => ({ label: item, value: item })) || []; | |
| setOptions(formattedOptions); | |
| } catch (error) { | |
| console.error(`Error fetching ${additionalPropertyKey} options:`, error); | |
| } | |
| }; | |
| fetchOptions(); | |
| }, [workspaceSlug, projectId, additionalPropertyKey]); | |
| const filteredOptions = searchQuery | |
| ? options.filter((option: any) => option?.label.toLowerCase().includes(searchQuery.toLowerCase())) | |
| : options; | |
| return ( | |
| <> | |
| <FilterHeader | |
| title={`${additionalPropertyTitle} ${appliedFilters?.length ? `(${appliedFilters.length})` : ""}`} | |
| isPreviewEnabled={previewEnabled} | |
| handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} | |
| /> | |
| {previewEnabled && ( | |
| <div> | |
| {filteredOptions.length > 0 ? ( | |
| <> | |
| {filteredOptions.slice(0, visibleOptions).map(({ label, value }) => ( | |
| <FilterOption | |
| key={value} | |
| isChecked={appliedFilters?.includes(value) ?? false} | |
| onClick={() => handleUpdate(value)} | |
| title={label} | |
| /> | |
| ))} | |
| {filteredOptions.length > 5 && ( | |
| <button | |
| className="text-blue-500 text-xs mt-2" | |
| onClick={() => setVisibleOptions(visibleOptions === 5 ? filteredOptions.length : 5)} | |
| > | |
| {visibleOptions === 5 ? "View More" : "View Less"} | |
| </button> | |
| )} | |
| </> | |
| ) : ( | |
| <p className="text-xs italic text-custom-text-400">No Matches Found</p> | |
| )} | |
| </div> | |
| )} | |
| </> | |
| ); | |
| }); | |
| export const FilterAdditionalProperties: React.FC<Props> = observer((props) => { | |
| const { workspaceSlug, projectId } = useParams(); | |
| const { appliedFilters, handleUpdate, searchQuery, additionalPropertyTitle, additionalPropertyKey } = props; | |
| const workspaceService = new WorkspaceService(API_BASE_URL); | |
| const [options, setOptions] = useState<{ label: string; value: string }[]>([]); | |
| const [previewEnabled, setPreviewEnabled] = useState(true); | |
| const [visibleOptions, setVisibleOptions] = useState(5); | |
| const [isLoading, setIsLoading] = useState(false); | |
| useEffect(() => { | |
| const fetchOptions = async () => { | |
| setIsLoading(true); | |
| try { | |
| const response = await workspaceService.getIssueAdditionalProperties( | |
| workspaceSlug?.toString() || "", | |
| projectId?.toString() || "", | |
| props.additionalPropertyKey | |
| ); | |
| const formattedOptions = response.data.map((item: string) => ({ label: item, value: item })) || []; | |
| setOptions(formattedOptions); | |
| } catch (error) { | |
| console.error(`Error fetching ${additionalPropertyKey} options:`, error); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| fetchOptions(); | |
| }, [workspaceSlug, projectId, additionalPropertyKey, workspaceService, props.additionalPropertyKey]); | |
| const filteredOptions = searchQuery | |
| ? options.filter((option: any) => option?.label.toLowerCase().includes(searchQuery.toLowerCase())) | |
| : options; | |
| return ( | |
| <> | |
| <FilterHeader | |
| title={`${additionalPropertyTitle} ${appliedFilters?.length ? `(${appliedFilters.length})` : ""}`} | |
| isPreviewEnabled={previewEnabled} | |
| handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} | |
| /> | |
| {previewEnabled && ( | |
| <div> | |
| {isLoading ? ( | |
| <p className="text-xs text-custom-text-400">Loading...</p> | |
| ) : ( | |
| filteredOptions.length > 0 ? ( | |
| <> | |
| {filteredOptions.slice(0, visibleOptions).map(({ label, value }) => ( | |
| <FilterOption | |
| key={value} | |
| isChecked={appliedFilters?.includes(value) ?? false} | |
| onClick={() => handleUpdate(value)} | |
| title={label} | |
| /> | |
| ))} | |
| {filteredOptions.length > 5 && ( | |
| <button | |
| className="text-blue-500 text-xs mt-2" | |
| onClick={() => setVisibleOptions(visibleOptions === 5 ? filteredOptions.length : 5)} | |
| > | |
| {visibleOptions === 5 ? "View More" : "View Less"} | |
| </button> | |
| )} | |
| </> | |
| ) : ( | |
| <p className="text-xs italic text-custom-text-400">No Matches Found</p> | |
| ) | |
| )} | |
| </div> | |
| )} | |
| </> | |
| ); | |
| }); |
| def patch(self, request, slug, project_id, issue_id, pk): | ||
| issue_attachment = FileAsset.objects.get( | ||
| pk=pk, workspace__slug=slug, project_id=project_id | ||
| ) | ||
| serializer = IssueAttachmentSerializer(issue_attachment) | ||
|
|
||
| # Send this activity only if the attachment is not uploaded before | ||
| if not issue_attachment.is_uploaded: | ||
| issue_activity.delay( | ||
| type="attachment.activity.created", | ||
| requested_data=None, | ||
| actor_id=str(self.request.user.id), | ||
| issue_id=str(self.kwargs.get("issue_id", None)), | ||
| project_id=str(self.kwargs.get("project_id", None)), | ||
| current_instance=json.dumps( | ||
| serializer.data, | ||
| cls=DjangoJSONEncoder, | ||
| ), | ||
| epoch=int(timezone.now().timestamp()), | ||
| notification=True, | ||
| origin=request.META.get("HTTP_ORIGIN"), | ||
| ) | ||
|
|
||
| # Update the attachment | ||
| issue_attachment.is_uploaded = True | ||
| issue_attachment.created_by = request.user | ||
|
|
||
| # Get the storage metadata | ||
| if not issue_attachment.storage_metadata: | ||
| get_asset_object_metadata.delay(str(issue_attachment.id)) | ||
| issue_attachment.save() | ||
| return Response(status=status.HTTP_204_NO_CONTENT) No newline at end of file |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Ensure concurrency safety when marking attachments as uploaded.
If multiple requests or background tasks simultaneously modify the same attachment’s status, there’s a possibility of inconsistent states. Confirm that the application logic or database layer properly handles concurrent updates (e.g., via transactional integrity in Django ORM).
🏁 Script executed:
#!/bin/bash
# Check for references to concurrency locks or transaction atomic blocks
rg -A 4 --case-sensitive "transaction\.atomic" Length of output: 1245
Concurrency safety for attachment updates needs to be addressed.
The current patch method in apiserver/plane/api/views/attachment.py checks and updates the attachment's is_uploaded and created_by fields without an explicit transactional safeguard. Our repository shows that similar update operations in other parts (see usage of transaction.atomic in apiserver/plane/db/models/issue.py and apiserver/plane/app/views/view/base.py) are wrapped in transaction blocks to ensure consistency. To prevent race conditions when concurrent requests attempt to update the same attachment, please consider wrapping the check-and-update logic in a transaction.atomic block and, if necessary, using select_for_update when fetching the attachment record.
- Location:
apiserver/plane/api/views/attachment.py, patch method (lines 143–174) - Suggested Change: Wrap the
if not issue_attachment.is_uploaded:block and subsequent updates in awith transaction.atomic():block to enforce transactional integrity.
| if self.instance is None: | ||
| if not is_uuid(data.get('created_by')): | ||
| if User.objects.filter(username=data['created_by']).exists(): | ||
| data['created_by'] = User.objects.get(username=data['created_by']) | ||
| else: | ||
| user_data = { | ||
| "email": data['created_by'] + '@plane-shipsy.com', | ||
| "username": data['created_by'], | ||
| "role": 5, | ||
| "display_name": data['created_by'] | ||
| } | ||
| from plane.api.views import ProjectMemberAPIEndpoint | ||
| PMObj = ProjectMemberAPIEndpoint() | ||
| user = PMObj.create_user(user_data) | ||
| PMObj.create_workspace_member(self.context.get("workspace_id"), user,5) | ||
| PMObj.create_project_member(self.context.get("project_id"), user,5) | ||
| data['created_by'] = user |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid view-layer imports in the serializer.
The code directly imports and calls ProjectMemberAPIEndpoint from within the serializer, coupling the serializers and views. This may make the code harder to maintain or test. Consider extracting user-creation logic into a shared service or manager class.
| email = request.data.get("email", False) | ||
| if not email: | ||
| username = request.data.get("username", False) | ||
| if username: | ||
| email = username + "@plane-shipsy.com" | ||
| print(email) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Beware of potential email collision.
Generating an email by appending “@plane-shipsy.com” could create duplicates if different users share the same username. Consider validating email uniqueness or informing the user on conflicts.
| class IssueCustomProperty(ProjectBaseModel): | ||
| issue = models.ForeignKey( | ||
| Issue, on_delete=models.CASCADE, related_name="custom_properties" | ||
| ) | ||
| key = models.CharField(max_length=255) | ||
| value = models.CharField(max_length=255, null=True, blank=True) | ||
| issue_type_custom_property = models.ForeignKey( | ||
| "db.IssueTypeCustomProperty", | ||
| on_delete=models.SET_NULL, | ||
| related_name="issue_type_custom_property", | ||
| null=True, | ||
| blank=True, | ||
| ) | ||
|
|
||
| class Meta: | ||
| verbose_name = "Issue Custom Property" | ||
| verbose_name_plural = "Issue Custom Properties" | ||
| db_table = "issue_custom_properties" | ||
| ordering = ("-created_at",) | ||
|
|
||
| def __str__(self): | ||
| return f"{self.issue.name} {self.key}" No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Enhance concurrency and validation for custom properties.
The new IssueCustomProperty model allows flexible additions of key-value pairs. If these properties can be updated frequently or in parallel, consider an optimistic lock or additional validation to prevent conflicting writes.
…operties--std-fields
…y_properties--std-fields
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (7)
apiserver/plane/authentication/views/app/magic.py (7)
70-70: Remove debug print statement.Remove the print statement before pushing to production.
- print(email)
95-101: Method name does not fully reflect its functionality.The
add_user_to_workspacemethod does more than just add a user to a workspace - it also creates workspaces and adds users to projects. Consider renaming to better reflect the full scope, such assetup_user_workspace_access.
104-127: Improve exception handling in add_to_workspace method.The exception is caught but only printed to console, which isn't appropriate for production code. Consider proper logging and error handling.
try: user.profile.last_workspace_id = workspace.id user.profile.onboarding_step.update({ 'profile_complete': True, 'workspace_join': True }) user.profile.is_tour_completed = True user.profile.is_onboarded = True user.is_password_autoset = False user.profile.company_name = workspace.name user.save() user.profile.save() except Exception as e: - print(e) + # Use proper logging + import logging + logger = logging.getLogger(__name__) + logger.error("Error updating user profile: %s", str(e))
158-183: Improve error handling in get_or_create_project method.The serializer validation errors are raised as a generic Exception, which makes error handling less precise. Consider returning a more descriptive error or using a specific exception type.
prSer.is_valid() if prSer.errors: - raise Exception(prSer.errors) + from rest_framework.exceptions import ValidationError + raise ValidationError(prSer.errors)
188-188: Remove debug print statement.Remove the print statement before pushing to production.
- print(base_host(request=request, is_app=True))
262-262: Remove debug print statement.Remove the print statement before pushing to production.
- print(params)
287-287: Unused variable 'is_app'.The variable
is_appis assigned but never used in the method, as flagged by static analysis.Either use this variable or remove it:
- is_app = request.POST.get("is_app", False)Or if it's intended for future use, add a comment:
is_app = request.POST.get("is_app", False) + # TODO: is_app will be used in future implementation🧰 Tools
🪛 Ruff (0.8.2)
287-287: Local variable
is_appis assigned to but never usedRemove assignment to unused variable
is_app(F841)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apiserver/plane/authentication/views/app/magic.py(5 hunks)apiserver/plane/db/models/issue_type.py(1 hunks)web/core/constants/spreadsheet.ts(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apiserver/plane/db/models/issue_type.py
🧰 Additional context used
🧬 Code Definitions (2)
web/core/constants/spreadsheet.ts (1)
web/core/components/issues/issue-layouts/spreadsheet/columns/standard-property-column.tsx (1) (1)
SpreadsheetStandardPropertyColumn(11-21)
apiserver/plane/authentication/views/app/magic.py (3)
apiserver/plane/api/serializers/project.py (2) (2)
ProjectSerializer(15-88)create(66-88)apiserver/plane/api/views/base.py (2) (2)
BaseAPIView(40-226)workspace_slug(195-196)apiserver/plane/api/views/project.py (1) (1)
create_project(34-122)
🪛 Ruff (0.8.2)
apiserver/plane/authentication/views/app/magic.py
287-287: Local variable is_app is assigned to but never used
Remove assignment to unused variable is_app
(F841)
🔇 Additional comments (12)
web/core/constants/spreadsheet.ts (6)
14-14: Icon import addition looks good.The addition of the
Tagsicon import is appropriate for the new standard properties being added.
36-36: SpreadsheetStandardPropertyColumn import is properly added.This component is required for the new standard properties and is correctly imported.
181-189: Hub Code property implementation looks good.The property follows the established pattern with appropriate title, ordering keys, and component usage.
190-234: Standard properties implementation is consistent.All the new properties (customer_code, worker_code, vendor_code, trip_reference_number, reference_number) follow the same consistent pattern with appropriate titles, ordering keys, and use of the Tags icon.
252-257: Property list updated correctly.The SPREADSHEET_PROPERTY_LIST has been properly updated to include all the new standard properties.
181-234:Details
❓ Verification inconclusive
Verify type definitions and backend support.
Please ensure that the
IIssueDisplayPropertiestype definition has been updated to include these new properties, and that the backend API supports these fields for issues.
🏁 Script executed:
#!/bin/bash # Check if IIssueDisplayProperties interface includes the new properties fd -e "ts" -e "tsx" "IIssueDisplayProperties" --exec grep -l "IIssueDisplayProperties" {} \; | xargs catLength of output: 101
Action Required: Manually Verify Interface and Backend Updates
It appears that our automated search did not return any results for the
IIssueDisplayPropertiesinterface. Please confirm manually that the interface now includes the new properties:
hub_codecustomer_codeworker_codevendor_codetrip_reference_numberreference_numberAlso, verify that the backend API supports these fields for issues.
apiserver/plane/authentication/views/app/magic.py (6)
27-32: Good organization of imports.The added imports are well-structured and logically grouped into related components (models, serializers). This promotes better code maintainability.
66-70: Beware of potential email collision.Generating an email by appending "@plane-shipsy.com" could create duplicates if different users share the same username. Consider validating email uniqueness or informing the user on conflicts.
79-79: Return value modification to include token.The response now returns both key and token, which appears to be an intentional change. Make sure any clients consuming this API are updated to handle the new response format.
212-234: User flow logic has been improved.The logic for handling non-existing users has been improved to authenticate the user, create a profile, add them to the workspace, and redirect them appropriately. This provides a smoother onboarding experience.
245-258: User flow for existing users updated to include workspace addition.The existing user flow now includes adding the user to the workspace before login, which ensures a consistent experience for both new and existing users.
41-44:Details
✅ Verification successful
Security concern: Commented out permission classes.
The permission classes are commented out but the base class has changed from
APIViewtoBaseAPIView. Verify whetherBaseAPIViewincludes appropriate permission classes, as removingAllowAnypermissions without replacement could create unintended authentication requirements.
🏁 Script executed:
#!/bin/bash # Check the BaseAPIView implementation for permission classes cat apiserver/plane/api/views/base.py | grep -A 10 "class BaseAPIView"Length of output: 349
Security Check: Inherited Permissions from BaseAPIView
The MagicGenerateEndpoint now relies on BaseAPIView, which explicitly sets its permission classes to
[IsAuthenticated]. This confirms that the intentional removal of theAllowAnypermission is safe—as long as the desired behavior is to require authentication. If you intended for the endpoint to allow anonymous access, you'll need to override the inherited permissions.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/core/constants/spreadsheet.ts (1)
181-234: Consider using more visually distinct icons for different property types.While the implementation of the new properties (hub_code, customer_code, worker_code, vendor_code, trip_reference_number, reference_number) is structurally sound, they all use the same
Tagsicon. Consider using different icons for different categories of properties to improve visual distinction in the UI.For example:
- Building/location icons for hub_code
- Person icons for worker_code and customer_code
- Document/number icons for reference numbers
Additionally, consider adding comments to describe what each of these properties represents and their purpose within the system.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
web/core/constants/spreadsheet.ts(4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
web/core/constants/spreadsheet.ts (1)
web/core/components/issues/issue-layouts/spreadsheet/columns/standard-property-column.tsx (1) (1)
SpreadsheetStandardPropertyColumn(11-21)
🔇 Additional comments (3)
web/core/constants/spreadsheet.ts (3)
14-14: Import addition looks good.The addition of the
Tagsicon fromlucide-reactis appropriate for the new properties that will use this icon.
36-36: Import of SpreadsheetStandardPropertyColumn is appropriate.Good inclusion of the necessary component that will be used for rendering the new standard properties.
252-257: Property list addition is correct.The new properties have been correctly added to the
SPREADSHEET_PROPERTY_LISTarray, making them available for display in the spreadsheet view.
* Added Persian (فارسی) to the list of supported languages. * Updated the TranslationStore to include Persian translations. * Extended TLanguage type to include "fa".
Description
Type of Change
Screenshots and Media (if applicable)
Test Scenarios
References
Summary by CodeRabbit
New Features
Improvements
Bug Fixes