Skip to content

Conversation

@pablohashescobar
Copy link
Member

@pablohashescobar pablohashescobar commented Dec 1, 2025

Description

  • rename IssueUserProperty to ProjectUserProperty
  • add fields sort_order and preferences to ProjectUserProperty
  • migration script for migrating data from ProjectMembers to ProjectUserProperty
  • change references of IssueUserProperty to ProjectUserProperty
  • added field allowed_rate_limit to APITokens

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring

Test Scenarios

  • project workitem grouping and filtering
  • project sorting in the sidebar

References

WEB-5537


Note

Renames IssueUserProperty to ProjectUserProperty across backend, adds preferences and sort_order with data migrations, updates related endpoints and member flows, and adds allowed_rate_limit to APIToken.

  • Models & Migrations
    • Rename IssueUserPropertyProjectUserProperty (table to project_user_properties), update relations/constraints and serializer names.
    • Add preferences and sort_order to ProjectUserProperty; migrate values from ProjectMember (bulk update).
    • Add APIToken.allowed_rate_limit and migrate existing non-service tokens to null workspace.
    • Remove IssueUserProperty model; expose ProjectUserProperty from db.models.project.
  • API & Endpoints
    • Replace IssueUserDisplayPropertyEndpoint with ProjectUserDisplayPropertyEndpoint at workspaces/<slug>/projects/<project_id>/user-properties/ (partial update via serializer; creates if missing).
    • Update imports/usages in issue/project views, serializers, and URLs to use ProjectUserProperty.
  • Project membership & seeding
    • On project create/member add/invite accept/seed: create ProjectUserProperty records; compute sort_order using member’s workspace min where applicable.
    • ProjectMember.save now auto-creates corresponding ProjectUserProperty with derived sort_order.
    • Management command create_project_member now targets ProjectUserProperty.
  • Tests
    • Contract tests updated to assert creation of ProjectUserProperty for project members.

Written by Cursor Bugbot for commit d75efb3. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • API tokens now support a configurable rate-limit setting.
    • Per-project preferences, display settings and sort order are persisted and applied.
  • Bug Fixes

    • Data migrations preserve existing user preferences and sort order and detach workspace associations from legacy API tokens.
  • Tests

    • Tests updated to validate per-project preference, display behavior and migration outcomes.

✏️ Tip: You can customize this high-level summary in your review settings.

@pablohashescobar pablohashescobar requested review from Copilot and dheeru0198 and removed request for Copilot December 1, 2025 09:30
@makeplane
Copy link

makeplane bot commented Dec 1, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

IssueUserProperty was renamed and moved to ProjectUserProperty with added fields (preferences, sort_order) and constraints; code references updated across serializers, views, URLs, tasks, management commands, and tests; two migrations (schema + data) perform the rename and data migration; APIToken gained allowed_rate_limit.

Changes

Cohort / File(s) Summary
Model definitions & exports
apps/api/plane/db/models/issue.py, apps/api/plane/db/models/project.py, apps/api/plane/db/models/__init__.py
Removed IssueUserProperty class; added ProjectUserProperty (fields: user, filters, display_filters, display_properties, rich_filters, preferences, sort_order), updated related_name, db_table, ordering, unique constraints, and adjusted module exports.
Schema & data migrations
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py, apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py
0113: rename table/model to project_user_properties, add preferences and sort_order, change FK/constraints, and add APIToken.allowed_rate_limit. 0114: data migration copies preferences/sort_order from ProjectMember to ProjectUserProperty and clears workspace on non-service APITokens.
API views & endpoints
apps/api/plane/api/views/project.py, apps/api/plane/app/views/project/*, apps/api/plane/app/views/issue/base.py, apps/api/plane/app/views/__init__.py
Replaced IssueUserPropertyProjectUserProperty across project creation, member management, invitations, post-join flows, and display-property endpoints; renamed IssueUserDisplayPropertyEndpointProjectUserDisplayPropertyEndpoint. Some logic now sources sort_order from ProjectUserProperty.
Serializers & URLs
apps/api/plane/app/serializers/issue.py, apps/api/plane/app/serializers/__init__.py, apps/api/plane/app/urls/issue.py
Renamed IssueUserPropertySerializerProjectUserPropertySerializer (Meta.model updated); exports updated; URL patterns and endpoint references switched to ProjectUserDisplayPropertyEndpoint.
Project member flows & sorting
apps/api/plane/app/views/project/member.py, apps/api/plane/db/models/project.py, apps/api/plane/db/management/commands/create_project_member.py
Member creation/update flows now consult or create ProjectUserProperty for per-user project sort_order; removed some prior sort_order computations tied to IssueUserProperty and adjusted field usage (use project_id for new ProjectMember links in some paths).
Invitations & join flows
apps/api/plane/app/views/project/invite.py
Bulk_create now creates ProjectUserProperty records instead of IssueUserProperty; post-join ProjectMember creation uses project_id when adding new members.
Background tasks & seed
apps/api/plane/bgtasks/workspace_seed_task.py
Replaced IssueUserProperty usages with ProjectUserProperty in seeding/bulk_create flows.
APIToken model
apps/api/plane/db/models/api.py
Added APIToken.allowed_rate_limit CharField (max_length=255, default "60/min").
Tests
apps/api/plane/tests/contract/app/test_project_app.py
Updated imports and assertions to expect ProjectUserProperty creation/associations instead of IssueUserProperty.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py — ensure schema changes (table rename, FK, constraints, JSON defaults) align with model code.
    • apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py — validate mapping logic, batch sizes, and data integrity for members without matches or duplicates.
    • apps/api/plane/db/models/project.py and apps/api/plane/app/views/project/member.py — review updated sort_order sourcing and any behavioral changes when adding members.
    • Cross-references — search remaining usages of IssueUserProperty across the codebase.

Poem

🐇 I hopped through code with whiskers bright,
I nudged Issue into Project overnight.
Preferences packed and sort orders set,
Tokens trimmed to limits — no debt.
A rabbit cheers this tidy byte of light!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: renaming IssueUserProperty to ProjectUserProperty across the codebase and updating related references.
Description check ✅ Passed The description covers all required template sections with specific details: changes listed, type of change marked, test scenarios provided, and references linked to the ticket. All critical information is present.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore-user-property-migrations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/api/plane/db/models/api.py (1)

35-39: New allowed_rate_limit field is consistent with migration; consider minor cleanups

The allowed_rate_limit CharField with default "60/min" looks fine and matches the migration; this should be safe for existing tokens. You may also want to (optionally) add help_text clarifying the expected format and fix the existing typo "API Tokems" in verbose_name_plural while you’re in this model.

apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1)

1-38: Migration sequence and new fields look consistent with the model changes

The migration cleanly renames the IssueUserProperty table/model to ProjectUserProperty and adds the allowed_rate_limit, preferences, and sort_order fields in line with the model definitions. If you want extra long‑term safety, you could inline a simple dict default for preferences in this migration instead of importing get_default_preferences, but it’s not strictly required.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8db95d9 and 6a09e51.

📒 Files selected for processing (16)
  • apps/api/plane/api/views/project.py (3 hunks)
  • apps/api/plane/app/serializers/issue.py (2 hunks)
  • apps/api/plane/app/urls/issue.py (1 hunks)
  • apps/api/plane/app/views/issue/base.py (3 hunks)
  • apps/api/plane/app/views/project/base.py (3 hunks)
  • apps/api/plane/app/views/project/invite.py (2 hunks)
  • apps/api/plane/app/views/project/member.py (3 hunks)
  • apps/api/plane/bgtasks/workspace_seed_task.py (2 hunks)
  • apps/api/plane/db/management/commands/create_project_member.py (2 hunks)
  • apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1 hunks)
  • apps/api/plane/db/migrations/0114_auto_20251201_0857.py (1 hunks)
  • apps/api/plane/db/models/__init__.py (1 hunks)
  • apps/api/plane/db/models/api.py (1 hunks)
  • apps/api/plane/db/models/issue.py (0 hunks)
  • apps/api/plane/db/models/project.py (1 hunks)
  • apps/api/plane/tests/contract/app/test_project_app.py (3 hunks)
💤 Files with no reviewable changes (1)
  • apps/api/plane/db/models/issue.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/api/plane/app/views/project/member.py
  • apps/api/plane/app/serializers/issue.py
  • apps/api/plane/api/views/project.py
  • apps/api/plane/tests/contract/app/test_project_app.py
  • apps/api/plane/bgtasks/workspace_seed_task.py
📚 Learning: 2025-10-29T09:17:54.815Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7905
File: apps/api/plane/app/views/search/base.py:241-276
Timestamp: 2025-10-29T09:17:54.815Z
Learning: In apps/api/plane/app/views/search/base.py, the `filter_intakes` method uses `Issue.objects` (base manager) instead of `Issue.issue_objects` (custom manager) because the custom manager filters out all intake statuses, which would prevent querying pending and snoozed intake issues.

Applied to files:

  • apps/api/plane/api/views/project.py
  • apps/api/plane/app/views/issue/base.py
📚 Learning: 2025-09-12T07:29:36.083Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 7625
File: apps/api/plane/bgtasks/workspace_seed_task.py:95-98
Timestamp: 2025-09-12T07:29:36.083Z
Learning: In the Plane codebase, workspace seed data should have project features (cycle_view, module_view, issue_views_view) enabled by default, even when regular project creation has these features disabled. This provides users with a complete demo experience in the seeded workspace.

Applied to files:

  • apps/api/plane/bgtasks/workspace_seed_task.py
🧬 Code graph analysis (14)
apps/api/plane/app/views/project/member.py (1)
apps/api/plane/db/models/project.py (2)
  • Project (64-158)
  • ProjectUserProperty (317-348)
apps/api/plane/app/serializers/issue.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/db/models/__init__.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/db/management/commands/create_project_member.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/app/urls/issue.py (1)
apps/api/plane/app/views/issue/base.py (1)
  • IssueUserDisplayPropertyEndpoint (718-735)
apps/api/plane/api/views/project.py (2)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/app/views/project/base.py (3)
  • create (238-299)
  • create (501-508)
  • create (578-606)
apps/api/plane/db/migrations/0114_auto_20251201_0857.py (2)
apps/api/plane/db/models/project.py (2)
  • ProjectMember (191-235)
  • ProjectUserProperty (317-348)
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1)
  • Migration (8-38)
apps/api/plane/app/views/issue/base.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/tests/contract/app/test_project_app.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/bgtasks/workspace_seed_task.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/db/models/project.py (1)
apps/api/plane/db/models/issue.py (3)
  • get_default_filters (43-54)
  • get_default_display_filters (57-66)
  • get_default_display_properties (69-84)
apps/api/plane/app/views/project/base.py (2)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/app/views/project/member.py (1)
  • create (42-150)
apps/api/plane/app/views/project/invite.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (2)
apps/api/plane/db/migrations/0114_auto_20251201_0857.py (1)
  • Migration (31-42)
apps/api/plane/db/models/project.py (1)
  • get_default_preferences (60-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Agent
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Lint API
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (22)
apps/api/plane/app/urls/issue.py (1)

211-217: Comment rename correctly reflects underlying model

The updated ## ProjectUserProperty comments around the user-properties/ route align with the new model name and keep the URL behavior unchanged.

apps/api/plane/db/models/__init__.py (1)

48-56: Public model surface correctly switched to ProjectUserProperty

Exporting ProjectUserProperty from .project (with IssueUserProperty no longer present) correctly aligns the public API with the new model.

apps/api/plane/app/serializers/issue.py (2)

16-41: Model import updated correctly to ProjectUserProperty

Switching the import in plane.db.models from the old property model to ProjectUserProperty keeps this serializer file aligned with the new model layout.


349-353: IssueUserPropertySerializer now bound to ProjectUserProperty

Pointing the serializer’s Meta.model to ProjectUserProperty matches the refactor and stays compatible with existing views that use this serializer. The read_only_fields list still matches the new model’s base fields, so no further changes seem necessary.

apps/api/plane/app/views/project/base.py (1)

24-37: Project creation flow now correctly seeds ProjectUserProperty

Importing and creating ProjectUserProperty for the creator (and distinct project lead) keeps per-user project properties in sync with the new model and respects the unique (user, project) constraint. The conditional on project_lead still prevents duplicate records for the same user.

Also applies to: 245-266

apps/api/plane/db/management/commands/create_project_member.py (1)

6-12: Management command now seeds ProjectUserProperty instead of IssueUserProperty

Updating the import and using ProjectUserProperty.objects.get_or_create(user=user, project=project) keeps this command aligned with the new model while preserving idempotent behavior.

Also applies to: 69-71

apps/api/plane/tests/contract/app/test_project_app.py (1)

6-13: Tests correctly updated to assert ProjectUserProperty creation

Switching the import and expectations from IssueUserProperty to ProjectUserProperty matches the refactor: you now assert a property for the creator and two properties when a distinct project lead is set, which aligns with the updated create flow.

Also applies to: 80-87, 116-121

apps/api/plane/bgtasks/workspace_seed_task.py (2)

23-23: LGTM!

The import correctly references the renamed ProjectUserProperty model.


121-160: LGTM!

The bulk creation correctly uses ProjectUserProperty instead of IssueUserProperty. The new fields preferences and sort_order are not explicitly set, which is acceptable as they have sensible defaults defined in the model.

apps/api/plane/app/views/issue/base.py (3)

54-54: LGTM!

Import correctly updated to ProjectUserProperty.


720-729: LGTM!

The patch method correctly uses ProjectUserProperty.objects.get() and accesses fields that exist in the new model definition. Note that the serializer is still named IssueUserPropertySerializer, which appears to be intentional as it's not part of this refactor's scope.


732-735: LGTM!

The get method correctly uses ProjectUserProperty.objects.get_or_create() with appropriate fields matching the model's unique constraints.

apps/api/plane/app/views/project/member.py (3)

16-16: LGTM!

Import statement correctly includes ProjectUserProperty.


121-127: LGTM!

ProjectUserProperty instantiation correctly sets the required fields. The new fields preferences and sort_order will use their default values, consistent with the pattern used elsewhere in the codebase.


132-132: LGTM!

bulk_create correctly uses ProjectUserProperty with appropriate parameters including ignore_conflicts=True to handle the unique constraint on user and project.

apps/api/plane/api/views/project.py (3)

21-21: LGTM!

Import correctly updated to ProjectUserProperty.


220-220: LGTM!

ProjectUserProperty creation for the requesting user correctly sets project_id and user. The new fields will use their default values.


231-234: LGTM!

ProjectUserProperty creation for the project lead is correctly implemented with appropriate conditional logic. Using user_id as a keyword argument is valid.

apps/api/plane/db/migrations/0114_auto_20251201_0857.py (2)

5-28: LGTM!

The data migration correctly copies preferences and sort_order from ProjectMember to ProjectUserProperty for matching user-project pairs. The implementation is efficient using dictionary-based lookups and bulk updates with an appropriate batch size.


31-42: LGTM!

The migration structure is correct with appropriate dependency on the previous migration and a no-op reverse operation, which is standard for data migrations.

apps/api/plane/app/views/project/invite.py (2)

27-27: LGTM!

Import correctly updated to ProjectUserProperty.


163-174: LGTM!

The bulk creation of ProjectUserProperty is correctly implemented with appropriate fields and ignore_conflicts=True to handle the unique constraint on user and project.

@sriramveeraghanta
Copy link
Member

@pablohashescobar Update the ProjectUserProperty Serializers to support updating the additional properties.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the IssueUserProperty model to ProjectUserProperty, reflecting its broader scope beyond issue-specific properties. The refactoring includes adding preferences and sort_order fields migrated from ProjectMember, and also adds allowed_rate_limit to the APIToken model.

Key changes:

  • Renamed IssueUserProperty to ProjectUserProperty and moved from issue.py to project.py
  • Added preferences and sort_order fields to ProjectUserProperty, populated via data migration from ProjectMember
  • Updated all references across views, serializers, tests, management commands, and background tasks

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/api/plane/db/models/issue.py Removed IssueUserProperty model definition
apps/api/plane/db/models/project.py Added ProjectUserProperty model with additional fields
apps/api/plane/db/models/init.py Updated import from IssueUserProperty to ProjectUserProperty
apps/api/plane/db/models/api.py Added allowed_rate_limit field to APIToken
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py Migration to rename model, alter table, and add new fields
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py Data migration from ProjectMember to ProjectUserProperty
apps/api/plane/app/serializers/issue.py Updated IssueUserPropertySerializer to use ProjectUserProperty model
apps/api/plane/app/views/issue/base.py Updated references to use ProjectUserProperty
apps/api/plane/app/views/project/base.py Updated project creation to use ProjectUserProperty
apps/api/plane/app/views/project/member.py Updated member addition to use ProjectUserProperty
apps/api/plane/app/views/project/invite.py Updated invite acceptance to use ProjectUserProperty
apps/api/plane/api/views/project.py Updated API project creation to use ProjectUserProperty
apps/api/plane/bgtasks/workspace_seed_task.py Updated seed task to use ProjectUserProperty
apps/api/plane/db/management/commands/create_project_member.py Updated management command to use ProjectUserProperty
apps/api/plane/app/urls/issue.py Updated comments to reflect rename
apps/api/plane/tests/contract/app/test_project_app.py Updated tests to assert ProjectUserProperty creation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a09e51 and e43d9b6.

📒 Files selected for processing (3)
  • apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1 hunks)
  • apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (1 hunks)
  • apps/api/plane/db/models/project.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/db/models/project.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.
🧬 Code graph analysis (2)
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (2)
apps/api/plane/db/models/project.py (2)
  • ProjectMember (191-235)
  • ProjectUserProperty (317-348)
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1)
  • Migration (8-54)
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (2)
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (1)
  • Migration (31-39)
apps/api/plane/db/models/project.py (1)
  • get_default_preferences (60-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL analysis (python)
  • GitHub Check: Agent
  • GitHub Check: Cursor Bugbot

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on December 20

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

…ertyEndpoint to ProjectUserPropertySerializer and ProjectUserDisplayPropertyEndpoint, updating all related references
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f75d40f and d874227.

📒 Files selected for processing (5)
  • apps/api/plane/app/serializers/__init__.py (1 hunks)
  • apps/api/plane/app/serializers/issue.py (2 hunks)
  • apps/api/plane/app/urls/issue.py (2 hunks)
  • apps/api/plane/app/views/__init__.py (1 hunks)
  • apps/api/plane/app/views/issue/base.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/plane/app/serializers/issue.py
  • apps/api/plane/app/views/issue/base.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/api/plane/app/serializers/__init__.py
🧬 Code graph analysis (3)
apps/api/plane/app/serializers/__init__.py (1)
apps/api/plane/app/serializers/issue.py (1)
  • ProjectUserPropertySerializer (349-353)
apps/api/plane/app/views/__init__.py (1)
apps/api/plane/app/views/issue/base.py (1)
  • ProjectUserDisplayPropertyEndpoint (718-735)
apps/api/plane/app/urls/issue.py (1)
apps/api/plane/app/views/issue/base.py (1)
  • ProjectUserDisplayPropertyEndpoint (718-735)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
apps/api/plane/app/serializers/__init__.py (1)

55-55: LGTM!

The serializer rename correctly reflects the model rename from IssueUserProperty to ProjectUserProperty.

apps/api/plane/app/views/__init__.py (1)

118-118: LGTM!

The public export correctly reflects the endpoint rename from IssueUserDisplayPropertyEndpoint to ProjectUserDisplayPropertyEndpoint, maintaining consistency with the model rename.

…perties by creating new entries and improve response handling
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/api/plane/app/views/issue/base.py (1)

718-745: Endpoint now supports updating new fields; consider minor cleanups

The renamed ProjectUserDisplayPropertyEndpoint now uses ProjectUserPropertySerializer with partial=True, so preferences and sort_order (and other writable fields) can be updated via PATCH, which addresses the earlier review concern. You could optionally:

  • Replace the explicit try/except + create in patch with a single get_or_create call (to mirror get and reduce duplication).
  • Rename issue_property to project_user_property or user_property for clarity post‑refactor.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d874227 and 280c3ce.

📒 Files selected for processing (1)
  • apps/api/plane/app/views/issue/base.py (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/api/plane/app/views/issue/base.py
🧬 Code graph analysis (1)
apps/api/plane/app/views/issue/base.py (2)
apps/api/plane/app/serializers/issue.py (3)
  • ProjectUserPropertySerializer (349-353)
  • create (194-269)
  • create (580-583)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/api/plane/app/views/issue/base.py (1)

37-38: Imports correctly updated to ProjectUserProperty model/serializer

The imports for ProjectUserPropertySerializer and ProjectUserProperty are consistent with the refactor and are used appropriately in this module; no issues from this change set.

Also applies to: 54-59

…y excluding bot users from workspace removal
dheeru0198
dheeru0198 previously approved these changes Dec 8, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (1)

20-25: Add None checks before copying ProjectMember fields.

The concern raised in previous review remains unresolved: older ProjectMember records may have None for preferences and sort_order (migration 0110 did not backfill), which would overwrite the defaults already set in migration 0113. Add conditional checks to preserve existing defaults when source fields are None.

🧹 Nitpick comments (1)
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (1)

31-39: Cursor bot comment is a false positive; the migration logic is correct.

The previous concern about user__is_bot=False in an update() call is incorrect. Line 35 uses user__is_bot=False in the .filter() clause (which supports related lookups), while line 36 only updates workspace_id (a direct field on APIToken). This is valid Django ORM usage.

The migration correctly clears workspace associations for non-service, non-bot tokens as intended.

Minor formatting note: Line 37 has an unnecessary blank line, and line 39 has an unnecessary return statement, but these are purely stylistic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f0ea09 and ab9a6b4.

📒 Files selected for processing (1)
  • apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/api/plane/db/migrations/0114_alter_projectuserproperty_options_and_more.py (3)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (317-348)
apps/api/plane/db/models/api.py (1)
  • APIToken (19-44)
apps/api/plane/db/migrations/0113_alter_issueuserproperty_table.py (1)
  • Migration (8-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot

@dheeru0198 dheeru0198 added the 🔄migrations Contains Migration changes label Dec 10, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/api/plane/api/views/project.py (1)

18-30: Remove unused import ProjectUserProperty.

ProjectUserProperty is imported but never used in this file. The explicit creation calls were removed because ProjectMember.save() now automatically creates the associated ProjectUserProperty record.

🔎 Suggested fix
 from plane.db.models import (
     Cycle,
     Intake,
-    ProjectUserProperty,
     Module,
     Project,
     DeployBoard,
apps/api/plane/app/views/project/member.py (1)

57-59: Misleading variable name after refactoring.

The variable bulk_issue_props now holds ProjectUserProperty instances, not issue properties. Consider renaming it to align with the refactored model name.

🔎 Suggested fix
         # Initialize the bulk arrays
         bulk_project_members = []
-        bulk_issue_props = []
+        bulk_project_user_props = []

Also update references at lines 120 and 132:

-            bulk_issue_props.append(
+            bulk_project_user_props.append(
-        _ = ProjectUserProperty.objects.bulk_create(bulk_issue_props, batch_size=10, ignore_conflicts=True)
+        _ = ProjectUserProperty.objects.bulk_create(bulk_project_user_props, batch_size=10, ignore_conflicts=True)
apps/api/plane/db/models/project.py (1)

207-223: Consider transaction safety for ProjectUserProperty creation.

The ProjectUserProperty is created before super().save() is called. If the parent save fails (e.g., due to a unique constraint violation), the created ProjectUserProperty record becomes orphaned.

Consider wrapping the creation and save in an atomic transaction, or moving the creation after the successful save:

🔎 Option 1: Use atomic transaction
+from django.db import transaction
+
     def save(self, *args, **kwargs):
         if self._state.adding and self.member:
-            # Get the minimum sort_order for this member in the workspace
-            min_sort_order_result = ProjectUserProperty.objects.filter(
-                workspace_id=self.project.workspace_id, user=self.member
-            ).aggregate(min_sort_order=models.Min("sort_order"))
-            min_sort_order = min_sort_order_result.get("min_sort_order")
-
-            # create project user property with project sort order
-            ProjectUserProperty.objects.create(
-                workspace_id=self.project.workspace_id,
-                project=self.project,
-                user=self.member,
-                sort_order=(min_sort_order - 10000 if min_sort_order is not None else 65535),
-            )
-
-        super(ProjectMember, self).save(*args, **kwargs)
+            with transaction.atomic():
+                min_sort_order_result = ProjectUserProperty.objects.filter(
+                    workspace_id=self.project.workspace_id, user=self.member
+                ).aggregate(min_sort_order=models.Min("sort_order"))
+                min_sort_order = min_sort_order_result.get("min_sort_order")
+
+                ProjectUserProperty.objects.create(
+                    workspace_id=self.project.workspace_id,
+                    project=self.project,
+                    user=self.member,
+                    sort_order=(min_sort_order - 10000 if min_sort_order is not None else 65535),
+                )
+                super(ProjectMember, self).save(*args, **kwargs)
+        else:
+            super(ProjectMember, self).save(*args, **kwargs)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab9a6b4 and d75efb3.

📒 Files selected for processing (6)
  • apps/api/plane/api/views/project.py (1 hunks)
  • apps/api/plane/app/views/project/base.py (2 hunks)
  • apps/api/plane/app/views/project/invite.py (3 hunks)
  • apps/api/plane/app/views/project/member.py (3 hunks)
  • apps/api/plane/db/management/commands/create_project_member.py (2 hunks)
  • apps/api/plane/db/models/project.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/app/views/project/base.py
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.
📚 Learning: 2025-07-23T18:18:06.875Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7460
File: apps/api/plane/app/serializers/draft.py:112-122
Timestamp: 2025-07-23T18:18:06.875Z
Learning: In the Plane codebase serializers, workspace_id is not consistently passed in serializer context, so parent issue validation in DraftIssueCreateSerializer only checks project_id rather than both workspace_id and project_id. The existing project member authentication system already validates that users can only access projects they belong to, providing sufficient security without risking breaking functionality by adding workspace_id validation where the context might not be available.

Applied to files:

  • apps/api/plane/app/views/project/invite.py
  • apps/api/plane/app/views/project/member.py
  • apps/api/plane/api/views/project.py
📚 Learning: 2025-10-29T09:17:54.815Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7905
File: apps/api/plane/app/views/search/base.py:241-276
Timestamp: 2025-10-29T09:17:54.815Z
Learning: In apps/api/plane/app/views/search/base.py, the `filter_intakes` method uses `Issue.objects` (base manager) instead of `Issue.issue_objects` (custom manager) because the custom manager filters out all intake statuses, which would prevent querying pending and snoozed intake issues.

Applied to files:

  • apps/api/plane/api/views/project.py
🧬 Code graph analysis (4)
apps/api/plane/db/management/commands/create_project_member.py (2)
apps/api/plane/db/models/project.py (2)
  • ProjectUserProperty (323-354)
  • ProjectMember (191-241)
apps/api/plane/app/views/project/member.py (1)
  • create (43-150)
apps/api/plane/db/models/project.py (1)
apps/api/plane/app/views/project/member.py (1)
  • create (43-150)
apps/api/plane/app/views/project/invite.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (323-354)
apps/api/plane/api/views/project.py (1)
apps/api/plane/db/models/project.py (1)
  • ProjectUserProperty (323-354)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (python)
🔇 Additional comments (5)
apps/api/plane/db/management/commands/create_project_member.py (1)

51-61: LGTM - Correct handling for both update and create paths.

The logic correctly handles both scenarios:

  • Update path (lines 53-55): Updates existing member, then get_or_create ensures ProjectUserProperty exists
  • Create path (line 58): ProjectMember.save() auto-creates ProjectUserProperty, and get_or_create safely retrieves the existing record
apps/api/plane/app/views/project/member.py (1)

93-127: LGTM - Sort order computation is correct.

The min_sort_order lookup from ProjectUserProperty and the calculation min_sort_order - 10000 is consistent with the pattern in ProjectMember.save(). Using bulk_create with ignore_conflicts=True safely handles cases where records already exist.

apps/api/plane/db/models/project.py (1)

323-354: LGTM - Model definition is correct after previous review fixes.

The ProjectUserProperty model correctly defines:

  • Proper verbose_name / verbose_name_plural as "Project User Property"
  • Correct related_name="project_property_user"
  • Appropriate constraint naming

The in-class import on line 324 is an unconventional but valid pattern to avoid circular imports between project.py and issue.py.

apps/api/plane/app/views/project/invite.py (2)

163-174: LGTM - Explicit ProjectUserProperty creation is necessary for bulk_create.

Since bulk_create bypasses the model's save() method, the explicit ProjectUserProperty creation here is correct. The ignore_conflicts=True safely handles any duplicates.


220-230: LGTM - ProjectUserProperty is auto-created via save().

Using ProjectMember.objects.create() invokes the save() method, which automatically creates the corresponding ProjectUserProperty record. The change from workspace_id to project_id is correct since ProjectBaseModel.save() derives the workspace from the project.

)
# Also create the issue property for the user
_ = IssueUserProperty.objects.create(project_id=serializer.data["id"], user=request.user)
_ = ProjectUserProperty.objects.create(project_id=serializer.data["id"], user=request.user)
Copy link

Choose a reason for hiding this comment

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

Bug: Duplicate ProjectUserProperty creation causes IntegrityError

The ProjectMember.save() method now automatically creates a ProjectUserProperty when a new member is added. However, line 259 still explicitly calls ProjectUserProperty.objects.create() after ProjectMember.objects.create(). This results in attempting to create two ProjectUserProperty records for the same user and project, which violates the unique constraint and will raise an IntegrityError. The explicit creation at line 259 needs to be removed, similar to how it was correctly removed in apps/api/plane/api/views/project.py.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔄migrations Contains Migration changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants