Skip to content

Commit

Permalink
Merge pull request #39 from ontoportal-lirmm/feature/add-agent-crud-e…
Browse files Browse the repository at this point in the history
…ndpoint

Feature: Add agent crud endpoints
  • Loading branch information
syphax-bouazzouni committed Sep 5, 2023
1 parent 472677a commit 20482a6
Show file tree
Hide file tree
Showing 9 changed files with 490 additions and 17 deletions.
5 changes: 3 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
source 'https://rubygems.org'

gem 'activesupport', '~> 3.0'
gem 'activesupport', '~> 3.1'
# see https://github.com/ncbo/ontologies_api/issues/69
gem 'bigdecimal', '1.4.2'
gem 'faraday', '~> 1.9'
Expand All @@ -18,7 +18,7 @@ gem 'sinatra-contrib', '~> 1.0'
gem 'ffi'
gem 'rack-accept', '~> 0.4'
gem 'rack-attack', '~> 6.6.1', require: 'rack/attack'
gem 'rack-cache', '~> 1.0'
gem 'rack-cache', '~> 1.13.0'
gem 'rack-cors', require: 'rack/cors'
# GitHub dependency can be removed when https://github.com/niko/rack-post-body-to-params/pull/6 is merged and released
gem 'rack-post-body-to-params', github: 'palexander/rack-post-body-to-params', branch: 'multipart_support'
Expand All @@ -28,6 +28,7 @@ gem 'redis-rack-cache', '~> 2.0'
# Data access (caching)
gem 'redis'
gem 'redis-activesupport'
gem 'redis-store', '1.9.1'

# Monitoring
gem 'cube-ruby', require: 'cube'
Expand Down
18 changes: 10 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ GIT

GIT
remote: https://github.com/ontoportal-lirmm/ontologies_linked_data.git
revision: e9b708c40b2b22b935fb48d18ed19de8148fca35
revision: 4c89c8346766d23e09b24c8e29750bf3a91e6b53
branch: development
specs:
ontologies_linked_data (0.0.1)
Expand Down Expand Up @@ -105,7 +105,7 @@ GEM
multi_json (~> 1.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
airbrussh (1.4.1)
airbrussh (1.4.2)
sshkit (>= 1.6.1, != 1.7.0)
backports (3.23.0)
bcrypt (3.1.18)
Expand Down Expand Up @@ -204,10 +204,10 @@ GEM
net-smtp
memoist (0.16.2)
method_source (1.0.0)
mime-types (3.4.1)
mime-types (3.5.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2022.0105)
mini_mime (1.1.2)
mime-types-data (3.2023.0808)
mini_mime (1.1.5)
minitest (4.7.5)
minitest-stub_any_instance (1.0.3)
mlanett-redis-lock (0.2.7)
Expand All @@ -228,7 +228,7 @@ GEM
net-protocol
net-ssh (7.0.1)
netrc (0.11.0)
newrelic_rpm (8.16.0)
newrelic_rpm (9.4.2)
oj (2.18.5)
omni_logger (0.1.4)
logger
Expand Down Expand Up @@ -341,10 +341,11 @@ GEM
webrick (1.8.1)

PLATFORMS
x86_64-darwin-21
x86_64-linux

DEPENDENCIES
activesupport (~> 3.0)
activesupport (~> 3.1)
bcrypt_pbkdf (>= 1.0, < 2.0)
bigdecimal (= 1.4.2)
capistrano (~> 3)
Expand Down Expand Up @@ -372,7 +373,7 @@ DEPENDENCIES
rack
rack-accept (~> 0.4)
rack-attack (~> 6.6.1)
rack-cache (~> 1.0)
rack-cache (~> 1.13.0)
rack-cors
rack-mini-profiler
rack-post-body-to-params!
Expand All @@ -383,6 +384,7 @@ DEPENDENCIES
redis
redis-activesupport
redis-rack-cache (~> 2.0)
redis-store (= 1.9.1)
shotgun!
simplecov
simplecov-cobertura
Expand Down
145 changes: 145 additions & 0 deletions controllers/agents_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
class AgentsController < ApplicationController

%w[/agents /Agents].each do |namespace|
namespace namespace do
# Display all agents
get do
check_last_modified_collection(LinkedData::Models::Agent)
query = LinkedData::Models::Agent.where
query = apply_filters(LinkedData::Models::Agent, query)
query = query.include(LinkedData::Models::Agent.goo_attrs_to_load(includes_param))
if page?
page, size = page_params
agents = query.page(page, size).all
else
agents = query.to_a
end
reply agents
end

# Display a single agent
get '/:id' do
check_last_modified_collection(LinkedData::Models::Agent)
id = params["id"]
agent = LinkedData::Models::Agent.find(id).include(LinkedData::Models::Agent.goo_attrs_to_load(includes_param)).first
error 404, "Agent #{id} not found" if agent.nil?
reply 200, agent
end

# Create a agent with the given acronym
post do
reply 201, create_new_agent
end

# Create a agent with the given acronym
put '/:acronym' do
reply 201, create_new_agent
end

# Update an existing submission of a agent
patch '/:id' do
acronym = params["id"]
agent = LinkedData::Models::Agent.find(acronym).include(LinkedData::Models::Agent.attributes).first

if agent.nil?
error 400, "Agent does not exist, please create using HTTP PUT before modifying"
else
agent = update_agent(agent, params)

error 400, agent.errors unless agent.errors.empty?
end
halt 204
end

# Delete a agent
delete '/:id' do
agent = LinkedData::Models::Agent.find(params["id"]).first
agent.delete
halt 204
end

private

def update_identifiers(identifiers)
Array(identifiers).map do |i|
next nil if i.empty?

id = i["id"] || LinkedData::Models::AgentIdentifier.generate_identifier(i['notation'], i['schemaAgency'])
identifier = LinkedData::Models::AgentIdentifier.find(RDF::URI.new(id)).first

if identifier
identifier.bring_remaining
else
identifier = LinkedData::Models::AgentIdentifier.new
end

i.delete "id"

next identifier if i.keys.size.zero?

populate_from_params(identifier, i)

if identifier.valid?
identifier.save
else
error 400, identifier.errors
end
identifier
end.compact
end

def update_affiliations(affiliations)
Array(affiliations).map do |aff|
affiliation = aff["id"] ? LinkedData::Models::Agent.find(RDF::URI.new(aff["id"])).first : nil

if affiliation
affiliation.bring_remaining
affiliation.identifiers.each{|i| i.bring_remaining}
end

next affiliation if aff.keys.size.eql?(1) && aff["id"]

if affiliation
affiliation = update_agent(affiliation, aff)
else
affiliation = create_new_agent(aff["id"], aff)
end

error 400, affiliation.errors unless affiliation.errors.empty?

affiliation
end
end

def create_new_agent (id = @params['id'], params = @params)
agent = nil
agent = LinkedData::Models::Agent.find(id).include(LinkedData::Models::Agent.goo_attrs_to_load(includes_param)).first if id

if agent.nil?
agent = update_agent(LinkedData::Models::Agent.new, params)
error 400, agent.errors unless agent.errors.empty?

return agent
else
error 400, "Agent exists, please use HTTP PATCH to update"
end
end

def update_agent(agent, params)
return agent unless agent

identifiers = params.delete "identifiers"
affiliations = params.delete "affiliations"
params.delete "id"
populate_from_params(agent, params)
agent.identifiers = update_identifiers(identifiers)
agent.affiliations = update_affiliations(affiliations)

agent.save if agent.valid?
return agent
end

end
end

end
5 changes: 1 addition & 4 deletions controllers/ontologies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ class OntologiesController < ApplicationController
if includes_param.first == :all
# Bring what we need to display all attr of the submission
latest.bring_remaining
latest.bring({:contact=>[:name, :email],
:ontology=>[:acronym, :name, :administeredBy, :group, :viewingRestriction, :doNotUpdate, :flat,
:hasDomain, :summaryOnly, :acl, :viewOf, :ontologyType],
:submissionStatus=>[:code], :hasOntologyLanguage=>[:acronym]})
latest.bring(*submission_attributes_all)
else
latest.bring(*OntologySubmission.goo_attrs_to_load(includes_param))
end
Expand Down
4 changes: 1 addition & 3 deletions controllers/ontology_submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ class OntologySubmissionsController < ApplicationController
check_last_modified_segment(LinkedData::Models::OntologySubmission, [ont.acronym])
if includes_param.first == :all
# When asking to display all metadata, we are using bring_remaining which is more performant than including all metadata (remove this when the query to get metadata will be fixed)
ont.bring(submissions: [:released, :creationDate, :status, :submissionId,
{:contact=>[:name, :email], :ontology=>[:administeredBy, :acronym, :name, :summaryOnly, :ontologyType, :viewingRestriction, :acl, :group, :hasDomain, :views, :viewOf, :flat],
:submissionStatus=>[:code], :hasOntologyLanguage=>[:acronym]}, :submissionStatus])
ont.bring(submission_attributes_all)

ont.submissions.each do |sub|
sub.bring_remaining
Expand Down
4 changes: 4 additions & 0 deletions helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ def populate_from_params(obj, params)
value = is_arr ? value : [value]
new_value = []
value.each do |cls|
if uri_as_needed(cls["ontology"]).nil?
new_value << cls
next
end
sub = LinkedData::Models::Ontology.find(uri_as_needed(cls["ontology"])).first.latest_submission
new_value << LinkedData::Models::Class.find(cls["class"]).in(sub).first
end
Expand Down
33 changes: 33 additions & 0 deletions helpers/request_params_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,39 @@ def filter
build_filter
end

def apply_filters(object, query)
attributes_to_filter = object.attributes(:all).select{|x| params.keys.include?(x.to_s)}
filters = attributes_to_filter.map {|key| [key, params[key]&.split(',')]}.to_h
add_direct_filters(filters, query)
end

def apply_submission_filters(query)

filters = {
naturalLanguage: params[:naturalLanguage]&.split(',') , #%w[http://lexvo.org/id/iso639-3/fra http://lexvo.org/id/iso639-3/eng],
hasOntologyLanguage_acronym: params[:hasOntologyLanguage]&.split(',') , #%w[OWL SKOS],
ontology_hasDomain_acronym: params[:hasDomain]&.split(',') , #%w[Crop Vue_francais],
ontology_group_acronym: params[:group]&.split(','), #%w[RICE CROP],
ontology_name: Array(params[:name]) + Array(params[:name]&.capitalize),
isOfType: params[:isOfType]&.split(','), #["http://omv.ontoware.org/2005/05/ontology#Vocabulary"],
hasFormalityLevel: params[:hasFormalityLevel]&.split(','), #["http://w3id.org/nkos/nkostype#thesaurus"],
ontology_viewingRestriction: params[:viewingRestriction]&.split(','), #["private"]
}
inverse_filters = {
status: params[:status], #"retired",
submissionStatus: params[:submissionStatus] #"RDF",
}

query = add_direct_filters(filters, query)

query = add_inverse_filters(inverse_filters, query)

query = add_acronym_name_filters(query)

add_order_by_patterns(query)
end


def get_order_by_from(params, default_order = :asc)
if is_set?(params['sortby'])
orders = (params["order"] || default_order.to_s).split(',')
Expand Down
68 changes: 68 additions & 0 deletions helpers/submission_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'sinatra/base'

module Sinatra
module Helpers
module SubmissionHelper

def submission_attributes_all
out = [LinkedData::Models::OntologySubmission.embed_values_hash]
out << {:contact=>[:name, :email]}
out << {:ontology=>[:acronym, :name, :administeredBy, :group, :viewingRestriction, :doNotUpdate, :flat,
:hasDomain, :summaryOnly, :acl, :viewOf, :ontologyType]}

out
end

def retrieve_submissions(options)
status = (options[:status] || "RDF").to_s.upcase
status = "RDF" if status.eql?("READY")
any = status.eql?("ANY")
include_views = options[:also_include_views] || false
includes, page, size, order_by, _ = settings_params(LinkedData::Models::OntologySubmission)
includes << :submissionStatus unless includes.include?(:submissionStatus)

submissions_query = LinkedData::Models::OntologySubmission
if any
submissions_query = submissions_query.where
else
submissions_query = submissions_query.where({ submissionStatus: [code: status] })
end

submissions_query = apply_submission_filters(submissions_query)
submissions_query = submissions_query.filter(Goo::Filter.new(ontology: [:viewOf]).unbound) unless include_views
submissions_query = submissions_query.filter(filter) if filter?

# When asking to display all metadata, we are using bring_remaining on each submission. Slower but best way to retrieve all attrs
if includes_param.first == :all
includes = [:submissionId, { :contact => [:name, :email],
:ontology => [:administeredBy, :acronym, :name, :summaryOnly, :ontologyType, :viewingRestriction, :acl,
:group, :hasDomain, :views, :viewOf, :flat, :notes, :reviews, :projects],
:submissionStatus => [:code], :hasOntologyLanguage => [:acronym], :metrics => [:classes, :individuals, :properties] },
:submissionStatus]
else
if includes.find { |v| v.is_a?(Hash) && v.keys.include?(:ontology) }
includes << { :ontology => [:administeredBy, :acronym, :name, :viewingRestriction, :group, :hasDomain, :notes, :reviews, :projects, :acl, :viewOf] }
end

if includes.find { |v| v.is_a?(Hash) && v.keys.include?(:contact) }
includes << { :contact => [:name, :email] }
end
end

submissions = submissions_query.include(includes)
if page?
submissions.page(page, size).all
else
submissions.to_a
end
end

def include_ready?(options)
options[:status] && options[:status].to_s.upcase.eql?("READY")
end

end
end
end

helpers Sinatra::Helpers::SubmissionHelper
Loading

0 comments on commit 20482a6

Please sign in to comment.