Skip to content

Commit

Permalink
Merge branch 'development' into test
Browse files Browse the repository at this point in the history
  • Loading branch information
syphax-bouazzouni committed Mar 22, 2024
2 parents 0689deb + ccf4f77 commit 1993c6b
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 173 deletions.
6 changes: 3 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ gem 'sinatra', '~> 1.0'
gem 'sinatra-advanced-routes'
gem 'sinatra-contrib', '~> 1.0'
gem 'request_store'
gem "parallel"
gem 'addressable', '~> 2.8'
gem 'json-ld', '~> 3.0.2'
gem 'parallel'
gem 'json-ld'


# Rack middleware
gem 'ffi'
Expand Down
3 changes: 2 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ GEM
google-cloud-env (2.1.1)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.4.0)
google-protobuf (3.25.3-x86_64-darwin)
google-protobuf (3.25.3-x86_64-linux)
googleapis-common-protos (1.5.0)
google-protobuf (~> 3.18)
Expand Down Expand Up @@ -430,7 +431,7 @@ DEPENDENCIES
ffi
goo!
haml (~> 5.2.2)
json-ld (~> 3.0.2)
json-ld
json-schema (~> 2.0)
listen (~> 3.9)
minitest (~> 4.0)
Expand Down
94 changes: 42 additions & 52 deletions controllers/dereference_resource_controller.rb
Original file line number Diff line number Diff line change
@@ -1,65 +1,55 @@
require_relative '../test/test_case'

use Rack::ContentNegotiation

class DereferenceResourceController < ApplicationController
namespace "/ontologies" do
get "/:acronym/resolve/:uri" do
acronym = params[:acronym]
uri = params[:uri]

namespace '/dereference_resource' do

get do
reply "GET: /:acronym/:uri?output_format= OR POST: acronym, uri, output_format parameters"
end
if acronym.blank? || uri.blank?
error 500, "Usage: ontologies/:acronym/resolve/:uri?output_format= OR POST: acronym, uri, output_format parameters"
end

get "/:acronym/:uri" do
acronym = params[:acronym]
uri = params[:uri]
output_format = params[:output_format].presence || 'jsonld'
process_request(acronym, uri, output_format)
end
output_format = env["format"].presence || params[:output_format].presence || 'application/n-triples'

process_request(acronym, uri, output_format)
end

post do
private

acronym = params[:acronym]
uri = params[:uri]
output_format = params[:output_format].presence || 'jsonld'
process_request(acronym, uri, output_format)
end
def process_request(acronym_param, uri_param, output_format)
acronym = acronym_param
uri = URI.decode_www_form_component(uri_param)

private
error 500, "INVALID URI" unless valid_url?(uri)
sub = LinkedData::Models::Ontology.find(acronym).first&.latest_submission

def process_request(acronym_param, uri_param, output_format)
acronym = URI.decode_www_form_component(acronym_param)
uri = URI.decode_www_form_component(uri_param)
unless valid_url?(acronym) && valid_url?(uri)
raise error 500, "INVALID URLs"
return
end
error 500, "Ontology not found" unless sub

r = Resource.new(acronym, uri)
case output_format
when 'jsonld'
content_type 'application/json'
reply JSON.parse(r.to_json)
when 'json'
content_type 'application/json'
reply JSON.parse(r.to_json)
when 'xml'
content_type 'application/xml'
reply r.to_xml
when 'turtle'
content_type 'text/turtle'
reply r.to_turtle
when 'ntriples'
content_type 'application/n-triples'
reply r.to_ntriples
else
raise error 500, "Invalid output format"
end
r = Resource.new(sub.id, uri)
case output_format
when 'application/ld+json', 'application/json'
r.to_json
when 'application/rdf+xml', 'application/xml'
r.to_xml
when 'text/turtle'
r.to_turtle
when 'application/n-triples'
r.to_ntriples
else
error 500, "Invalid output format, valid format are: application/json, application/ld+json, application/xml, application/rdf+xml, text/turtle and application/n-triples"
end

end

def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
rescue URI::InvalidURIError
false
end
end

def valid_url?(url)
uri = URI.parse(url)
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
rescue URI::InvalidURIError
false
end
end
end
131 changes: 131 additions & 0 deletions lib/rack/content_negotiation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
module Rack
class ContentNegotiation
DEFAULT_CONTENT_TYPE = "application/n-triples" # N-Triples
VARY = { 'Vary' => 'Accept' }.freeze
ENDPOINTS_FILTER = %r{^/ontologies/[^/]+/resolve/[^/]+$} # Accepted API endpoints to apply content negotiation

# @return [#call]
attr_reader :app

# @return [Hash{Symbol => String}]
attr_reader :options

##
# @param [#call] app
# @param [Hash{Symbol => Object}] options
# Other options passed to writer.
# @option options [String] :default (DEFAULT_CONTENT_TYPE) Specific content type
# @option options [RDF::Format, #to_sym] :format Specific RDF writer format to use
def initialize(app, options = {})
@app, @options = app, options
@options[:default] = (@options[:default] || DEFAULT_CONTENT_TYPE).to_s
end

##
# Handles a Rack protocol request.
# Parses Accept header to find appropriate mime-type and sets content_type accordingly.
#
# Inserts ordered content types into the environment as `ORDERED_CONTENT_TYPES` if an Accept header is present
#
# @param [Hash{String => String}] env
# @return [Array(Integer, Hash, #each)] Status, Headers and Body
# @see https://rubydoc.info/github/rack/rack/file/SPEC
def call(env)
if env['PATH_INFO'].match?(ENDPOINTS_FILTER)
if env.has_key?('HTTP_ACCEPT')
accepted_types = parse_accept_header(env['HTTP_ACCEPT'])
if !accepted_types.empty?
env["format"] = accepted_types.first
add_content_type_header(app.call(env), env["format"])
else
not_acceptable
end
else
env["format"] = options[:default]
add_content_type_header(app.call(env), env["format"])
end
else
app.call(env)
end
end

protected

# Parses an HTTP `Accept` header, returning an array of MIME content types ordered by precedence rules.
#
# @param [String, #to_s] header
# @return [Array<String>] Array of content types sorted by precedence
# @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
def parse_accept_header(header)
entries = header.to_s.split(',')
parsed_entries = entries.map { |entry| parse_accept_entry(entry) }
sorted_entries = parsed_entries.sort_by { |entry| entry.quality }.reverse
content_types = sorted_entries.map { |entry| entry.content_type }
content_types.flatten.compact
end



# Parses an individual entry from the Accept header.
#
# @param [String] entry An entry from the Accept header
# @return [Entry] An object representing the parsed entry
def parse_accept_entry(entry)
# Represents an entry parsed from the Accept header
entry_struct = Struct.new(:content_type, :quality, :wildcard_count, :param_count)
content_type, *params = entry.split(';').map(&:strip)
quality = 1.0 # Default quality
params.reject! do |param|
if param.start_with?('q=')
quality = param[2..-1].to_f
true
end
end
wildcard_count = content_type.count('*')
entry_struct.new(content_type, quality, wildcard_count, params.size)
end


##
# Returns a content type appropriate for the given `media_range`,
# returns `nil` if `media_range` contains a wildcard subtype
# that is not mapped.
#
# @param [String, #to_s] media_range
# @return [String, nil]
def find_content_type_for_media_range(media_range)
case media_range.to_s
when '*/*', 'text/*'
options[:default]
when 'application/n-triples'
'application/n-triples'
when 'text/turtle'
'text/turtle'
when 'application/json', 'application/ld+json', 'application/*'
'application/ld+json'
when 'text/xml', 'text/rdf+xml', 'application/rdf+xml', 'application/xml'
'application/rdf+xml'
else
nil
end
end

##
# Outputs an HTTP `406 Not Acceptable` response.
#
# @param [String, #to_s] message
# @return [Array(Integer, Hash, #each)]
def not_acceptable(message = nil)
code = 406
http_status = [code, Rack::Utils::HTTP_STATUS_CODES[code]].join(' ')
message = http_status + (message.nil? ? "\n" : " (#{message})\n")
[code, { 'Content-Type' => "text/plain" }.merge(VARY), [message]]
end

def add_content_type_header(response, type)
response[1] = response[1].merge(VARY).merge('Content-Type' => type)
response
end

end
end
Loading

0 comments on commit 1993c6b

Please sign in to comment.