isomorfeus-data 2.5.5 → 22.9.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73175d7dc425cc54e235ee9719bacaaebe93b60753794949506cb044d05a5599
4
- data.tar.gz: 1af87ebf47618289399a3cdf7539e533577f09ae0757cae9e5f7b873e3f05b59
3
+ metadata.gz: 87cf7029a092f0aaf3f834a6b87a2f4650883b79c4f4ee64bbf6aaff64f076c2
4
+ data.tar.gz: 1a1f38dfcf62726729633ed055f5dfc2026bd75498720a4cd83109bbcd67d5f2
5
5
  SHA512:
6
- metadata.gz: 4fe59f656951f87f9382f4dfd2b86b00ba835644e69b729d51cc1eef6dbec9ca13a05e683d655cb085e536d1f13d0e738c42b15309e4ba681102e1d54bbca5ad
7
- data.tar.gz: c2403b92cb3c88e1f38d05270afef98610694d8edd8cfca274735e4b456cfae2d2902db9512737dbd7316427dd7579d8f2275c691e64d6ac31e4c77affb725c4
6
+ metadata.gz: 9595255f9ed1739bcc8b67d2541d685ce1588285c4b5efc06c87ef781311c05e6b2f78f79d2fbe449bcb8e465e5209480b97fef3439c8d5162a22120020a8b90
7
+ data.tar.gz: 6d28028f95ee4d1f7e9f733698cd2f7e4baeab3dc4df516c30c5f9433956b496ac1f63abd7d85a1be5c939861b525be67164982a70087c3a52377811abd8cbe8
@@ -1,12 +1,9 @@
1
1
  module Isomorfeus
2
2
  # available settings
3
- class << self
4
3
  if RUBY_ENGINE == 'opal'
5
- def instance_from_sid(sid)
6
- data_class = cached_data_class(sid[0])
7
- data_class.new(key: sid[1], _loading: true)
8
- end
4
+ add_client_option(:api_files_path)
9
5
 
6
+ class << self
10
7
  def cached_data_classes
11
8
  @cached_data_classes ||= `{}`
12
9
  end
@@ -16,12 +13,9 @@ module Isomorfeus
16
13
  return cached_data_classes.JS[class_name] if cached_data_classes.JS[class_name]
17
14
  cached_data_classes.JS[class_name] = "::#{class_name}".constantize
18
15
  end
19
- else
20
- def instance_from_sid(sid)
21
- data_class = cached_data_class(sid[0])
22
- data_class.new(key: sid[1])
23
- end
24
-
16
+ end
17
+ else
18
+ class << self
25
19
  def cached_data_classes
26
20
  @cached_data_classes ||= {}
27
21
  end
@@ -44,18 +38,6 @@ module Isomorfeus
44
38
  valid_data_classes[raw_class_name(klass)] = true
45
39
  end
46
40
 
47
- def valid_file_classes
48
- @valid_file_classes ||= {}
49
- end
50
-
51
- def valid_file_class_name?(class_name)
52
- valid_file_classes.key?(class_name)
53
- end
54
-
55
- def add_valid_file_class(klass)
56
- valid_file_classes[raw_class_name(klass)] = true
57
- end
58
-
59
41
  attr_accessor :storage_path
60
42
  attr_accessor :files_path
61
43
 
@@ -64,10 +46,9 @@ module Isomorfeus
64
46
 
65
47
  attr_accessor :data_object_envs_path
66
48
  attr_accessor :data_object_idxs_path
49
+ attr_accessor :api_files_path
67
50
  end
68
- end
69
51
 
70
- if RUBY_ENGINE != 'opal'
71
52
  self.storage_path = File.expand_path(File.join(Isomorfeus.root, 'storage', Isomorfeus.env))
72
53
  self.files_path = File.expand_path(File.join(self.storage_path, 'files'))
73
54
 
@@ -75,4 +56,6 @@ module Isomorfeus
75
56
  self.ferret_path = File.expand_path(File.join(self.storage_path, 'ferret'))
76
57
  self.data_documents_path = File.expand_path(File.join(self.ferret_path, 'documents'))
77
58
  end
59
+
60
+ self.api_files_path = '/files/'
78
61
  end
@@ -0,0 +1,291 @@
1
+ module Isomorfeus
2
+ module Data
3
+ module Enhancer
4
+ if RUBY_ENGINE == 'opal'
5
+ OBJECT_ACTION_TYPES = %w[DATA_LOAD DATA_SAVE DATA_CREATE DATA_SEARCH DATA_DESTROY]
6
+ OBJECT_MERGE_TYPES = %w[DATA_SAVE DATA_CREATE]
7
+
8
+ CLIENT_OBJECT_ENHANCER = proc do |action, next_enh|
9
+ if OBJECT_ACTION_TYPES.include?(action[:type])
10
+ Isomorfeus.store.promise_action(action)
11
+ if OBJECT_MERGE_TYPES.include?(action[:type])
12
+ action = { type: 'DATA_MERGE', data: { action[:class_name] => { action[:key] => { fields: action[:fields], revision: action[:revision] }}}} # optimistic
13
+ end
14
+ end
15
+ next_enh.call(action)
16
+ end
17
+
18
+ FILE_ACTION_TYPES = %w[FILE_LOAD FILE_SAVE FILE_CREATE FILE_DESTROY]
19
+ FILE_MERGE_TYPES = %w[FILE_SAVE FILE_CREATE]
20
+
21
+ CLIENT_FILE_ENHANCER = proc do |action, next_enh|
22
+ if FILE_ACTION_TYPES.include?(action[:type])
23
+ Isomorfeus.store.promise_action(action)
24
+ if FILE_MERGE_TYPES.include?(action[:type])
25
+ action = { type: 'DATA_MERGE', data: { action[:class_name] => { action[:key] => { data_uri: action[:data_uri], revision: action[:revision] }}}} # optimistic
26
+ end
27
+ end
28
+ next_enh.call(action)
29
+ end
30
+
31
+ def self.add_enhancer_to_store
32
+ ::Redux::Store.add_middleware(CLIENT_OBJECT_ENHANCER)
33
+ ::Redux::Store.add_middleware(CLIENT_FILE_ENHANCER)
34
+ end
35
+ else # RUBY_ENGINE
36
+ SERVER_OBJECT_ENHANCER = proc do |action, next_enh|
37
+ action_type = action[:type]
38
+ begin
39
+ if action_type == 'DATA_LOAD'
40
+ item_class_name = action[:class_name]
41
+ item_key = action[:key]
42
+ item_class = get_verified_class(item_class_name)
43
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :load, key: item_key)
44
+ sid_s = SID.new(item_class_name, item_key).to_s
45
+ loaded_data = item_class.object_accelerator.load(sid_s: sid_s) || {}
46
+ revision = loaded_data.delete(:_revision)
47
+ item_class.field_conditions.each do |field, options|
48
+ loaded_data.delete(field) if options[:server_only]
49
+ end
50
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => { fields: loaded_data, revision: revision }}}}
51
+ elsif action_type == 'DATA_SAVE'
52
+ item_class_name = action[:class_name]
53
+ item_key = action[:key]
54
+ item_class = get_verified_class(item_class_name)
55
+ item_fields = action[:fields]
56
+ item_fields.transform_keys!(&:to_sym)
57
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :save, { key: item_key, fields: item_fields })
58
+ sid_s = SID.new(item_class_name, item_key).to_s
59
+ stored_revision = item_class.object_accelerator.revision(sid_s: sid_s)
60
+ if stored_revision == action[:revision]
61
+ revision = stored_revision + 1
62
+ instance_uuid = action[:instance_uuid]
63
+ if item_class.before_save_block
64
+ res = item_class.before_save_block.call(key: item_key, fields: item_fields)
65
+ if res.is_a?(Hash)
66
+ item_fields = res
67
+ item_fields.transform_keys!(&:to_sym)
68
+ convert_sids(item_class, item_fields)
69
+ end
70
+ end
71
+ if item_class.object_accelerator.save(sid_s: sid_s, fields: item_fields, revision: revision)
72
+ item_class.after_save_block.call(key: item_key, fields: item_fields) if item_class.after_save_block
73
+ else
74
+ instance_uuid = nil
75
+ end
76
+ else
77
+ revision = 0
78
+ instance_uuid = nil
79
+ end
80
+ loaded_data = item_class.object_accelerator.load(sid_s: sid_s)
81
+ revision = loaded_data.delete(:_revision)
82
+ item_class.field_conditions.each do |field, options|
83
+ loaded_data.delete(field) if options[:server_only]
84
+ end
85
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => { fields: loaded_data, revision: revision, instance_uuid: instance_uuid }}}}
86
+ elsif action_type == 'DATA_CREATE'
87
+ item_class_name = action[:class_name]
88
+ item_key = action[:key]
89
+ item_class = get_verified_class(item_class_name)
90
+ item_fields = action[:fields]
91
+ item_fields.transform_keys!(&:to_sym)
92
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :create, { key: item_key, fields: item_fields })
93
+ sid_s = SID.new(item_class_name, item_key).to_s
94
+ stored_revision = item_class.object_accelerator.revision(sid_s: sid_s)
95
+ if stored_revision.nil?
96
+ revision = 1
97
+ instance_uuid = action[:instance_uuid]
98
+ if item_class.before_create_block
99
+ res = item_class.before_create_block.call(key: item_key, fields: item_fields)
100
+ if res.is_a?(Hash)
101
+ item_fields = res
102
+ item_fields.transform_keys!(&:to_sym)
103
+ convert_sids(item_class, item_fields)
104
+ end
105
+ end
106
+ if item_class.object_accelerator.create(sid_s: sid_s, fields: item_fields)
107
+ item_class.after_create_block.call(key: item_key, fields: item_fields) if item_class.after_create_block
108
+ else
109
+ stored_revision = 0
110
+ instance_uuid = nil
111
+ end
112
+ else
113
+ revision = stored_revision
114
+ instance_uuid = nil
115
+ end
116
+ loaded_data = item_class.object_accelerator.load(sid_s: sid_s)
117
+ revision = loaded_data.delete(:_revision)
118
+ item_class.field_conditions.each do |field, options|
119
+ loaded_data.delete(field) if options[:server_only]
120
+ end
121
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => { fields: loaded_data, revision: revision, instance_uuid: instance_uuid }}}}
122
+ elsif action_type == 'DATA_SEARCH'
123
+ item_class_name = action[:class_name]
124
+ item_class = get_verified_class(item_class_name)
125
+ query = action[:query].to_sym
126
+ params = action[:params]
127
+ raise "Invalid params #{params}!" unless params.nil? || params.is_a?(Hash)
128
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :search, { query: query, params: params })
129
+ result = []
130
+ query_string = item_class.queries.dig(query, :query_string)
131
+ raise "Invalid query '#{query}'!" unless query_string
132
+ options = item_class.queries[query][:options] || {}
133
+ oa = item_class.object_accelerator
134
+ full_query = if params.is_a?(Hash)
135
+ if options[:escape_params]
136
+ escaped_params = params.to_h do |k,v|
137
+ [k, oa.escape(v)]
138
+ end
139
+ query_string % escaped_params
140
+ else
141
+ query_string % params
142
+ end
143
+ else
144
+ query_string
145
+ end
146
+ oa.search_each(full_query, options) do |id|
147
+ sid_s = oa.load_sid_s(id: id)
148
+ result << sid_s if sid_s
149
+ end
150
+ action = { type: 'DATA_MERGE', data: { queries: { item_class_name => { query => { Oj.dump(params, mode: :strict) => result }}}}}
151
+ elsif action_type == 'DATA_DESTROY'
152
+ item_class_name = action[:class_name]
153
+ item_key = action[:key]
154
+ item_class = get_verified_class(item_class_name)
155
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :destroy, key: item_key)
156
+ item_class.before_destroy_block.call(key: item_key) if item_class.before_destroy_block
157
+ item_class.object_accelerator.destroy(sid_s: SID.new(item_class_name, item_key).to_s)
158
+ item_class.after_destroy_block.call(key: item_key) if item_class.after_destroy_block
159
+ action = { type: 'DATA_SET', class_name: item_class_name, key: item_key, data: { revision: -1 }}
160
+ end
161
+ rescue => e
162
+ STDERR.puts "#{e.message}\n#{e.backtrace&.join("\n")}"
163
+ # do nothing
164
+ end
165
+ next_enh.call(action)
166
+ end
167
+
168
+ SERVER_FILE_ENHANCER = proc do |action, next_enh|
169
+ action_type = action[:type]
170
+ begin
171
+ if action_type == 'FILE_LOAD'
172
+ item_class_name = action[:class_name]
173
+ item_key = action[:key]
174
+ item_class = get_verified_class(item_class_name)
175
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :load, key: item_key)
176
+ sid = SID.new(item_class_name, item_key)
177
+ data, meta = item_class.file_accelerator.load(sid: sid)
178
+ item_hash = {}
179
+ if data
180
+ content_type = Marcel::MimeType.for(data)
181
+ data_uri = item_class.format_data_uri(content_type, data)
182
+ revision = meta[:revision]
183
+ item_hash = { data_uri: data_uri, revision: revision }
184
+ end
185
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => item_hash }}}
186
+ elsif action_type == 'FILE_SAVE'
187
+ item_class_name = action[:class_name]
188
+ item_key = action[:key]
189
+ item_class = get_verified_class(item_class_name)
190
+ data_uri = action[:data_uri]
191
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :save, { key: item_key, data_uri: data_uri })
192
+ revision = action[:revision]
193
+ sid = SID.new(item_class_name, item_key)
194
+ meta = item_class.file_accelerator.meta(sid: sid)
195
+ stored_revision = meta[:revision]
196
+ if stored_revision == action[:revision]
197
+ revision = stored_revision + 1
198
+ instance_uuid = action[:instance_uuid]
199
+ if item_class.before_save_block
200
+ res = item_class.before_save_block.call(key: item_key, data_uri: data_uri)
201
+ data_uri = res[:data_uri] if res.is_a?(Hash) && res.key?(:data_uri)
202
+ end
203
+ if item_class.file_accelerator.save(sid: sid, data: URI::Data.new(data_uri).data, meta: { revision: revision })
204
+ item_class.after_save_block.call(key: item_key, data_uri: data_uri) if item_class.after_save_block
205
+ else
206
+ instance_uuid = nil
207
+ end
208
+ else
209
+ revision = 0
210
+ instance_uuid = nil
211
+ end
212
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => { data_uri: data_uri, revision: revision, instance_uuid: instance_uuid }}}}
213
+ elsif action_type == 'FILE_CREATE'
214
+ item_class_name = action[:class_name]
215
+ item_key = action[:key]
216
+ item_class = get_verified_class(item_class_name)
217
+ data_uri = action[:data_uri]
218
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :create, { key: item_key, data_uri: data_uri })
219
+ sid = SID.new(item_class_name, item_key)
220
+ meta = item_class.file_accelerator.meta(sid: sid)
221
+ stored_revision = meta ? meta[:revision] : nil
222
+ if stored_revision.nil?
223
+ revision = 1
224
+ instance_uuid = action[:instance_uuid]
225
+ if item_class.before_create_block
226
+ res = item_class.before_create_block.call(key: item_key, data_uri: data_uri)
227
+ data_uri = res[:data_uri] if res.is_a?(Hash) && res.key?(:data_uri)
228
+ end
229
+ if item_class.file_accelerator.create(sid: sid, data: URI::Data.new(data_uri).data, meta: { revision: revision })
230
+ item_class.after_create_block.call(key: item_key, data_uri: data_uri) if item_class.after_create_block
231
+ else
232
+ stored_revision = 0
233
+ instance_uuid = nil
234
+ end
235
+ else
236
+ revision = stored_revision
237
+ instance_uuid = nil
238
+ end
239
+ action = { type: 'DATA_MERGE', data: { item_class_name => { item_key => { data_uri: data_uri, revision: revision, instance_uuid: instance_uuid }}}}
240
+ elsif action_type == 'FILE_DESTROY'
241
+ item_class_name = action[:class_name]
242
+ item_key = action[:key]
243
+ item_class = get_verified_class(item_class_name)
244
+ authorization_error!(action) unless Isomorfeus.current_user.authorized?(item_class, :destroy, key: item_key)
245
+ item_class.before_destroy_block.call(key: item_key) if item_class.before_destroy_block
246
+ item_class.file_accelerator.destroy(sid: SID.new(item_class_name, item_key))
247
+ item_class.after_destroy_block.call(key: item_key) if item_class.after_destroy_block
248
+ action = { type: 'DATA_SET', class_name: item_class_name, key: item_key, data: { revision: -1 }}
249
+ end
250
+ rescue => e
251
+ STDERR.puts "#{e.message}\n#{e.backtrace&.join("\n")}"
252
+ # do nothing
253
+ end
254
+ next_enh.call(action)
255
+ end
256
+
257
+ class << self
258
+ def convert_sids(item_class, item_fields)
259
+ item_class.field_types.each do |field, type|
260
+ if type == :object
261
+ field_s = field.to_s
262
+ v = item_fields[field_s]
263
+ if v.is_a?(SID)
264
+ item_fields[field_s] = v.to_s
265
+ elsif v.is_a?(Array)
266
+ item_fields[field_s].map! do |o|
267
+ o.is_a?(SID) ? o.to_s : o
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+ def get_verified_class(item_class_name)
275
+ raise "Invalid data class!" unless Isomorfeus.valid_data_class_name?(item_class_name)
276
+ item_class = Isomorfeus.cached_data_class(item_class_name)
277
+ end
278
+
279
+ def authorization_error!(action)
280
+ raise "Not authorized!#{" (action: #{action})" if Isomorfeus.development?}"
281
+ end
282
+
283
+ def add_enhancer_to_store
284
+ ::Redux::Store.add_middleware(SERVER_OBJECT_ENHANCER)
285
+ ::Redux::Store.add_middleware(SERVER_FILE_ENHANCER)
286
+ end
287
+ end
288
+ end # RUBY_ENGINE
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,95 @@
1
+ module Isomorfeus
2
+ module Data
3
+ class FileAccelerator
4
+ attr_reader :object_class, :object_class_name
5
+
6
+ def initialize(ruby_class)
7
+ @object_class = ruby_class
8
+ @object_class_name = ruby_class.name
9
+ @store_path = File.expand_path(File.join(Isomorfeus.files_path, "#{@object_class_name.underscore}"))
10
+ FileUtils.mkdir_p(@store_path) unless Dir.exist?(@store_path)
11
+ end
12
+
13
+ def serialize(o)
14
+ Oj.dump(o, mode: :strict)
15
+ end
16
+
17
+ def unserialize(s)
18
+ Oj.load(s, mode: :strict)
19
+ end
20
+
21
+ def destroy_store
22
+ close_store
23
+ FileUtils.rm_rf(@store_path)
24
+ end
25
+
26
+ def meta(sid:)
27
+ _path, meta_path = check_and_prepare_paths(key: sid.key)
28
+ if File.exist?(meta_path)
29
+ meta = unserialize(File.binread(meta_path))
30
+ meta.transform_keys!(&:to_sym)
31
+ end
32
+ end
33
+
34
+ def exist?(sid:)
35
+ path, meta_path = check_and_prepare_paths(key: sid.key)
36
+ File.exist?(path)
37
+ end
38
+
39
+ def create(sid:, data:, meta:)
40
+ path, meta_path = check_and_prepare_paths(key: sid.key)
41
+ File.binwrite(path, data)
42
+ File.binwrite(meta_path, serialize(meta))
43
+ true
44
+ end
45
+ alias save create
46
+
47
+ def destroy(sid:)
48
+ path, meta_path = check_and_prepare_paths(key: sid.key)
49
+ FileUtils.rm_f([path, meta_path])
50
+ true
51
+ end
52
+
53
+ def load(sid:)
54
+ path, meta_path = check_and_prepare_paths(key: sid.key)
55
+ begin
56
+ if File.exist?(meta_path)
57
+ meta = unserialize(File.binread(meta_path))
58
+ meta.transform_keys!(&:to_sym)
59
+ else
60
+ meta = {}
61
+ end
62
+ [File.binread(path), meta]
63
+ rescue Errno::ENOENT
64
+ [nil, nil]
65
+ end
66
+ end
67
+
68
+ def load_data(sid:)
69
+ path, _meta_path = check_and_prepare_paths(key: sid.key)
70
+ begin
71
+ File.binread(path)
72
+ rescue Errno::ENOENT
73
+ nil
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def check_and_prepare_paths(key:)
80
+ Isomorfeus.raise_error(message: 'Invalid key (contains ".." or "\\")') if key.include?('..') || key.include?('\\')
81
+ elements = key.split('/')
82
+ Isomorfeus.raise_error(message: 'Invalid key (contains more than 2 slashes "/")') if elements.size > 3
83
+ file = elements.pop
84
+ path = if elements.size > 0
85
+ dir_path = ::File.expand_path(::File.join(@store_path, *elements))
86
+ FileUtils.mkdir_p(dir_path) unless Dir.exist?(dir_path)
87
+ File.join(dir_path, file)
88
+ else
89
+ File.join(@store_path, file)
90
+ end
91
+ [path, "#{path}__meta__"]
92
+ end
93
+ end
94
+ end
95
+ end