linked_rails 0.0.4.pre.g14b377f91 → 0.0.4.pre.g58e27be7f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad7cb510016bb7b42c2b472031bb0eebcd976cd93f0a20cfca9f70b87271a4c9
4
- data.tar.gz: 5965b0ffb3715273ed446f29d3544124145b06162a66c866f899731c85819acb
3
+ metadata.gz: 7b8ffa3a829f2c55d62661b0e4e619ee8e923a68a74bc4a48e6e052876ac61af
4
+ data.tar.gz: c32f19ca0b17a94237e717f04f0b277c5d21fde64e24e0a20ade4e91dca8b0c4
5
5
  SHA512:
6
- metadata.gz: 7f82190e18c85082737f30ff5ed4b5ef1e13cf90b5a3b2c8941589a8812eb79dbf34948005a28895c6aa12884134c66e869258374759506bb7b1642829072773
7
- data.tar.gz: 3abb5a9fe9fedde3040d55d89f2a3af74d13e97f93b07dca126a6dcbebcfd27309cee9548b0b95b09dec84cdc6519acd6f44c8e11a4d978e47eca82c4ee88cca
6
+ metadata.gz: b9d423962871a075e287a5c74e4d71c2e111a974efcc64362133b759c9e9decba9427c43c559311191ba3b1f6b76af4e0971e279540f74ffdfae43f2670c88b8
7
+ data.tar.gz: 755f741653de9d6c2889003df66ec0a43649c5bcb03365ef624b84758bbd44b33954605e88d1fed119ab6277e4a4b6744d840556075f4e70e576f153c6c41c8c
@@ -21,8 +21,11 @@ module LinkedRails
21
21
  return response_for_wrong_host(opts) if wrong_host?(opts[:iri])
22
22
 
23
23
  include = opts[:include].to_s == 'true'
24
+ resource = LinkedRails.iri_mapper.resource_from_iri(request_path_to_url(opts[:iri]), user_context)
24
25
 
25
- response_from_request(include, RDF::URI(opts[:iri]))
26
+ return response_from_request(include, RDF::URI(opts[:iri])) if resource.blank?
27
+
28
+ response_from_resource(include, opts[:iri], resource)
26
29
  rescue StandardError => e
27
30
  handle_resource_error(opts, e)
28
31
  end
@@ -84,10 +87,19 @@ module LinkedRails
84
87
  false
85
88
  end
86
89
 
90
+ def resource_cache_control(cacheable, status, resource_policy)
91
+ return :private unless status == 200 && cacheable
92
+ return 'no-cache' unless resource_policy.try(:public_resource?)
93
+
94
+ :public
95
+ end
96
+
87
97
  def resource_params(param)
88
98
  params = param.permit(:include, :iri)
89
99
  params[:iri] = URI(params[:iri])
90
100
  params
101
+ rescue URI::InvalidURIError
102
+ params.except(:iri)
91
103
  end
92
104
 
93
105
  def resource_response_body(iri, rack_body, status)
@@ -131,6 +143,12 @@ module LinkedRails
131
143
  }.merge(opts)
132
144
  end
133
145
 
146
+ def resource_status(resource_policy)
147
+ raise(LinkedRails::Errors::Forbidden.new(query: :show?)) unless resource_policy.show?
148
+
149
+ 200
150
+ end
151
+
134
152
  def response_for_wrong_host(opts)
135
153
  iri = opts[:iri]
136
154
  term = term_from_vocab(iri)
@@ -139,6 +157,23 @@ module LinkedRails
139
157
  ontology_term_response(iri, term, opts[:include])
140
158
  end
141
159
 
160
+ def response_from_resource(include, iri, resource)
161
+ resource_policy = policy(resource)
162
+ status = resource_status(resource_policy)
163
+
164
+ resource_response(
165
+ iri,
166
+ body: response_from_resource_body(include, iri, resource, status),
167
+ cache: resource_cache_control(resource.try(:cacheable?), status, resource_policy),
168
+ language: I18n.locale,
169
+ status: status
170
+ )
171
+ end
172
+
173
+ def response_from_resource_body(include, _iri, resource, status)
174
+ include && status == 200 ? resource_body(resource) : nil
175
+ end
176
+
142
177
  def term_from_vocab(iri)
143
178
  vocab = Vocab.for(iri)
144
179
  tag = iri.to_s.split(vocab.to_s).last
@@ -216,7 +251,7 @@ module LinkedRails
216
251
 
217
252
  def sanitized_relative_path(iri) # rubocop:disable Metrics/AbcSize
218
253
  iri.path = "#{iri.path}/" unless iri.path&.ends_with?('/')
219
- uri = URI(LinkedRails.iri.path.present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
254
+ uri = URI(LinkedRails.iri.path.chomp('/').present? ? iri.to_s.split("#{LinkedRails.iri.path}/").last : iri)
220
255
 
221
256
  [uri.path, uri.query].compact.join('?')
222
257
  end
@@ -187,7 +187,7 @@ module LinkedRails
187
187
 
188
188
  def target_url_fallback # rubocop:disable Metrics/AbcSize
189
189
  base = (resource.try(:singular_resource?) ? resource.singular_iri : resource.iri).dup
190
- base.path = "#{base.path}/#{target_path}" if target_path.present?
190
+ base.path = "#{base.path.chomp('/')}/#{target_path}" if target_path.present?
191
191
  base.query = Rack::Utils.parse_nested_query(base.query).merge(target_query).to_param if target_query.present?
192
192
  base
193
193
  end
@@ -8,8 +8,9 @@ module LinkedRails
8
8
  include LinkedRails::Model
9
9
 
10
10
  attr_accessor :collection, :filter
11
- delegate :apply_scope, :association_base, :association_class, :default_page_size, :display, :include_members,
12
- :parent, :policy, :total_page_count, :unfiltered_collection, :user_context, to: :collection
11
+ delegate :apply_scope, :association_base, :association_class, :child_resource, :default_page_size, :display,
12
+ :include_members,:parent, :policy, :total_page_count, :unfiltered_collection, :user_context,
13
+ to: :collection
13
14
  delegate :count, to: :members
14
15
 
15
16
  alias id iri
@@ -5,6 +5,7 @@ require 'pundit'
5
5
  module LinkedRails
6
6
  class Form # rubocop:disable Metrics/ClassLength
7
7
  include LinkedRails::Model
8
+ include LinkedRails::Model::Cacheable
8
9
 
9
10
  class_attribute :abstract_form, :pages, :model_class
10
11
 
@@ -5,15 +5,11 @@ module LinkedRails
5
5
  include ActiveModel::Model
6
6
  include LinkedRails::Model
7
7
 
8
- MANIFEST_KEY = 'cache:Manifest'
9
- CACHE_DB = ENV.fetch('PERSISTENT_REDIS_DATABASE', '6')
10
-
11
8
  def save
12
- self.class.redis_client.hset(
13
- MANIFEST_KEY,
14
- {
15
- LinkedRails.iri.to_s => web_manifest.to_json
16
- }
9
+ Storage.hset(
10
+ :persistent,
11
+ :manifest,
12
+ URL.as_href(LinkedRails.iri) => web_manifest.to_json
17
13
  )
18
14
  end
19
15
 
@@ -45,7 +41,7 @@ module LinkedRails
45
41
  def blob_preview_iri
46
42
  return unless ActiveStorage::Blob.service.present?
47
43
 
48
- LinkedRails.iri(path: 'rails/active_storage/blobs/redirect/{signed_id}/preview')
44
+ "#{LinkedRails.iri(path: 'rails/active_storage/blobs/redirect')}/{signed_id}/preview')"
49
45
  end
50
46
 
51
47
  def blob_upload_iri
@@ -142,7 +138,7 @@ module LinkedRails
142
138
 
143
139
  def web_manifest_sw_section
144
140
  {
145
- src: "#{scope}/sw.js",
141
+ src: "#{scope.chomp('/')}/sw.js",
146
142
  scope: scope
147
143
  }
148
144
  end
@@ -165,19 +161,21 @@ module LinkedRails
165
161
 
166
162
  class << self
167
163
  def destroy(iri)
168
- redis_client.hdel(MANIFEST_KEY, iri)
164
+ Storage.hdel(:persistent, :manifest, URL.as_href(iri))
169
165
  end
170
166
 
171
167
  def move(from, to)
172
- data = redis_client.hget(MANIFEST_KEY, from)
168
+ Storage.hset(
169
+ :persistent,
170
+ :redirect_prefix,
171
+ URL.as_href(from) => URL.as_href(to)
172
+ )
173
173
 
174
- redis_client.hset(MANIFEST_KEY, to, data) if data
174
+ data = Storage.hget(:persistent, :manifest, URL.as_href(from))
175
175
 
176
- destroy(from)
177
- end
176
+ Storage.hset(:persistent, :manifest, URL.as_href(to), data) if data
178
177
 
179
- def redis_client
180
- @redis_client = Redis.new(db: CACHE_DB)
178
+ destroy(from)
181
179
  end
182
180
  end
183
181
  end
@@ -26,7 +26,7 @@ module LinkedRails
26
26
  end
27
27
 
28
28
  def menus
29
- @menus ||= available_menus.map(&method(:memoised_menu_item))
29
+ @menus ||= available_menus.map(&method(:memoized_menu_item))
30
30
  end
31
31
 
32
32
  def menu(tag)
@@ -4,6 +4,7 @@ module LinkedRails
4
4
  class Ontology
5
5
  include ActiveModel::Model
6
6
  include LinkedRails::Model
7
+ include LinkedRails::Model::Cacheable
7
8
 
8
9
  def classes
9
10
  @classes ||= LinkedRails.linked_models.map do |klass|
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class FormPolicy < LinkedRails.policy_parent_class
5
+ def show?
6
+ true
7
+ end
8
+
9
+ def public_resource?
10
+ true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class OntologyPolicy < LinkedRails.policy_parent_class
5
+ def show?
6
+ true
7
+ end
8
+
9
+ def public_resource?
10
+ true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ class InvalidationStreamWorker < ActiveJob::Base
5
+ def perform(type, iri, resource_type)
6
+ entry = {
7
+ type: type,
8
+ resource: iri,
9
+ resourceType: resource_type
10
+ }
11
+ id = Storage.xadd(:stream, LinkedRails.cache_stream, entry)
12
+
13
+ raise('No message id returned, implies failure') if id.blank?
14
+ end
15
+ end
16
+ end
@@ -11,6 +11,8 @@ en:
11
11
  update:
12
12
  label: "Edit"
13
13
  success: 'Updated successfully'
14
+ errors:
15
+ access_denied: "You're not authorized for this action. (%{action})"
14
16
  linked_rails:
15
17
  status:
16
18
  400: "Error in request"
@@ -6,6 +6,10 @@ module LinkedRails
6
6
  extend ActiveSupport::Concern
7
7
  include ActiveSupport::Rescuable
8
8
 
9
+ included do
10
+ rescue_from LinkedRails::Errors::Forbidden, with: :handle_error
11
+ end
12
+
9
13
  private
10
14
 
11
15
  def add_error_snackbar(error)
@@ -69,6 +73,7 @@ module LinkedRails
69
73
  'Doorkeeper::Errors::InvalidGrantReuse' => 422,
70
74
  'LinkedRails::Auth::Errors::Expired' => 410,
71
75
  'LinkedRails::Auth::Errors::Unauthorized' => 401,
76
+ 'LinkedRails::Errors::Forbidden' => 403,
72
77
  'Pundit::NotAuthorizedError' => 403
73
78
  }
74
79
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Errors
5
+ class Forbidden < StandardError
6
+ attr_reader :query, :record, :policy, :action
7
+
8
+ # @param [Hash] options
9
+ # @option options [String] query The action of the request
10
+ # @option options [ActiveRecord::Base] record The record that was requested
11
+ # @option options [Policy] policy The policy that raised the exception
12
+ # @option options [String] message Override the default error message
13
+ # @return [String] the message
14
+ def initialize(**options)
15
+ @query = options.fetch(:query).to_s
16
+ @record = options[:record]
17
+ @policy = options[:policy]
18
+ @action = @query[-1] == '?' ? @query[0..-2] : @query
19
+ @message = options[:message]
20
+
21
+ raise StandardError if @query.blank? && @message.blank?
22
+
23
+ super(@message || default_message)
24
+ end
25
+
26
+ private
27
+
28
+ def default_message
29
+ I18n.t(
30
+ "pundit.#{@policy.class.to_s.underscore}.#{@query}",
31
+ action: @action,
32
+ default: I18n.t('errors.access_denied')
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors/forbidden'
@@ -46,12 +46,22 @@ module LinkedRails
46
46
 
47
47
  private
48
48
 
49
+ def request_path_to_url(path)
50
+ return path unless path.present? && URI(path).relative?
51
+
52
+ port = [80, 443].include?(request.port) ? nil : request.port
53
+ URI::Generic.new(request.scheme, nil, request.host, port, nil, path, nil, nil, nil).to_s
54
+ end
55
+
49
56
  def build_new_resource
50
57
  controller_class.build_new(user_context: user_context)
51
58
  end
52
59
 
53
60
  def new_resource_from_parent
54
- return requested_resource.child_resource if requested_resource.is_a?(Collection)
61
+ if requested_resource.is_a?(LinkedRails.collection_class) ||
62
+ requested_resource.is_a?(LinkedRails.collection_view_class)
63
+ return requested_resource.child_resource
64
+ end
55
65
 
56
66
  parent_resource.build_child(
57
67
  controller_class,
@@ -38,7 +38,7 @@ module LinkedRails
38
38
  params = Rails.application.routes.recognize_path(iri.to_s, method: method)
39
39
 
40
40
  route_params_to_opts(params.merge(query), iri.to_s)
41
- rescue ActionController::RoutingError
41
+ rescue ActionController::RoutingError, SystemStackError
42
42
  EMPTY_IRI_OPTS.dup
43
43
  end
44
44
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LinkedRails
4
+ module Model
5
+ module Cacheable
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ if respond_to?(:after_commit)
10
+ after_commit :publish_create, on: :create, if: :should_publish_changes
11
+ after_commit :publish_update, on: :update, if: :should_publish_changes
12
+ after_commit :publish_delete, on: :destroy, if: :should_publish_changes
13
+ end
14
+ end
15
+
16
+ def cacheable?
17
+ true
18
+ end
19
+
20
+ def publish_create
21
+ publish_message('io.ontola.transactions.Created')
22
+ end
23
+
24
+ def publish_update
25
+ publish_message('io.ontola.transactions.Updated')
26
+ end
27
+
28
+ def publish_delete
29
+ publish_message('io.ontola.transactions.Deleted')
30
+ end
31
+
32
+ private
33
+
34
+ def publish_message(type)
35
+ LinkedRails::InvalidationStreamWorker.perform_now(type, iri.to_s, self.class.iri.to_s)
36
+ rescue StandardError
37
+ LinkedRails::InvalidationStreamWorker.perform_later(type, iri.to_s, self.class.iri.to_s)
38
+ end
39
+
40
+ def should_publish_changes
41
+ cacheable? && !Rails.env.test?
42
+ end
43
+ end
44
+ end
45
+ end
@@ -62,6 +62,7 @@ module LinkedRails
62
62
  iri = root_relative_iri.dup
63
63
  iri.scheme = LinkedRails.scheme
64
64
  iri.host = LinkedRails.host
65
+ iri.path = iri.path.presence || '/'
65
66
  iri
66
67
  end
67
68
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'model/actionable'
4
+ require_relative 'model/cacheable'
4
5
  require_relative 'model/collections'
5
6
  require_relative 'model/dirty'
6
7
  require_relative 'model/enhancements'
@@ -46,6 +46,10 @@ module LinkedRails
46
46
  self.class.policy_class
47
47
  end
48
48
 
49
+ def public_resource?
50
+ false
51
+ end
52
+
49
53
  def show?
50
54
  false
51
55
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+
5
+ module LinkedRails
6
+ class Storage
7
+ REDIS_DB = {
8
+ cache: LinkedRails.cache_redis_database,
9
+ persistent: LinkedRails.persistent_redis_database,
10
+ stream: LinkedRails.stream_redis_database
11
+ }.freeze
12
+ KEYS = {
13
+ manifest: 'cache:Manifest',
14
+ redirect_exact: 'cache:Redirect:Exact',
15
+ redirect_prefix: 'cache:Redirect:Prefix'
16
+ }.freeze
17
+
18
+ class << self
19
+ %i[xadd].each do |method|
20
+ define_method(method) do |db, *args|
21
+ Redis.new(db: REDIS_DB.fetch(db)).send(method, *args)
22
+ end
23
+ end
24
+
25
+ %i[hset hdel hget].each do |method|
26
+ define_method(method) do |db, key, *args|
27
+ Redis.new(db: REDIS_DB.fetch(db)).send(method, KEYS.fetch(key), *args)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module LinkedRails
2
+ class URL
3
+ class << self
4
+ def as_href(url)
5
+ uri = URI(url)
6
+ uri.path = uri.path.presence || '/'
7
+ uri.to_s
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/linked_rails.rb CHANGED
@@ -22,6 +22,10 @@ module LinkedRails
22
22
 
23
23
  mattr_accessor :whitelisted_spi_ips
24
24
  mattr_writer :host, :scheme
25
+ mattr_accessor :persistent_redis_database, default: ENV['PERSISTENT_REDIS_DATABASE'].presence || 6
26
+ mattr_accessor :stream_redis_database, default: ENV['STREAM_REDIS_DATABASE'].presence || 7
27
+ mattr_accessor :cache_redis_database, default: ENV['CACHE_REDIS_DATABASE'].presence || 8
28
+ mattr_accessor :cache_stream, default: ENV['CACHE_STREAM'].presence || 'transactions'
25
29
 
26
30
  def self.configurable_class(parent, klass, default: nil, reader: nil) # rubocop:disable Metrics/AbcSize
27
31
  method = :"#{[parent, klass.to_s.downcase].compact.join('_')}_class"
@@ -50,8 +54,10 @@ module LinkedRails
50
54
  @@scheme ||= Rails.application.routes.default_url_options[:protocol] || :http # rubocop:disable Style/ClassVars
51
55
  end
52
56
 
53
- def iri(**opts)
54
- RDF::URI.new(**{scheme: LinkedRails.scheme, host: LinkedRails.host}.merge(opts))
57
+ def iri(**args)
58
+ opts = {scheme: LinkedRails.scheme, host: LinkedRails.host}.merge(args)
59
+ opts[:path] = opts[:path].presence || '/'
60
+ RDF::URI.new(**opts)
55
61
  end
56
62
  end
57
63
 
@@ -84,6 +90,7 @@ ActiveSupport::Inflector.inflections do |inflect|
84
90
  inflect.acronym 'SHACL'
85
91
  end
86
92
 
93
+ require 'linked_rails/errors'
87
94
  require 'linked_rails/uri_template'
88
95
  require 'linked_rails/vocab'
89
96
  require 'linked_rails/cache'
@@ -101,3 +108,5 @@ require 'linked_rails/routes'
101
108
  require 'linked_rails/serializer'
102
109
  require 'linked_rails/translate'
103
110
  require 'linked_rails/railtie'
111
+ require 'linked_rails/url'
112
+ require 'linked_rails/storage'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linked_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4.pre.g14b377f91
4
+ version: 0.0.4.pre.g58e27be7f
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arthur Dingemans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-08 00:00:00.000000000 Z
11
+ date: 2022-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_response
@@ -299,8 +299,10 @@ files:
299
299
  - app/policies/linked_rails/collection/view_policy.rb
300
300
  - app/policies/linked_rails/collection_policy.rb
301
301
  - app/policies/linked_rails/enum_value_policy.rb
302
+ - app/policies/linked_rails/form_policy.rb
302
303
  - app/policies/linked_rails/menus/item_policy.rb
303
304
  - app/policies/linked_rails/menus/list_policy.rb
305
+ - app/policies/linked_rails/ontology_policy.rb
304
306
  - app/policies/linked_rails/sequence_policy.rb
305
307
  - app/serializers/linked_rails/actions/item_serializer.rb
306
308
  - app/serializers/linked_rails/actions/object_serializer.rb
@@ -338,6 +340,7 @@ files:
338
340
  - app/serializers/linked_rails/web_page_serializer.rb
339
341
  - app/serializers/linked_rails/web_site_serializer.rb
340
342
  - app/serializers/linked_rails/widget_serializer.rb
343
+ - app/workers/linked_rails/invalidation_stream_worker.rb
341
344
  - config/initializers/inflections.rb
342
345
  - lib/generators/linked_rails/install/install_generator.rb
343
346
  - lib/generators/linked_rails/install/templates/README
@@ -387,6 +390,8 @@ files:
387
390
  - lib/linked_rails/enhancements/destroyable/controller.rb
388
391
  - lib/linked_rails/enhancements/updatable/controller.rb
389
392
  - lib/linked_rails/enhancements/updatable/serializer.rb
393
+ - lib/linked_rails/errors.rb
394
+ - lib/linked_rails/errors/forbidden.rb
390
395
  - lib/linked_rails/helpers/delta_helper.rb
391
396
  - lib/linked_rails/helpers/ontola_actions_helper.rb
392
397
  - lib/linked_rails/helpers/resource_helper.rb
@@ -395,6 +400,7 @@ files:
395
400
  - lib/linked_rails/middleware/linked_data_params.rb
396
401
  - lib/linked_rails/model.rb
397
402
  - lib/linked_rails/model/actionable.rb
403
+ - lib/linked_rails/model/cacheable.rb
398
404
  - lib/linked_rails/model/collections.rb
399
405
  - lib/linked_rails/model/dirty.rb
400
406
  - lib/linked_rails/model/enhancements.rb
@@ -418,10 +424,12 @@ files:
418
424
  - lib/linked_rails/serializer/actionable.rb
419
425
  - lib/linked_rails/serializer/menuable.rb
420
426
  - lib/linked_rails/serializer/singularable.rb
427
+ - lib/linked_rails/storage.rb
421
428
  - lib/linked_rails/test_methods.rb
422
429
  - lib/linked_rails/translate.rb
423
430
  - lib/linked_rails/types/iri_type.rb
424
431
  - lib/linked_rails/uri_template.rb
432
+ - lib/linked_rails/url.rb
425
433
  - lib/linked_rails/version.rb
426
434
  - lib/linked_rails/vocab.rb
427
435
  - lib/nill_class_renderer.rb