Class: RESTHelpers::Endpoint
- Inherits:
-
Object
- Object
- RESTHelpers::Endpoint
- Defined in:
- backend/app/lib/rest.rb
Constant Summary collapse
- @@endpoints =
[]
- @@param_types =
{ :repo_id => [Integer, "The Repository ID", {:validation => ["The Repository must exist", ->(v) {Repository.exists?(v)}]}], :resolve => [[String], "A list of references to resolve and embed in the response", :optional => true], :id => [Integer, "The ID of the record"] }
- @@return_types =
{ :created => '{:status => "Created", :id => (id of created object), :warnings => {(warnings)}}', :updated => '{:status => "Updated", :id => (id of updated object)}', :suppressed => '{:status => "Suppressed", :id => (id of updated object), :suppressed_state => (true|false)}', :error => '{:error => (description of error)}' }
Class Method Summary collapse
-
.all ⇒ Object
-
.delete(uri) ⇒ Object
-
.get(uri) ⇒ Object
-
.get_or_post(uri) ⇒ Object
-
.is_potentially_destructive_request?(env) ⇒ Boolean
-
.is_toplevel_request?(env) ⇒ Boolean
Helpers.
-
.method(method) ⇒ Object
-
.post(uri) ⇒ Object
Instance Method Summary collapse
-
#[](key) ⇒ Object
-
#deprecated(description = nil) ⇒ Object
-
#description(description) ⇒ Object
-
#documentation(docs = nil, prepend: true) ⇒ Object
Add documentation for endpoint to be interpolated into the API docs.
-
#example(highlighter, contents = nil) ⇒ Object
Add an example to the example code tabs.
-
#initialize(method) ⇒ Endpoint
constructor
A new instance of Endpoint.
-
#no_data(val) ⇒ Object
-
#paged(val) ⇒ Object
-
#paginated(val) ⇒ Object
-
#params(*params) ⇒ Object
-
#permissions(permissions) ⇒ Object
-
#preconditions(*preconditions) ⇒ Object
-
#request_context(hash) ⇒ Object
-
#returns(*returns, &block) ⇒ Object
-
#sufficient_permissions(permissions) ⇒ Object
useful in cases where a permission presupposes another created earlier and not necessarily propagated to existing repos.
-
#uri(uri) ⇒ Object
-
#use_transaction(val) ⇒ Object
Constructor Details
#initialize(method) ⇒ Endpoint
Returns a new instance of Endpoint.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'backend/app/lib/rest.rb', line 87 def initialize(method) @methods = ASUtils.wrap(method) @uri = "" @description = "-- No description provided --" @documentation = nil @prepend_to_autodoc = true @examples = {} = [] @preconditions = [] @required_params = [] @paginated = false @paged = false @no_data = false @use_transaction = :unspecified @returns = [] @request_context_keyvals = {} end |
Class Method Details
.all ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'backend/app/lib/rest.rb', line 112 def self.all @@endpoints.map do |e| e.instance_eval do { :uri => @uri, :description => @description, :permissions => , :documentation => @documentation, :deprecated => @deprecated, :deprecated_description => @deprecated_description, :prepend_docs => @prepend_to_autodoc, :examples => @examples, :method => @methods, :params => @required_params, :paginated => @paginated, :paged => @paged, :no_data => @no_data, :returns => @returns } end end end |
.delete(uri) ⇒ Object
140 |
# File 'backend/app/lib/rest.rb', line 140 def self.delete(uri) self.method(:delete).uri(uri) end |
.get(uri) ⇒ Object
136 |
# File 'backend/app/lib/rest.rb', line 136 def self.get(uri) self.method(:get).uri(uri) end |
.get_or_post(uri) ⇒ Object
142 |
# File 'backend/app/lib/rest.rb', line 142 def self.get_or_post(uri) self.method([:get, :post]).uri(uri) end |
.is_potentially_destructive_request?(env) ⇒ Boolean
151 152 153 |
# File 'backend/app/lib/rest.rb', line 151 def self.is_potentially_destructive_request?(env) env["REQUEST_METHOD"] != "GET" end |
.is_toplevel_request?(env) ⇒ Boolean
Helpers
147 148 149 |
# File 'backend/app/lib/rest.rb', line 147 def self.is_toplevel_request?(env) env["ASPACE_REENTRANT"].nil? end |
.method(method) ⇒ Object
144 |
# File 'backend/app/lib/rest.rb', line 144 def self.method(method) Endpoint.new(method) end |
.post(uri) ⇒ Object
138 |
# File 'backend/app/lib/rest.rb', line 138 def self.post(uri) self.method(:post).uri(uri) end |
Instance Method Details
#[](key) ⇒ Object
105 106 107 108 109 |
# File 'backend/app/lib/rest.rb', line 105 def [](key) if instance_variable_defined?("@#{key}") instance_variable_get("@#{key}") end end |
#deprecated(description = nil) ⇒ Object
270 271 272 273 274 275 |
# File 'backend/app/lib/rest.rb', line 270 def deprecated(description = nil) @deprecated = true @deprecated_description = description self end |
#description(description) ⇒ Object
158 |
# File 'backend/app/lib/rest.rb', line 158 def description(description) @description = description; self end |
#documentation(docs = nil, prepend: true) ⇒ Object
Add documentation for endpoint to be interpolated into the API docs. If “prepend” is true, the automated docs (e.g. pagination) will be appended to this when API docs are generated, otherwise this will replace the docs entirely.
Note: If you make prepend false, you should provide Parameters and Returns sections manually.
Recommended usage:
endpoint.documentation do «~DOCS # Header Some content - with maybe a list - who doesn’t like lists, right? DOCS end
183 184 185 186 187 188 189 190 191 192 193 |
# File 'backend/app/lib/rest.rb', line 183 def documentation(docs = nil, prepend: true) if block_given? docs = yield docs, prepend end if docs @documentation = docs @prepend_to_autodoc = prepend end self end |
#example(highlighter, contents = nil) ⇒ Object
Add an example to the example code tabs.
The highlighter argument must be a language code understood by the rouge highlighting library (https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers)
Recommended usage:
endpoint.example(‘shell’) do «~CONTENTS wget ‘blah blah blah’ CONTENTS end
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'backend/app/lib/rest.rb', line 207 def example(highlighter, contents = nil) if block_given? contents = yield contents end if contents contents = <<~TEMPLATE ```#{highlighter} #{contents} ``` TEMPLATE @examples[highlighter] = contents end self end |
#no_data(val) ⇒ Object
289 290 291 292 293 |
# File 'backend/app/lib/rest.rb', line 289 def no_data(val) @no_data = val self end |
#paged(val) ⇒ Object
283 284 285 286 287 |
# File 'backend/app/lib/rest.rb', line 283 def paged(val) @paged = val self end |
#paginated(val) ⇒ Object
277 278 279 280 281 |
# File 'backend/app/lib/rest.rb', line 277 def paginated(val) @paginated = val self end |
#params(*params) ⇒ Object
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'backend/app/lib/rest.rb', line 253 def params(*params) @required_params = params.map do |p| param_name, param_type = p if @@param_types[param_type] # This parameter type has a standard definition defn = @@param_types[param_type] [param_name, *defn] else p end end self end |
#permissions(permissions) ⇒ Object
234 235 236 237 238 239 240 241 242 243 |
# File 'backend/app/lib/rest.rb', line 234 def () = true += .each do || @preconditions << proc { |request| current_user.can?() } end self end |
#preconditions(*preconditions) ⇒ Object
160 |
# File 'backend/app/lib/rest.rb', line 160 def preconditions(*preconditions) @preconditions += preconditions; self end |
#request_context(hash) ⇒ Object
246 247 248 249 250 |
# File 'backend/app/lib/rest.rb', line 246 def request_context(hash) @request_context_keyvals = hash self end |
#returns(*returns, &block) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'backend/app/lib/rest.rb', line 302 def returns(*returns, &block) raise "No .permissions declaration for endpoint #{@methods.map {|m| m.to_s.upcase}.join('|')} #{@uri}" if ! @returns = returns.map { |r| r[1] = @@return_types[r[1]] || r[1]; r } @@endpoints << self preconditions = @preconditions rp = @required_params paginated = @paginated paged = @paged deprecated = @deprecated deprecated_description = @deprecated_description use_transaction = @use_transaction uri = @uri methods = @methods request_context = @request_context_keyvals if ArchivesSpaceService.development? # Undefine any pre-existing routes (sinatra reloader seems to have trouble # with this for our instances) ArchivesSpaceService.instance_eval { new_route = compile(uri) methods.each do |method| if @routes[method.to_s.upcase] @routes[method.to_s.upcase].reject! do |route| route[0..1] == new_route end end end } end methods.each do |method| ArchivesSpaceService.send(method, uri, {}) do if deprecated Log.warn("\n" + ("*" * 80) + "\n*** CALLING A DEPRECATED ENDPOINT: #{method} #{uri}\n" + (deprecated_description ? ("\n" + deprecated_description) : "") + "\n" + ("*" * 80)) end RequestContext.open(request_context) do DB.open do |db| ensure_params(rp, paginated, paged) end Log.debug("Post-processed params: #{Log.filter_passwords(params).inspect}") RequestContext.put(:repo_id, params[:repo_id]) RequestContext.put(:is_high_priority, high_priority_request?) if Endpoint.is_toplevel_request?(env) || Endpoint.is_potentially_destructive_request?(env) unless preconditions.all? { |precondition| self.instance_eval &precondition } raise AccessDeniedException.new("Access denied") end end use_transaction = (use_transaction == :unspecified) ? true : use_transaction db_opts = {} if use_transaction if methods == [:post] # Pure POST requests use read committed so that tree position # updates can be retried with a chance of succeeding (i.e. we # can read the last committed value when determining our # position) db_opts[:isolation_level] = :committed else # Anything that might be querying the DB will get repeatable read. db_opts[:isolation_level] = :repeatable end end DB.open(use_transaction, db_opts) do RequestContext.put(:current_username, current_user.username) # If the current user is a manager, show them suppressed records # too. if RequestContext.get(:repo_id) if current_user.can?(:index_system) # Don't mess with the search user RequestContext.put(:enforce_suppression, false) else RequestContext.put(:enforce_suppression, !((current_user.can?(:manage_repository) || current_user.can?(:view_suppressed) || current_user.can?(:suppress_archival_record)) && Preference.defaults['show_suppressed'])) end end self.instance_eval &block end end end end end |
#sufficient_permissions(permissions) ⇒ Object
useful in cases where a permission presupposes another created earlier and not necessarily propagated to existing repos
225 226 227 228 229 230 231 232 |
# File 'backend/app/lib/rest.rb', line 225 def () = true += @preconditions << proc { |request| .any? { || current_user.can?() } } self end |
#uri(uri) ⇒ Object
156 |
# File 'backend/app/lib/rest.rb', line 156 def uri(uri) @uri = uri; self end |
#use_transaction(val) ⇒ Object
295 296 297 298 299 |
# File 'backend/app/lib/rest.rb', line 295 def use_transaction(val) @use_transaction = val self end |