Skip to content

Commit

Permalink
Update for new /objects API spec
Browse files Browse the repository at this point in the history
  • Loading branch information
johnf committed Nov 8, 2024
1 parent 218c8a7 commit f7bfb41
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 26 deletions.
33 changes: 14 additions & 19 deletions app/controllers/api/v1/oni_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ module Api
module V1
class OniController < ApplicationController
def objects
limit = (params[:limit] || 100).to_i
offset = (params[:offset] || 0).to_i
sortBy = params[:sortBy] || 'identifier'
memberOf = params[:memberOf]
unless %w[identifier title created_at].include?(sortBy)
orderBy = 'identifier'
end
sortDirection = (params[:sortDirection] || '').upcase === 'DESC' ? 'DESC' : 'ASC'
conforms_to = params[:conformsTo]
query = Oni::ObjectsValidator.new(params)

render json: { errors: query.errors.full_messages }, status: :unprocessable_entity unless query.valid?

# TODO: Expand this once we have an authenticated version of this endpoint
collections_table = Collection.where(private: false).arel_table
items_table = Item.where(private: false).arel_table

Expand All @@ -26,11 +21,11 @@ def objects
collections_query = collections_table.where(collections_table[:private].eq(false))
items_query = items_table
.join(collections_table).on(items_table[:collection_id].eq(collections_table[:id]))
.project(items_table[:id], items_table[:created_at], item_identifier, items_table[:title], item_label.as('type'))
.project(items_table[:id], items_table[:created_at], items_table[:updated_at], item_identifier, items_table[:title], item_label.as('type'))
.where(items_table[:private].eq(false))

if memberOf
md = params[:memberOf].match(repository_collection_url(collection_identifier: '(.*)'))
if query.member_of
md = query.member_of.match(repository_collection_url(collection_identifier: '(.*)'))
unless md
render json: { error: 'Invalid memberOf parameter' }, status: :bad_request
return
Expand All @@ -40,17 +35,17 @@ def objects
items_query = items_query.where(items_table[:collection_id].in(collections_query.clone.project(collections_table[:id])))
end

collections_query = collections_query.project(:id, :created_at, :identifier, :title, collection_label.as('type'))
collections_query = collections_query.project(:id, :created_at, :updated_at, :identifier, :title, collection_label.as('type'))

combined_query = case conforms_to
when 'https://w3id.org/ldac/profile#Collection'
combined_query = case query.conforms_to
when ['https://w3id.org/ldac/profile#Collection']
# A bit hacky but we dont' have colletctions of collections
if memberOf
if query.member_of
collections_query.where(collections_table[:identifier].eq('DUMMYsajkdhakshfvksfslkj'))
else
collections_query
end
when 'https://w3id.org/ldac/profile#Item'
when ['https://w3id.org/ldac/profile#Object']
items_query
else
collections_query.union(items_query)
Expand All @@ -65,7 +60,7 @@ def objects

# Final query with limit and offset
final_query = Arel::SelectManager.new(Arel::Table.engine)
final_query.from(combined_query.as('combined')).project(Arel.star).order("#{sortBy} #{sortDirection}").skip(offset).take(limit)
final_query.from(combined_query.as('combined')).project(Arel.star).order("#{query.sort} #{query.order}").skip(query.offset).take(query.limit)

ids = ActiveRecord::Base.connection.select_all(final_query.to_sql)

Expand All @@ -75,7 +70,7 @@ def objects
collections = Collection.where(id: collection_ids).includes(:access_condition)
items = Item.where(id: item_ids).includes(:collection, :access_condition, :content_languages)

@data = ids.map do |id|
@objects = ids.map do |id|
if id['type'] == 'collection'
collections.find { |c| c.id == id['id'] }
else
Expand Down
44 changes: 44 additions & 0 deletions app/validators/oni/objects_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Oni
class ObjectsValidator
include ActiveModel::Validations

# NOTE: We remap name to title below to match the database column
SORT_FIELDS = %w[id title created_at updated_at].freeze
ORDER_FIELDS = %w[asc desc].freeze
CONFORMS_TO_VALUES = %w[https://w3id.org/ldac/profile#Collection https://w3id.org/ldac/profile#Object].freeze

ATTRIBUTES = %i[member_of conforms_to limit offset order sort].freeze
attr_accessor(*ATTRIBUTES)

validates :member_of, format: URI::DEFAULT_PARSER.make_regexp(%w[http https]), allow_nil: true
validates :conforms_to, inclusion: { in: CONFORMS_TO_VALUES, message: '%{value} is not a valid conformsTo' }, allow_nil: true
validates :order, inclusion: { in: ORDER_FIELDS, message: '%{value} is not a valid order' }, allow_nil: true
validates :sort, inclusion: { in: SORT_FIELDS, message: '%{value} is not a valid sort field' }, allow_nil: true
validates :limit, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 1000 }, allow_nil: true
validates :offset, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true

def initialize(params)
object_params = params.permit(*ATTRIBUTES.map { | attr| attr.to_s.camelize(:lower).to_sym }).except(:format)
object_params.each do |key, value|
snake_key = key.to_s.underscore

if %w[limit offset].include?(snake_key)
value = value.to_i if value.present?
end

value = value.underscore if snake_key == 'sort' && value.present?

snake_key = 'title' if snake_key == 'name'

value = value.split(',') if snake_key == 'conforms_to' && value.present?

send("#{snake_key}=", value) if respond_to?("#{snake_key}=")
end

@limit ||= 1000
@offset ||= 0
@order ||= 'asc'
@sort ||= 'title'
end
end
end
16 changes: 9 additions & 7 deletions app/views/api/v1/oni/objects.json.jb
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
{
total: @total,

data: @data.map do |data|
objects: @objects.map do |object|
class_name = data.class.name
is_item = class_name == 'Item'

response = {
crateId: is_item ? repository_item_url(data.collection, data) : repository_collection_url(data),
id: is_item ? repository_item_url(object.collection, object) : repository_collection_url(object),
name: object.title,
description: object.description.truncate(256),
conformsTo: "https://w3id.org/ldac/profile##{is_item ? 'Object' : 'Collection'}",
recordType: ['Data', 'Object', is_item ? 'RepositoryObject' : 'RepositoryCollection'],
name: data.title,
license: data.access_condition_name,
description: data.description.truncate(256),
inLanguage: (is_item ? data.content_languages : data.languages).map { |language| { name: language.name, code: language.code } }
# FIXME: Outside of current API spec and subject to change
inLanguage: (is_item ? object.content_languages : object.languages).map { |language| { name: language.name, code: language.code } }[0]
}

if is_item
response[:memberOf] = repository_collection_url(data.collection)
response[:memberOf] = repository_collection_url(object.collection)
response[:parent] = repository_collection_url(object.collection)
response[:root] = repository_collection_url(object.collection)
end

response
Expand Down
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Application < Rails::Application
config.solid_queue.silence_polling = true

ActiveSupport::Dependencies.autoload_paths << Rails.root.join('app/services')
ActiveSupport::Dependencies.autoload_paths << Rails.root.join('app/validators')
ActiveSupport::Dependencies.autoload_paths << Rails.root.join('lib')

config.viewer_url = '/viewer'
Expand Down

0 comments on commit f7bfb41

Please sign in to comment.