Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ActiveStorage, Ruby on Rails 7, GraphQL and RSpec
# app/graphql/mutations/create_document.rb
module Mutations
class CreateDocument < Mutations::BaseMutation
description "Create a document"
argument :doc, ApolloUploadServer::Upload, required: true, description: "The document to upload"
field :document, Types::Objects::DocumentType, null: false
field :code, Types::Enums::CodeEnum, null: false
def resolve(doc:)
authenticate_user
# First you need to create blob file in case of active storage. https://stackoverflow.com/q/70550808/4011757
attachment = ActiveStorage::Blob::create_and_upload!(io: doc, filename: doc.original_filename, content_type: doc.content_type)
document = context[:current_user].documents.build(doc: attachment)
if document.save
{document: document, code: "SUCCESS"}
else
raise GraphQL::ExecutionError.new(document.errors.full_messages.join(", "), extensions: {code: "UNPROCESSABLE_ENTITY", errors: document.errors})
end
end
end
end
# spec/graphql/mutations/create_document_spec.rb
require 'rails_helper'
module Mutations
RSpec.describe CreateDocument, type: :request do
describe '.resolve' do
let(:session) {FactoryBot.create(:session)}
it 'create a document for the user' do
session.user.confirm
headers = sign_in_test_headers session
params = FactoryBot.attributes_for(:document, :with_image, user_id: session.user_id)
query = <<-GRAPHQL
mutation ($input: CreateDocumentInput!) {
createDocument(input: $input) {
document {
id
url
}
}
}
GRAPHQL
variables = {input: {doc: ::ApolloUploadServer::Wrappers::UploadedFile.new(ActionDispatch::Http::UploadedFile.new(filename: "image.png", type: "image/png", tempfile: File.new("spec/support/fixtures/image.png")))}}
response = ApiSchema.execute(query, variables: variables, context: {current_user: session.user})
expect(response.to_h["data"]["createDocument"]["document"]["url"]).not_to be_nil
end
end
end
end
# app/models/document.rb
class Document < ApplicationRecord
## RELATIONSHIPS
has_one_attached :doc
belongs_to :documentable, polymorphic: true, optional: true
belongs_to :user, optional: true
## VALIDATIONS
validate :doc_presence, on: :create
def doc_presence
pattern = %r{^(image|application|text)/(.)+$}
unless doc.attached? && pattern.match?(doc.attachment.blob.content_type)
errors.add(:doc, I18n.t("errors.models.document.file_presence"))
end
end
end
# spec/models/document_spec.rb
require 'rails_helper'
RSpec.describe Document, type: :model do
it 'has a valid factory' do
document = FactoryBot.build(:document)
expect(document.valid?).to be_falsey
document = FactoryBot.build(:document, :with_image)
expect(document.valid?).to be_truthy
expect(document.doc.attached?).to be_truthy
document.save!
expect(document.doc.attached?).to be_truthy
document = FactoryBot.build(:document, :with_video)
expect(document.valid?).to be_truthy
expect(document.doc.attached?).to be_truthy
document.save!
expect(document.doc.attached?).to be_truthy
document = FactoryBot.build(:document, :with_pdf)
expect(document.valid?).to be_truthy
document = FactoryBot.build(:document, :with_csv)
expect(document.valid?).to be_truthy
end
it 'must have user' do
document = FactoryBot.build(:document, user: nil)
expect(document.valid?).to be_falsey
end
it 'must contain file' do
document = FactoryBot.build(:document, doc: nil)
expect(document.valid?).to be_falsey
end
end
# app/graphql/types/objects/document_type.rb
module Types
module Objects
class DocumentType < Types::BaseObject
field :id, Integer, null: false
field :documentable_type, String, null: true
field :documentable_id, Integer, null: true
field :content_type, String, null: true
field :url, String, null: false
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
def url
(Rails.env.development? || Rails.env.test?) ? Rails.application.routes.url_helpers.rails_blob_path(object.doc) : object.doc.service_url
end
def content_type
object.doc.present? ? object.doc.blob.content_type : nil
end
end
end
end
# spec/factories/documents.rb
FactoryBot.define do
factory :document do
association :user
trait :with_image do
after :build do |document|
file_name = 'image.png'
file_path = Rails.root.join('spec', 'support', 'fixtures', file_name)
document.doc.attach(io: File.open(file_path), filename: file_name, content_type: 'image/png')
end
end
trait :with_video do
after :build do |document|
file_name = 'video.mp4'
file_path = Rails.root.join('spec', 'support', 'fixtures', file_name)
document.doc.attach(io: File.open(file_path), filename: file_name, content_type: 'video/mp4')
end
end
trait :with_pdf do
after :build do |document|
file_name = 'pdf.pdf'
file_path = Rails.root.join('spec', 'support', 'fixtures', file_name)
document.doc.attach(io: File.open(file_path), filename: file_name, content_type: 'application/pdf')
end
end
trait :with_csv do
after :build do |document|
file_name = 'import_csv.csv'
file_path = Rails.root.join('spec', 'support', 'fixtures', file_name)
document.doc.attach(io: File.open(file_path), filename: file_name, content_type: 'text/csv')
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment