Class: TriannonClient::TriannonClient
- Inherits:
-
Object
- Object
- TriannonClient::TriannonClient
- Defined in:
- lib/triannon-client/triannon_client.rb
Constant Summary collapse
- CONTENT_TYPES =
Triannon may not support all content types in RDF::Format.content_types, but the client code is more generic using this as a reasonable set; this allows triannon to evolve support for anything supported by RDF::Format.
RDF::Format.content_types.keys
- JSONLD_TYPE =
'application/ld+json'
- PROFILE_IIIF =
'http://iiif.io/api/presentation/2/context.json'
- PROFILE_OA =
'http://www.w3.org/ns/oa-context-20130208.json'
- CONTENT_TYPE_IIIF =
"#{JSONLD_TYPE}; profile=\"#{PROFILE_IIIF}\""
- CONTENT_TYPE_OA =
"#{JSONLD_TYPE}; profile=\"#{PROFILE_OA}\""
Instance Attribute Summary collapse
-
#auth ⇒ Object
Returns the value of attribute auth.
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#container ⇒ Object
Returns the value of attribute container.
-
#site ⇒ Object
Returns the value of attribute site.
Instance Method Summary collapse
-
#annotation_id(uri) ⇒ Object
extract an annotation ID from the URI.
-
#annotation_uris(graph) ⇒ Object
query an annotation graph to extract a URI for the first open annotation.
- #authenticate ⇒ Object
-
#authenticate! ⇒ Object
Reset authentication.
-
#delete_annotation(id) ⇒ Object
Delete an annotation.
-
#get_annotation(id, content_type = JSONLD_TYPE) ⇒ Object
Get an annotation (with a default annotation context).
-
#get_annotations(content_type = JSONLD_TYPE) ⇒ Object
Get annotations.
-
#get_iiif_annotation(id) ⇒ Object
Get an annotation using a IIIF context.
-
#get_oa_annotation(id) ⇒ Object
Get an annotation using an open annotation context.
-
#initialize ⇒ TriannonClient
constructor
Initialize a new triannon client All params are set using ::TriannonClient.configuration.
-
#post_annotation(oa) ⇒ Object
POST and open annotation to triannon; the response contains an ID.
-
#response2graph(response) ⇒ Object
Parse a Triannon response into an RDF::Graph; the graph can be empty on failure to parse input.
Constructor Details
#initialize ⇒ TriannonClient
Initialize a new triannon client All params are set using ::TriannonClient.configuration
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/triannon-client/triannon_client.rb', line 21 def initialize # Configure triannon-app service @config = ::TriannonClient.configuration host = @config.host host.chomp!('/') if host.end_with?('/') @site = RestClient::Resource.new( host, cookies: {}, headers: jsonld_payloads, open_timeout: 5, read_timeout: 30 ) @auth = @site['/auth'] container = @config.container container = "/#{container}" unless container.start_with?('/') container = "#{container}/" unless container.end_with?('/') @container = @site[container] end |
Instance Attribute Details
#auth ⇒ Object
Returns the value of attribute auth.
15 16 17 |
# File 'lib/triannon-client/triannon_client.rb', line 15 def auth @auth end |
#config ⇒ Object (readonly)
Returns the value of attribute config.
14 15 16 |
# File 'lib/triannon-client/triannon_client.rb', line 14 def config @config end |
#container ⇒ Object
Returns the value of attribute container.
17 18 19 |
# File 'lib/triannon-client/triannon_client.rb', line 17 def container @container end |
#site ⇒ Object
Returns the value of attribute site.
16 17 18 |
# File 'lib/triannon-client/triannon_client.rb', line 16 def site @site end |
Instance Method Details
#annotation_id(uri) ⇒ Object
extract an annotation ID from the URI
274 275 276 277 278 |
# File 'lib/triannon-client/triannon_client.rb', line 274 def annotation_id(uri) raise ArgumentError, 'uri is not an RDF::URI' unless uri.instance_of? RDF::URI path = uri.path.split(@config.container).last CGI::escape(path) end |
#annotation_uris(graph) ⇒ Object
query an annotation graph to extract a URI for the first open annotation
265 266 267 268 269 |
# File 'lib/triannon-client/triannon_client.rb', line 265 def annotation_uris(graph) raise ArgumentError, 'graph is not an RDF::Graph' unless graph.instance_of? RDF::Graph q = [:s, RDF.type, RDF::Vocab::OA.Annotation] graph.query(q).collect {|s| s.subject } end |
#authenticate ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/triannon-client/triannon_client.rb', line 46 def authenticate if @access_expiry.to_i < Time.now.to_i @access_code = nil @site.headers.delete :Authorization @site.[:cookies] = {} end @access_code || begin # 1. Obtain a client authorization code (short-lived token) return false if @config.client_id.empty? && @config.client_pass.empty? client = { clientId: @config.client_id, clientSecret: @config.client_pass } response = @auth["/client_identity"].post client.to_json, json_payloads @auth.[:cookies] = response. # save the cookie data return false unless response.code == 200 auth = JSON.parse(response.body) auth_code = auth['authorizationCode'] return false if auth_code.nil? # 2. The client POSTs user credentials for a container, which modifies # content in the cookies. return false if @config.container_user.empty? && @config.container_workgroups.empty? user = { userId: @config.container_user, workgroups: @config.container_workgroups } client_auth = "?code=#{auth_code}" response = @auth["/login#{client_auth}"].post user.to_json, json_payloads @auth.[:cookies] = response. # save the cookie data return false unless response.code == 200 # 3. The client, on behalf of user, obtains a long-lived access token. response = @auth["/access_token#{client_auth}"].get # no content type @auth.[:cookies] = response. # save the cookie data return false unless response.code == 200 access = JSON.parse(response.body) return false if access['accessToken'].nil? @access_code = "Bearer #{access['accessToken']}" @access_expiry = Time.now.to_i + access['expiresIn'].to_i @site.headers[:Authorization] = @access_code end end |
#authenticate! ⇒ Object
Reset authentication
41 42 43 44 |
# File 'lib/triannon-client/triannon_client.rb', line 41 def authenticate! @access_expiry = nil authenticate end |
#delete_annotation(id) ⇒ Object
Delete an annotation
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/triannon-client/triannon_client.rb', line 91 def delete_annotation(id) tries = 0 begin tries += 1 check_id(id) if tries == 1 authenticate if tries == 2 response = @container[id].delete # HTTP DELETE response codes: A successful response SHOULD be # 200 (OK) if the response includes an entity describing the status, # 202 (Accepted) if the action has not yet been enacted, or # 204 (No Content) if the action has been enacted but the response # does not include an entity. return [200, 202, 204].include?(response.code) rescue RestClient::Exception => e msg = e. response = e.response if response.is_a?(RestClient::Response) case response.code when 401 retry if tries == 1 when 403 # DELETE is not authorized when 404, 410 # If an annotation doesn't exist, consider it a 'success' return true end msg = response.body end @config.logger.error("Failed to DELETE annotation: #{id}, #{msg}") binding.pry if @config.debug rescue => e binding.pry if @config.debug @config.logger.error("Failed to DELETE annotation: #{id}, #{e.}") end false end |
#get_annotation(id, content_type = JSONLD_TYPE) ⇒ Object
Get an annotation (with a default annotation context)
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/triannon-client/triannon_client.rb', line 193 def get_annotation(id, content_type=JSONLD_TYPE) g = RDF::Graph.new begin check_id(id) content_type = check_content_type(content_type) response = @container[id].get({:accept => content_type}) g = response2graph(response) rescue => e msg = e. r = e.response rescue nil if r.is_a?(RestClient::Response) case r.code when 404 msg = "Failed to GET annotation: #{id}, #{r.code}" else msg = "Failed to GET annotation: #{id}, #{r.code}, #{r.body}" end end binding.pry if @config.debug @config.logger.error(msg) end g end |
#get_annotations(content_type = JSONLD_TYPE) ⇒ Object
Get annotations
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/triannon-client/triannon_client.rb', line 166 def get_annotations(content_type=JSONLD_TYPE) g = RDF::Graph.new begin content_type = check_content_type(content_type) response = @container.get({:accept => content_type}) g = response2graph(response) rescue => e msg = e. r = e.response rescue nil if r.is_a?(RestClient::Response) case r.code when 404 msg = "Failed to GET annotations: #{r.code}" else msg = "Failed to GET annotations: #{r.code}, #{r.body}" end end binding.pry if @config.debug @config.logger.error(msg) end g end |
#get_iiif_annotation(id) ⇒ Object
Get an annotation using a IIIF context
220 221 222 |
# File 'lib/triannon-client/triannon_client.rb', line 220 def get_iiif_annotation(id) get_annotation(id, CONTENT_TYPE_IIIF) end |
#get_oa_annotation(id) ⇒ Object
Get an annotation using an open annotation context
227 228 229 |
# File 'lib/triannon-client/triannon_client.rb', line 227 def get_oa_annotation(id) get_annotation(id, CONTENT_TYPE_OA) end |
#post_annotation(oa) ⇒ Object
POST and open annotation to triannon; the response contains an ID
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/triannon-client/triannon_client.rb', line 131 def post_annotation(oa) post_data = { 'commit' => 'Create Annotation', 'annotation' => {'data' => oa} } response = nil tries = 0 begin tries += 1 authenticate if tries == 2 response = @container.post post_data rescue RestClient::Exception => e msg = e. response = e.response if response.is_a?(RestClient::Response) case response.code when 401 retry if tries == 1 when 403 # POST is not authorized end msg = "Failed to POST annotation: #{response.code}, #{response.body}" end binding.pry if @config.debug @config.logger.error(msg) rescue => e binding.pry if @config.debug @config.logger.error("Failed to POST annotation: #{e.}") end return response end |
#response2graph(response) ⇒ Object
Parse a Triannon response into an RDF::Graph; the graph can be empty on failure to parse input. The response must contain a content-type that is available in RDF::Format.content_types (see code specs for details).
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/triannon-client/triannon_client.rb', line 236 def response2graph(response) unless response.is_a? RestClient::Response raise ArgumentError, 'response2graph only accepts a RestClient::Response' end g = RDF::Graph.new begin content_type = response.headers[:content_type] content_type = check_content_type(content_type) case content_type when /ld\+json/ g = RDF::Graph.new.from_jsonld(response.body) when /turtle/ g = RDF::Graph.new.from_ttl(response.body) else format = RDF::Format.for(:content_type => content_type) format.reader.new(response.body) do |reader| reader.each_statement {|s| g << s } end end rescue => e binding.pry if @config.debug @config.logger.error("Failed to parse response into RDF::Graph: #{e.}") end g end |