Compare commits
4 commits
develop
...
feature/up
Author | SHA1 | Date | |
---|---|---|---|
|
2d04cca125 | ||
|
f10af09164 | ||
|
961b3f2444 | ||
|
c2565b75c8 |
20 changed files with 288 additions and 4 deletions
11
app/controllers/api/v2/attachments_controller.rb
Normal file
11
app/controllers/api/v2/attachments_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V2
|
||||
class AttachmentsController < RestfulController
|
||||
def searchable_columns
|
||||
[:file]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
37
app/controllers/attachments_controller.rb
Normal file
37
app/controllers/attachments_controller.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# 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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
app/policies/attachment_policy.rb
Normal file
21
app/policies/attachment_policy.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# 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
|
14
app/serializers/api/v2/attachment_serializer.rb
Normal file
14
app/serializers/api/v2/attachment_serializer.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# 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
|
|
@ -14,7 +14,8 @@ module Api
|
|||
def self.embeddable
|
||||
{
|
||||
user: {},
|
||||
metacode: {}
|
||||
metacode: {},
|
||||
attachments: {}
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -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: %i[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
|
||||
|
|
|
@ -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 apis/attachments.raml
|
||||
/maps: !include apis/maps.raml
|
||||
/mappings: !include apis/mappings.raml
|
||||
/metacodes: !include apis/metacodes.raml
|
||||
|
|
16
doc/api/apis/attachments.raml
Normal file
16
doc/api/apis/attachments.raml
Normal 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
|
10
doc/api/examples/attachment.json
Normal file
10
doc/api/examples/attachment.json
Normal 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"
|
||||
}
|
||||
}
|
28
doc/api/examples/attachments.json
Normal file
28
doc/api/examples/attachments.json
Normal 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
|
||||
}
|
||||
}
|
34
doc/api/schemas/_attachment.json
Normal file
34
doc/api/schemas/_attachment.json
Normal 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"
|
||||
]
|
||||
}
|
12
doc/api/schemas/attachment.json
Normal file
12
doc/api/schemas/attachment.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "Attachment Envelope",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "_attachment.json"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
}
|
19
doc/api/schemas/attachments.json
Normal file
19
doc/api/schemas/attachments.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "Attachments",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "_attachment.json"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"$ref": "_page.json"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"page"
|
||||
]
|
||||
}
|
38
spec/api/v2/attachments_api_spec.rb
Normal file
38
spec/api/v2/attachments_api_spec.rb
Normal 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
|
6
spec/factories/attachments.rb
Normal file
6
spec/factories/attachments.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
FactoryGirl.define do
|
||||
factory :attachment do
|
||||
association :attachable, factory: :topic
|
||||
end
|
||||
end
|
|
@ -9,4 +9,20 @@ RSpec.describe Topic, type: :model do
|
|||
it { is_expected.to have_many :mappings }
|
||||
it { is_expected.to validate_presence_of :permission }
|
||||
it { is_expected.to validate_inclusion_of(:permission).in_array Perm::ISSIONS.map(&:to_s) }
|
||||
|
||||
describe 'attachments_json' do
|
||||
let (:attachments) do
|
||||
create_list(:attachment, 1,
|
||||
file_file_name: 'file_name',
|
||||
file_content_type: 'text/plain',
|
||||
file_file_size: '100')
|
||||
end
|
||||
let (:topic) { create(:topic, attachments: attachments) }
|
||||
let(:json) { topic.attachments_json }
|
||||
|
||||
it 'returns correct json' do
|
||||
expect(json.first[:id]).to equal(attachments.first.id)
|
||||
binding.pry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue