Search code examples
ruby-on-railsrubyrails-activestorage

Deleting a single ActiveStorage attachment out of a collection via an API call


EDIT: TLDR: This boils down to serializing the attachments. See my response.

I can see two ways to achieve this:

(1) Serialize the attachments (with id and url attributes), thus providing an id to the FE that they can use to DELETE /attachments/:id which would then call ActiveStorage::Attachment.find(:id).purge. The problem is with serializing as attachments do not have built-in models. I tried creating an ActiveStorageAttachment model for the active_storage_attachments table but could not get the url for the attachment as the Rails.application.routes.url_helpers.url_for(@object) requires an ActiveStorage::Attachment object not an ActiveStorageAttachment object.

(2) Another option would be to have a DELETE /attachments/:attachment_url endpoint. For this to work, I'd need to get the ActiveStorage::Attachment object based on the url, in order to call purge on it. Not sure if that is possible?

I'd much prefer the first solution, it feels cleaner and more adaptable. Any help with either approach would be much appreciated!


Solution

  • In the end, I managed to serialize the attached images (option(1) above) using the jsonapi-rb gem.

    In the controller, I include: :images and pass in the attachment type using the expose option:

    class PostsController < ApplicationController
    ...
      def show
        respond_to do |format|
          format.html
          format.json { render jsonapi: @post, include: :images, expose: {attachment_type: "images"} }
        end
      end
    end
    

    ActiveSupport gives you the post.images_attachments method for free:

    class SerializablePost < JSONAPI::Serializable::Resource
      type 'posts'
      ...
      has_many :images do
        @object.images_attachments
      end
    end
    

    I created an attachment serializer:

    class SerializableAttachment < JSONAPI::Serializable::Resource
      include Rails.application.routes.url_helpers
    
      type do
        @attachment_type
      end
      attribute :id
      attribute :url do 
        url_for(@object)
      end
    end
    

    I needed to tell jsonapi to use this serializer for all attachments:

    class ApplicationController < ActionController::Base
    ...
      def jsonapi_class
        super.merge(
          'ActiveStorage::Attachment': SerializableAttachment
        )
      end
    end
    

    Now I can implement DELETE /attachments/:id.