add normal/API endpoints for attachments

This commit is contained in:
Devin Howard 2017-11-04 10:23:11 -07:00
parent 955ebdd747
commit f2d909e2d7
19 changed files with 268 additions and 4 deletions

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Api
module V2
class AttachmentsController < RestfulController
def searchable_columns
[:file]
end
end
end
end

View file

@ -0,0 +1,36 @@
# frozen_string_literal: true
class AttachmentsController < ApplicationController
before_action :set_attachment, only: [:destroy]
after_action :verify_authorized
def create
@attachment = Attachment.new(attachment_params)
authorize @attachment
respond_to do |format|
if @attachment.save
format.json { render json: @attachment, status: :created }
else
format.json { render json: @attachment.errors, status: :unprocessable_entity }
end
end
end
def destroy
@attachment.destroy
respond_to do |format|
format.json { head :no_content }
end
end
private
def set_attachment
@attachment = Attachment.find(params[:id])
authorize @attachment
end
def attachment_params
params.require(:attachment).permit(:id, :file, :attachable_id, :attachable_type)
end
end

View file

@ -15,7 +15,9 @@ class Attachment < ApplicationRecord
end
}
validates :attachable, presence: true
validates_attachment_content_type :file, content_type: Attachable.allowed_types
validates_attachment_size :file, in: 0.megabytes..5.megabytes
def image?
Attachable.image_types.include?(file.instance.file_content_type)

View file

@ -33,7 +33,8 @@ module Attachable
end
def audio_types
['audio/ogg', 'audio/mp3']
# .ogg files might be labelled as video
['audio/ogg', 'video/ogg', 'audio/mpeg', 'audio/wav', 'video/webm']
end
def text_types

View file

@ -69,10 +69,23 @@ class Topic < ApplicationRecord
Pundit.policy_scope(user, maps).map(&:id)
end
def attachments_json
attachments.map do |a|
{
id: a.id,
file_name: a.file_file_name,
content_type: a.file_content_type,
file_size: a.file_file_size,
url: a.file.url
}
end
end
def as_json(options = {})
super(methods: %i[user_name user_image collaborator_ids])
.merge(inmaps: inmaps(options[:user]), inmapsLinks: inmaps_links(options[:user]),
map_count: map_count(options[:user]), synapse_count: synapse_count(options[:user]))
map_count: map_count(options[:user]), synapse_count: synapse_count(options[:user]),
attachments: attachments_json)
end
def as_rdf

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AttachmentPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(attachable: TopicPolicy::Scope.new(user, Topic).resolve)
end
end
def index?
true
end
def create?
Pundit.policy(user, record.attachable).update?
end
def destroy?
Pundit.policy(user, record.attachable).update?
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Api
module V2
class AttachmentSerializer < ApplicationSerializer
attributes :id,
:file,
:attachable_type,
:attachable_id,
:created_at,
:updated_at
end
end
end

View file

@ -14,7 +14,8 @@ module Api
def self.embeddable
{
user: {},
metacode: {}
metacode: {},
attachments: {}
}
end

View file

@ -7,6 +7,8 @@ Metamaps::Application.routes.draw do
root to: 'main#home', via: :get
get 'request', to: 'main#requestinvite', as: :request
resources :attachments, only: [:create, :destroy], shallow: true
namespace :explore do
get 'active'
get 'featured'
@ -121,6 +123,7 @@ Metamaps::Application.routes.draw do
namespace :api, path: '/api', default: { format: :json } do
namespace :v2, path: '/v2' do
resources :attachments, only: %i[index show]
resources :metacodes, only: %i[index show]
resources :mappings, only: %i[index create show update destroy]
resources :maps, only: %i[index create show update destroy] do

View file

@ -22,6 +22,7 @@ traits:
searchable: !include traits/searchable.raml
schemas:
attachment: !include schemas/_attachment.json
map: !include schemas/_map.json
mapping: !include schemas/_mapping.json
metacode: !include schemas/_metacode.json
@ -35,6 +36,7 @@ schemas:
# item: !include resourceTypes/item.raml
# collection: !include resourceTypes/collection.raml
/attachments : !include api/attachments.raml
/maps: !include apis/maps.raml
/mappings: !include apis/mappings.raml
/metacodes: !include apis/metacodes.raml

View file

@ -0,0 +1,16 @@
get:
is: [ searchable: { searchFields: "file" }, orderable, pageable ]
securedBy: [ null, token, oauth_2_0 ]
responses:
200:
body:
application/json:
example: !include ../examples/attachments.json
/{id}:
get:
securedBy: [ null, token, oauth_2_0 ]
responses:
200:
body:
application/json:
example: !include ../examples/attachment.json

View file

@ -0,0 +1,10 @@
{
"data": {
"id": 1,
"file": "https://example.org/file.png",
"attachable_type": "Topic",
"attachable_id": 187,
"created_at": "2017-03-01T05:48:09.533Z",
"updated_at": "2017-03-01T05:48:09.533Z"
}
}

View file

@ -0,0 +1,28 @@
{
"data": [
{
"id": 1,
"file": "https://example.org/file.png",
"attachable_type": "Topic",
"attachable_id": 187,
"created_at": "2017-03-01T05:48:09.533Z",
"updated_at": "2017-03-01T05:48:09.533Z"
},
{
"id": 2,
"file": "https://example.org/file.docx",
"attachable_type": "Message",
"attachable_id": 1043,
"created_at": "2017-03-01T05:50:19.533Z",
"updated_at": "2017-03-01T05:50:19.533Z"
}
],
"page": {
"current_page": 1,
"next_page": 2,
"prev_page": 0,
"total_pages": 156,
"total_count": 312,
"per": 2
}
}

View file

@ -0,0 +1,34 @@
{
"name": "Attachment",
"type": "object",
"properties": {
"id": {
"$ref": "_id.json"
},
"file": {
"format": "uri",
"type": "string"
},
"attachable_type": {
"pattern": "(Topic|Message)",
"type": "string"
},
"attachable_id": {
"$ref": "_id.json"
},
"created_at": {
"$ref": "_datetimestamp.json"
},
"updated_at": {
"$ref": "_datetimestamp.json"
}
},
"required": [
"id",
"file",
"attachable_type",
"attachable_id",
"created_at",
"updated_at"
]
}

View file

@ -0,0 +1,12 @@
{
"name": "Attachment Envelope",
"type": "object",
"properties": {
"data": {
"$ref": "_attachment.json"
}
},
"required": [
"data"
]
}

View file

@ -0,0 +1,19 @@
{
"name": "Attachments",
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "_attachment.json"
}
},
"page": {
"$ref": "_page.json"
}
},
"required": [
"data",
"page"
]
}

View file

@ -0,0 +1,38 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'topics API', type: :request do
let(:user) { create(:user, admin: true) }
let(:token) { create(:token, user: user).token }
let(:attachment) { create(:attachment) }
it 'GET /api/v2/attachments' do
create_list(:attachment, 5)
get '/api/v2/attachments', params: { access_token: token }
expect(response).to have_http_status(:success)
expect(response).to match_json_schema(:attachments)
expect(JSON.parse(response.body)['data'].count).to eq 5
end
it 'GET /api/v2/attachments/:id' do
get "/api/v2/attachments/#{attachment.id}"
expect(response).to have_http_status(:success)
expect(response).to match_json_schema(:attachment)
expect(JSON.parse(response.body)['data']['id']).to eq attachment.id
end
context 'RAML example' do
let(:resource) { get_json_example(:attachment) }
let(:collection) { get_json_example(:attachments) }
it 'resource matches schema' do
expect(resource).to match_json_schema(:attachment)
end
it 'collection matches schema' do
expect(collection).to match_json_schema(:attachments)
end
end
end

View file

@ -0,0 +1,6 @@
# frozen_string_literal: true
FactoryGirl.define do
factory :attachment do
association :attachable, factory: :topic
end
end

View file

@ -23,7 +23,7 @@ RSpec.describe MapPolicy, type: :policy do
context 'private' do
let(:map) { create(:map, permission: :private) }
permissions :show?, :create?, :update?, :destroy? do
it 'permits access' do
it 'denies access' do
expect(subject).to_not permit(nil, map)
end
end