Skip to content

Commit

Permalink
[F] Implement backend collection management
Browse files Browse the repository at this point in the history
[Completes #281]
  • Loading branch information
SMaxOwok committed Jun 2, 2017
1 parent c0da28b commit 70f56d2
Show file tree
Hide file tree
Showing 74 changed files with 3,650 additions and 897 deletions.
19 changes: 19 additions & 0 deletions api/app/authorizers/collection_authorizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class CollectionAuthorizer < ApplicationAuthorizer

def self.updatable_by?(user)
user.admin?
end

def self.readable_by?(_user)
true
end

def self.creatable_by?(user)
user.admin?
end

def self.deletable_by?(user)
user.admin?
end

end
45 changes: 18 additions & 27 deletions api/app/controllers/api/v1/collections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,43 @@ module V1
# Collections controller
class CollectionsController < ApplicationController

authorize_actions_for Collection, except: [:index, :show]
before_action :set_collection, only: [:show, :update, :destroy]
INCLUDES = [:project, :resources].freeze

resourceful! Collection, authorize_options: { except: [:index, :show] } do
Collection.filter(with_pagination!(collection_filter_params))
end

# GET /collections
def index
@collections = Collection.filtered(collection_filter_params)
render json: @collections,
each_serializer: CollectionSerializer
@collections = load_collections
render_multiple_resources(
@collections,
each_serializer: CollectionSerializer
)
end

# GET /collections/1
def show
render json: @collection,
include: [:resources]
@collection = load_collection
render_single_resource(@collection, include: INCLUDES)
end

# POST /collections
def create
@collection = Collection.new(collection_params)
if @collection.save
render json: @collection, status: :created, location: [:api, :v1, @collection]
else
render json: @collection.errors, status: :unprocessable_entity
end
@collection = authorize_and_create_collection(collection_params)
render_single_resource @collection
end

# PATCH/PUT /collections/1
def update
if @collection.update(collection_params)
render json: @collection
else
render json: @collection.errors, status: :unprocessable_entity
end
@collection = load_and_authorize_collection
::Updaters::Collection.new(collection_params).update(@collection)
render_single_resource(@collection, include: INCLUDES)
end

# DELETE /projects/1
def destroy
@collection = load_and_authorize_collection
@collection.destroy
end

Expand All @@ -49,15 +49,6 @@ def scope_for_collections
Collection.friendly
end

def set_collection
@collection = Collection.includes(resources: :collection_resources)
.friendly.find(params[:id])
end

# Only allow a trusted parameter "white list" through.
def collection_params
params.require(:collection)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Api
module V1
module Projects
module Relationships
# Responds with resources in a project
class CollectionsController < ApplicationController

before_action :set_project, only: [:index, :create]

resourceful! Collection, authorize_options: { except: [:index] } do
Collection.filter(
with_pagination!(collection_filter_params),
scope: @project.collections
)
end

def index
@collections = load_collections
render_multiple_resources(@collections, each_serializer: CollectionSerializer)
end

def create
@collection = ::Updaters::Collection.new(collection_params)
.update(@project.collections.new)
@collection.save
authorize_action_for @collection
location = api_v1_project_relationships_collections_url(
@collection,
project_id: @project.id
)
render_single_resource @collection, location: location
end

private

def set_project
@project = Project.friendly.find(params[:project_id])
end
end
end
end
end
end
10 changes: 9 additions & 1 deletion api/app/controllers/concerns/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ def collaborator_params
params.permit(param_config)
end

def collection_params
params.require(:data)
attributes = [:title, :description, attachment(:thumbnail)]
relationships = [:project, :resources]
param_config = structure_params(attributes: attributes, relationships: relationships)
params.permit(param_config)
end

def resource_params
params.require(:data)
attributes = [attachment(:attachment), :remove_attachment,
Expand Down Expand Up @@ -164,7 +172,7 @@ def favoritable_params
end

def collection_filter_params
params.permit(filter: [])[:filter]
params.permit(filter: [:keyword])[:filter]
end

def resource_filter_params
Expand Down
13 changes: 13 additions & 0 deletions api/app/lib/updaters/collection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Updaters
# Updates a Collection model from JSON-API style params
class Collection
include ::Updaters

def attachment_fields
[
:thumbnail
]
end

end
end
7 changes: 7 additions & 0 deletions api/app/models/collection.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# A collection of resources
class Collection < ApplicationRecord

# Constants
TYPEAHEAD_ATTRIBUTES = [:title].freeze

# Search
searchkick word_start: TYPEAHEAD_ATTRIBUTES, callbacks: :async

# Concerns
include Filterable
include Authority::Abilities
include Attachments
extend FriendlyId
Expand Down
5 changes: 5 additions & 0 deletions api/app/models/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class Resource < ApplicationRecord
return all unless project.present?
where(project: project)
}
scope :by_collection, lambda { |collection|
return all unless collection.present?
joins(:collection_resources)
.where("collection_resources.collection_id = ?", collection)
}
scope :by_tag, lambda { |tag|
return all unless tag.present?
tagged_with(tag)
Expand Down
2 changes: 1 addition & 1 deletion api/app/serializers/resource_partial_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class ResourcePartialSerializer < ActiveModel::Serializer
attributes :title, :kind, :sub_kind, :caption, :alt_text, :title_formatted, :project_id,
:caption_formatted, :attachment_styles, :variant_thumbnail_styles,
:credit_formatted, :credit, :external_type, :external_id, :slug,
:downloadable
:downloadable, :created_at

has_many :collection_resources

Expand Down
2 changes: 1 addition & 1 deletion api/app/serializers/resource_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ResourceSerializer < ResourcePartialSerializer

attributes :title, :kind, :attachment_file_name, :attachment_extension,
:attachment_content_type, :attachment_file_size, :attachment_updated_at,
:created_at, :updated_at, :project_id, :description_formatted,
:updated_at, :project_id, :description_formatted,
:caption, :description, :fingerprint, :alt_text, :keywords,
:copyright_status, :copyright_holder, :external_url,
:allow_high_res, :allow_download, :doi, :high_res_url,
Expand Down
4 changes: 4 additions & 0 deletions api/app/services/demonstration/data_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def create_pages
end
end

# rubocop:disable Metrics/AbcSize
def reindex_records
Project.reindex
@logger.info("Projects reindexed".green)
Expand All @@ -82,9 +83,12 @@ def reindex_records
@logger.info("Makers reindexed".green)
Resource.reindex
@logger.info("Resources reindexed".green)
Collection.reindex
@logger.info("Collections reindexed".green)
Event.reindex
@logger.info("Events reindexed".green)
end
# rubocop:enable Metrics/AbcSize

private

Expand Down
1 change: 1 addition & 0 deletions api/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
namespace :relationships do
resources :uncollected_resources, only: [:index]
resources :resources, only: [:index, :create]
resources :collections, only: [:index, :create]
resources :events, only: [:index]
resources :collaborators
resources :text_categories, only: [:index, :create]
Expand Down
2 changes: 1 addition & 1 deletion api/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@
t.jsonb "tweet_fetch_config", default: {}
t.date "publication_date"
t.string "slug"
t.jsonb "citations", default: {}
t.string "avatar_color", default: "primary"
t.jsonb "citations", default: {}
t.index ["slug"], name: "index_projects_on_slug", unique: true, using: :btree
end

Expand Down
13 changes: 13 additions & 0 deletions api/spec/models/resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
before(:each) do
@project_a = FactoryGirl.create(:project, title: "project_a")
@project_b = FactoryGirl.create(:project, title: "project_b")
@collection_a = FactoryGirl.create(:collection, title: "collection_a", project: @project_a)
@collection_b = FactoryGirl.create(:collection, title: "collection_b", project: @project_a)
@resource_a = FactoryGirl.create(:resource, title: "resource_a", project: @project_a)
@resource_b = FactoryGirl.create(:resource, title: "resource_b", project: @project_a)
@resource_c = FactoryGirl.create(:resource, title: "resource_c", project: @project_b, keywords: "test")
Expand All @@ -105,6 +107,17 @@
expect(results.length).to be 1
end

it "to only include those belonging to a collection" do
@resource_d = FactoryGirl.create(:resource, title: "resource_d", project: @project_a)
@collection_resource_a = FactoryGirl.create(:collection_resource, collection: @collection_a, resource: @resource_a)
@collection_resource_b = FactoryGirl.create(:collection_resource, collection: @collection_a, resource: @resource_b)
@collection_resource_c = FactoryGirl.create(:collection_resource, collection: @collection_b, resource: @resource_d)
results = Resource.filter({collection: @collection_a.id})
expect(results.length).to be 2
results = Resource.filter({collection: @collection_b.id})
expect(results.length).to be 1
end

it "by kind" do
# TBD: Expand this test. Right now all factory resources are links to avoid dealing
# with attachments.
Expand Down
66 changes: 66 additions & 0 deletions api/spec/requests/collections_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "rails_helper"

RSpec.describe "Collections API", type: :request do

include_context("authenticated request")
include_context("param helpers")

let(:collection) { FactoryGirl.create(:collection) }

describe "sends a list of collections" do
describe "the response" do
it "has a 200 status code" do
get api_v1_collections_path
expect(response).to have_http_status(200)
end
end
end

describe "updates a collection" do

let(:path) { api_v1_collection_path(collection) }

context "when the user is an admin" do

let(:headers) { admin_headers }

describe "the response" do
context "body" do
it("contains the updated title") { expect_updated_param("title", "some title") }
it("contains the updated description") { expect_updated_param("description", "some description") }
end

it "has a 200 OK status code" do
patch path, headers: headers, params: json_payload()
expect(response).to have_http_status(200)
end
end
end

describe "destroys a collection" do

let(:path) { api_v1_collection_path(collection) }

context "when the user is an admin" do

let(:headers) { admin_headers }

it "has a 204 NO CONTENT status code" do
delete path, headers: headers
expect(response).to have_http_status(204)
end
end

context "when the user is a reader" do

let(:headers) { reader_headers }

it "has a 403 FORBIDDEN status code" do
delete path, headers: headers
expect(response).to have_http_status(403)
end
end
end
end

end
2 changes: 1 addition & 1 deletion api/spec/requests/resources_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

let(:resource) { FactoryGirl.create(:resource) }

describe "sends a resource" do
describe "sends a list of resources" do
describe "the response" do
it "has a 200 status code" do
get api_v1_resources_path
Expand Down
16 changes: 16 additions & 0 deletions client/src/api/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,21 @@ export default {
heading: "Success",
body: `${payload.data.attributes.title} has been updated`,
};
},
[r.beCollectionCreate]: (payload) => {
return {
level: 0,
heading: "Your collection has been created.",
body: "A new manifold collection is born.",
expiration: 5000
};
},
[r.beCollectionUpdate]: (payload) => {
return {
level: 0,
heading: "Success",
body: `${payload.data.attributes.title} has been updated`,
expiration: 5000
};
}
};
Loading

0 comments on commit 70f56d2

Please sign in to comment.