Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api for post upload #888

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

api for post upload #888

wants to merge 5 commits into from

Conversation

krmax44
Copy link
Member

@krmax44 krmax44 commented Nov 11, 2024

notable changes:

ts client example:

import { retrieveFoiMessage } from './api/index.ts'

retrieveFoiMessage({
  path: { id: '22699' }
}).then((response) => {
  console.log(response.data?.kind) // "email" | "post" | "fax" | "upload" | "phone" | "visit" | "import" | undefined
})

@krmax44 krmax44 force-pushed the krmax44/post-upload-api branch 5 times, most recently from a4aa599 to d7df699 Compare November 11, 2024 16:23
@krmax44 krmax44 force-pushed the krmax44/post-upload-api branch 3 times, most recently from 7e86c43 to c1424fe Compare November 18, 2024 14:58
@krmax44 krmax44 force-pushed the krmax44/post-upload-api branch 2 times, most recently from bd9e35e to d6e3dc4 Compare November 18, 2024 15:47
@krmax44 krmax44 marked this pull request as ready for review November 18, 2024 16:03
@krmax44 krmax44 requested review from pajowu, stefanw and bikubi and removed request for pajowu November 18, 2024 16:03
qs = super(FoiMessageAdmin, self).get_queryset(request)
qs = qs.prefetch_related("deliverystatus")
return qs
return self.model.objects.with_drafts(False).prefetch_related("deliverystatus")
Copy link
Member

Choose a reason for hiding this comment

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

The super().get_queryset(request) should be kept and extended with the filter, right? There's more happening in Django ModelAdmin's get_queryset, like ordering. .width_drafts(False) is fancy but only works on the manager and not the queryset which makes it difficult to compose.

@admin.register(FoiMessageDraft)
class FoiMessageDraftAdmin(FoiMessageAdmin):
def get_queryset(self, request):
return self.model.objects.all().prefetch_related("deliverystatus")
Copy link
Member

Choose a reason for hiding this comment

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

Should also call super and possibly just show drafts?

from ..auth import can_write_foirequest


class OwnsFoiRequestPermission(permissions.BasePermission):
Copy link
Member

Choose a reason for hiding this comment

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

Maybe rename to WriteFoiRequestPermission? The concept of owning is not clearly defined in the code base apart from request.user == object.user which is not what is checked here.

if request.method in permissions.SAFE_METHODS:
return True

return can_write_foirequest(getattr(obj, self.request_field), request)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this can work with belongs_to__request used in the FoiAttachmentViewSet as this is just attribute access.

# the other kinds can only be created by the system
MESSAGE_KIND_USER_ALLOWED = [
MessageKind.POST,
MessageKind.PHONE,
Copy link
Member

Choose a reason for hiding this comment

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

We are not using PHONE or VISIT. Should we disallow their creation? Otherwise we might get random visit messages in the database.

raise serializers.ValidationError("Only draft messages can be altered.")

def create(self, validated_data):
return super().create(validated_data)
Copy link
Member

Choose a reason for hiding this comment

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

I think we should create a FoiEvent for auditing purposes when the message is created and/or finalized, either directly or by sending a signal. (example)

def get_queryset(self):
return super().get_queryset().filter(is_draft=False)
return self.with_drafts()
Copy link
Member

Choose a reason for hiding this comment

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

Not calling super here is just a workaround because the method is only defined on the manager and doesn't exist on the queryset. Looks great as an API, but the problem I see here: we are using inheritance but then not calling super. A subtle bug may arise if FoiMessageManager get its own get_queryset method which is then not called.

Alternatives:

  • define a custom queryset instead which has the with_drafts method. You can create a matching manager by using objects = FoiMessageQueryset.as_manager().
  • Define a function with_drafts(qs: Queryset) -> Queryset and return with_drafts(super().get_queryset())

@@ -201,6 +212,9 @@ def save(self, *args, **kwargs):

super().save(*args, **kwargs)

def is_public(self) -> bool:
return self.is_draft is False
Copy link
Member

Choose a reason for hiding this comment

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

This works and is likely going to continue to work but it makes me uneasy as the intention here does not match the operator: comparing identity instead of checking truthiness. If you don't care about identity, don't compare it. It can be a subtle source of bugs because it can be an implementation detail. Favorite example:

>>> x = 3
>>> y = 3
>>> x is y
True
>>> x = 123123123123123
>>> y = 123123123123123
>>> x == y
True
>>> x is y
False

What we want is return not self.is_draft.

requests = [int(r) for r in requests]
except ValueError:
except (ValueError, AttributeError):
Copy link
Member

Choose a reason for hiding this comment

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

This should probably check self.request is None explicitly and return a value. Otherwise we might hide request.query_params no longer being an attribute or any other AttributeErrors. Keeping try/except small and focused is a good idea.

try:
if self.request.user.is_superuser:
return GeoRegionDetailSerializer
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
Copy link
Member

Choose a reason for hiding this comment

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

Same here, should handle self.request is None explicitly and bail with a value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants