Skip to content

Commit

Permalink
fix: private bucket (#5812)
Browse files Browse the repository at this point in the history
* fix: workspace level issue creation

* dev: add draft issue support, fix your work tab and cache invalidation for workspace level logos

* chore: issue description

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
  • Loading branch information
pablohashescobar and aaryan610 authored Oct 12, 2024
1 parent e404450 commit 0ac406e
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 20 deletions.
150 changes: 135 additions & 15 deletions apiserver/plane/app/views/asset/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from plane.settings.storage import S3Storage
from plane.app.permissions import allow_permission, ROLE
from plane.utils.cache import invalidate_cache_directly


class UserAssetsV2Endpoint(BaseAPIView):
Expand All @@ -35,7 +36,7 @@ def asset_delete(self, asset_id):
asset.save()
return

def entity_asset_save(self, asset_id, entity_type, asset):
def entity_asset_save(self, asset_id, entity_type, asset, request):
# User Avatar
if entity_type == FileAsset.EntityTypeContext.USER_AVATAR:
user = User.objects.get(id=asset.user_id)
Expand All @@ -46,6 +47,18 @@ def entity_asset_save(self, asset_id, entity_type, asset):
# Save the new avatar
user.avatar_asset_id = asset_id
user.save()
invalidate_cache_directly(
path="/api/users/me/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/settings/",
url_params=False,
user=True,
request=request,
)
return
# User Cover
if entity_type == FileAsset.EntityTypeContext.USER_COVER:
Expand All @@ -57,21 +70,57 @@ def entity_asset_save(self, asset_id, entity_type, asset):
# Save the new cover image
user.cover_image_asset_id = asset_id
user.save()
invalidate_cache_directly(
path="/api/users/me/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/settings/",
url_params=False,
user=True,
request=request,
)
return
return

def entity_asset_delete(self, entity_type, asset):
def entity_asset_delete(self, entity_type, asset, request):
# User Avatar
if entity_type == FileAsset.EntityTypeContext.USER_AVATAR:
user = User.objects.get(id=asset.user_id)
user.avatar_asset_id = None
user.save()
invalidate_cache_directly(
path="/api/users/me/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/settings/",
url_params=False,
user=True,
request=request,
)
return
# User Cover
if entity_type == FileAsset.EntityTypeContext.USER_COVER:
user = User.objects.get(id=asset.user_id)
user.cover_image_asset_id = None
user.save()
invalidate_cache_directly(
path="/api/users/me/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/settings/",
url_params=False,
user=True,
request=request,
)
return
return

Expand All @@ -82,6 +131,9 @@ def post(self, request):
size = int(request.data.get("size", settings.FILE_SIZE_LIMIT))
entity_type = request.data.get("entity_type", False)

# Check if the file size is within the limit
size_limit = min(size, settings.FILE_SIZE_LIMIT)

# Check if the entity type is allowed
if not entity_type or entity_type not in ["USER_AVATAR", "USER_COVER"]:
return Response(
Expand All @@ -103,9 +155,6 @@ def post(self, request):
status=status.HTTP_400_BAD_REQUEST,
)

# Get the size limit
size_limit = min(settings.FILE_SIZE_LIMIT, size)

# asset key
asset_key = f"{uuid.uuid4().hex}-{name}"

Expand Down Expand Up @@ -153,7 +202,12 @@ def patch(self, request, asset_id):
object_name=asset.asset.name
)
# get the entity and save the asset id for the request field
self.entity_asset_save(asset_id, asset.entity_type, asset)
self.entity_asset_save(
asset_id=asset_id,
entity_type=asset.entity_type,
asset=asset,
request=request,
)
# update the attributes
asset.attributes = request.data.get("attributes", asset.attributes)
# save the asset
Expand All @@ -165,7 +219,9 @@ def delete(self, request, asset_id):
asset.is_deleted = True
asset.deleted_at = timezone.now()
# get the entity and save the asset id for the request field
self.entity_asset_delete(asset.entity_type, asset)
self.entity_asset_delete(
entity_type=asset.entity_type, asset=asset, request=request
)
asset.save()
return Response(status=status.HTTP_204_NO_CONTENT)

Expand All @@ -174,16 +230,19 @@ class WorkspaceFileAssetEndpoint(BaseAPIView):
"""This endpoint is used to upload cover images/logos etc for workspace, projects and users."""

def get_entity_id_field(self, entity_type, entity_id):
# Workspace Logo
if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO:
return {
"workspace_id": entity_id,
}

# Project Cover
if entity_type == FileAsset.EntityTypeContext.PROJECT_COVER:
return {
"project_id": entity_id,
}

# User Avatar and Cover
if entity_type in [
FileAsset.EntityTypeContext.USER_AVATAR,
FileAsset.EntityTypeContext.USER_COVER,
Expand All @@ -192,6 +251,7 @@ def get_entity_id_field(self, entity_type, entity_id):
"user_id": entity_id,
}

# Issue Attachment and Description
if entity_type in [
FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
FileAsset.EntityTypeContext.ISSUE_DESCRIPTION,
Expand All @@ -200,11 +260,13 @@ def get_entity_id_field(self, entity_type, entity_id):
"issue_id": entity_id,
}

# Page Description
if entity_type == FileAsset.EntityTypeContext.PAGE_DESCRIPTION:
return {
"page_id": entity_id,
}

# Comment Description
if entity_type == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION:
return {
"comment_id": entity_id,
Expand All @@ -222,7 +284,7 @@ def asset_delete(self, asset_id):
asset.save()
return

def entity_asset_save(self, asset_id, entity_type, asset):
def entity_asset_save(self, asset_id, entity_type, asset, request):
# Workspace Logo
if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO:
workspace = Workspace.objects.filter(id=asset.workspace_id).first()
Expand All @@ -235,6 +297,24 @@ def entity_asset_save(self, asset_id, entity_type, asset):
workspace.logo = ""
workspace.logo_asset_id = asset_id
workspace.save()
invalidate_cache_directly(
path="/api/workspaces/",
url_params=False,
user=False,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/workspaces/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/instances/",
url_params=False,
user=False,
request=request,
)
return

# Project Cover
Expand All @@ -253,14 +333,32 @@ def entity_asset_save(self, asset_id, entity_type, asset):
else:
return

def entity_asset_delete(self, entity_type, asset):
def entity_asset_delete(self, entity_type, asset, request):
# Workspace Logo
if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO:
workspace = Workspace.objects.get(id=asset.workspace_id)
if workspace is None:
return
workspace.logo_asset_id = None
workspace.save()
invalidate_cache_directly(
path="/api/workspaces/",
url_params=False,
user=False,
request=request,
)
invalidate_cache_directly(
path="/api/users/me/workspaces/",
url_params=False,
user=True,
request=request,
)
invalidate_cache_directly(
path="/api/instances/",
url_params=False,
user=False,
request=request,
)
return
# Project Cover
elif entity_type == FileAsset.EntityTypeContext.PROJECT_COVER:
Expand Down Expand Up @@ -322,7 +420,9 @@ def post(self, request, slug):
workspace=workspace,
created_by=request.user,
entity_type=entity_type,
**self.get_entity_id_field(entity_type, entity_identifier),
**self.get_entity_id_field(
entity_type=entity_type, entity_id=entity_identifier
),
)

# Get the presigned URL
Expand Down Expand Up @@ -355,7 +455,12 @@ def patch(self, request, slug, asset_id):
object_name=asset.asset.name
)
# get the entity and save the asset id for the request field
self.entity_asset_save(asset_id, asset.entity_type, asset)
self.entity_asset_save(
asset_id=asset_id,
entity_type=asset.entity_type,
asset=asset,
request=request,
)
# update the attributes
asset.attributes = request.data.get("attributes", asset.attributes)
# save the asset
Expand All @@ -367,7 +472,9 @@ def delete(self, request, slug, asset_id):
asset.is_deleted = True
asset.deleted_at = timezone.now()
# get the entity and save the asset id for the request field
self.entity_asset_delete(asset.entity_type, asset)
self.entity_asset_delete(
entity_type=asset.entity_type, asset=asset, request=request
)
asset.save()
return Response(status=status.HTTP_204_NO_CONTENT)

Expand Down Expand Up @@ -454,7 +561,7 @@ def post(self, request, slug, asset_id):
class ProjectAssetEndpoint(BaseAPIView):
"""This endpoint is used to upload cover images/logos etc for workspace, projects and users."""

def get_entity_id_fiekd(self, entity_type, entity_id):
def get_entity_id_field(self, entity_type, entity_id):
if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO:
return {
"workspace_id": entity_id,
Expand Down Expand Up @@ -490,6 +597,11 @@ def get_entity_id_fiekd(self, entity_type, entity_id):
return {
"comment_id": entity_id,
}

if entity_type == FileAsset.EntityTypeContext.DRAFT_ISSUE_DESCRIPTION:
return {
"draft_issue_id": entity_id,
}
return {}

@allow_permission(
Expand All @@ -513,7 +625,7 @@ def post(self, request, slug, project_id):
)

# Check if the file type is allowed
allowed_types = ["image/jpeg", "image/png", "image/webp"]
allowed_types = ["image/jpeg", "image/png", "image/webp", "image/jpg"]
if type not in allowed_types:
return Response(
{
Expand Down Expand Up @@ -545,7 +657,7 @@ def post(self, request, slug, project_id):
created_by=request.user,
entity_type=entity_type,
project_id=project_id,
**self.get_entity_id_fiekd(entity_type, entity_identifier),
**self.get_entity_id_field(entity_type, entity_identifier),
)

# Get the presigned URL
Expand Down Expand Up @@ -688,4 +800,12 @@ def post(self, request, slug, project_id, entity_id):
page_id=entity_id,
)

if (
asset.entity_type
== FileAsset.EntityTypeContext.DRAFT_ISSUE_DESCRIPTION
):
assets.update(
draft_issue_id=entity_id,
)

return Response(status=status.HTTP_204_NO_CONTENT)
2 changes: 1 addition & 1 deletion apiserver/plane/app/views/workspace/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def get(self, request, slug, user_id):
)
.annotate(
attachment_count=FileAsset.objects.filter(
entity_identifier=OuterRef("id"),
issue_id=OuterRef("id"),
entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
)
.order_by()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 4.2.15 on 2024-10-12 18:45

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("db", "0079_auto_20241009_0619"),
]

operations = [
migrations.AddField(
model_name="fileasset",
name="draft_issue",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="assets",
to="db.draftissue",
),
),
migrations.AlterField(
model_name="fileasset",
name="entity_type",
field=models.CharField(
blank=True,
choices=[
("ISSUE_ATTACHMENT", "Issue Attachment"),
("ISSUE_DESCRIPTION", "Issue Description"),
("COMMENT_DESCRIPTION", "Comment Description"),
("PAGE_DESCRIPTION", "Page Description"),
("USER_COVER", "User Cover"),
("USER_AVATAR", "User Avatar"),
("WORKSPACE_LOGO", "Workspace Logo"),
("PROJECT_COVER", "Project Cover"),
("DRAFT_ISSUE_ATTACHMENT", "Draft Issue Attachment"),
("DRAFT_ISSUE_DESCRIPTION", "Draft Issue Description"),
],
max_length=255,
null=True,
),
),
]
Loading

0 comments on commit 0ac406e

Please sign in to comment.