safrano 0.4.1 → 0.4.6
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 +4 -4
- data/lib/core_ext/Dir/iter.rb +18 -0
- data/lib/core_ext/Hash/transform.rb +21 -0
- data/lib/core_ext/Integer/edm.rb +13 -0
- data/lib/core_ext/REXML/Document/output.rb +16 -0
- data/lib/core_ext/String/convert.rb +25 -0
- data/lib/core_ext/String/edm.rb +13 -0
- data/lib/core_ext/dir.rb +3 -0
- data/lib/core_ext/hash.rb +3 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/rexml.rb +3 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/odata/attribute.rb +15 -10
- data/lib/odata/batch.rb +15 -13
- data/lib/odata/collection.rb +144 -535
- data/lib/odata/collection_filter.rb +47 -40
- data/lib/odata/collection_media.rb +155 -99
- data/lib/odata/collection_order.rb +50 -37
- data/lib/odata/common_logger.rb +36 -34
- data/lib/odata/complex_type.rb +152 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +183 -216
- data/lib/odata/error.rb +195 -31
- data/lib/odata/expand.rb +126 -0
- data/lib/odata/filter/base.rb +74 -0
- data/lib/odata/filter/error.rb +49 -6
- data/lib/odata/filter/parse.rb +44 -36
- data/lib/odata/filter/sequel.rb +136 -67
- data/lib/odata/filter/sequel_function_adapter.rb +148 -0
- data/lib/odata/filter/token.rb +26 -19
- data/lib/odata/filter/tree.rb +113 -63
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +639 -0
- data/lib/odata/navigation_attribute.rb +44 -61
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +54 -0
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +128 -37
- data/lib/odata/walker.rb +20 -10
- data/lib/safrano.rb +17 -37
- data/lib/safrano/contract.rb +143 -0
- data/lib/safrano/core.rb +29 -104
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +39 -43
- data/lib/safrano/rack_app.rb +68 -67
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
- data/lib/safrano/request.rb +102 -51
- data/lib/safrano/response.rb +5 -3
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +274 -219
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +17 -29
- metadata +34 -11
@@ -1,61 +1,68 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require_relative 'filter/sequel.rb'
|
3
|
+
require 'odata/error'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
require_relative 'filter/parse'
|
6
|
+
require_relative 'filter/sequel'
|
7
|
+
|
8
|
+
# filter base class and subclass in our OData namespace
|
9
|
+
module Safrano
|
10
|
+
class FilterBase
|
11
|
+
# re-useable empty filtering (idempotent)
|
12
|
+
EmptyFilter = new.freeze
|
13
|
+
|
14
|
+
def self.factory(filterstr)
|
15
|
+
filterstr.nil? ? EmptyFilter : FilterByParse.new(filterstr)
|
16
|
+
end
|
17
|
+
|
18
|
+
def apply_to_dataset(dtcx)
|
19
|
+
Contract.valid(dtcx)
|
17
20
|
end
|
18
|
-
yield self
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
# finalize
|
23
|
+
def finalize(_jh) Contract::OK end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
true
|
23
27
|
end
|
24
|
-
end
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
repl = {}
|
29
|
-
tmpstr = gsub(MASK_RGX) do |_m|
|
30
|
-
cnt += 1
|
31
|
-
repl["$#{cnt}"] = Regexp.last_match(1)
|
32
|
-
"'$#{cnt}'"
|
29
|
+
def parse_error?
|
30
|
+
false
|
33
31
|
end
|
34
|
-
yield tmpstr
|
35
32
|
end
|
36
|
-
end
|
37
33
|
|
38
|
-
# filter base class and subclass in our OData namespace
|
39
|
-
module OData
|
40
34
|
# should handle everything by parsing
|
41
|
-
class FilterByParse
|
42
|
-
|
35
|
+
class FilterByParse < FilterBase
|
36
|
+
attr_reader :filterstr
|
37
|
+
|
38
|
+
def initialize(filterstr)
|
43
39
|
@filterstr = filterstr.dup
|
44
|
-
@ast =
|
45
|
-
@jh = jh
|
40
|
+
@ast = Safrano::Filter::Parser.new(@filterstr).build
|
46
41
|
end
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
# this build's up the Sequel Filter Expression, and as a side effect,
|
44
|
+
# it also finalizes the join helper that we need for the start dataset join
|
45
|
+
# the join-helper is shared by the order-by object and was potentially already
|
46
|
+
# partly built on order-by object creation.
|
47
|
+
def finalize(jh)
|
48
|
+
@filtexpr = @ast.if_valid { |ast| ast.sequel_expr(jh) }
|
51
49
|
end
|
52
50
|
|
53
|
-
def
|
54
|
-
@
|
51
|
+
def apply_to_dataset(dtcx)
|
52
|
+
# normally finalize is called before, and thus @filtexpr is set
|
53
|
+
@filtexpr.map_result! { |f| dtcx.where(f) }
|
55
54
|
end
|
56
55
|
|
56
|
+
# Note: this is really only *parse* error, ie the error encounterd while
|
57
|
+
# trying to build the AST
|
58
|
+
# Later when evaluating the AST, there can be other errors, they shall
|
59
|
+
# be tracked with @error
|
57
60
|
def parse_error?
|
58
|
-
@ast.
|
61
|
+
@ast.error
|
62
|
+
end
|
63
|
+
|
64
|
+
def empty?
|
65
|
+
false
|
59
66
|
end
|
60
67
|
end
|
61
68
|
end
|
@@ -1,29 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack'
|
2
4
|
require 'fileutils'
|
3
|
-
require_relative './navigation_attribute
|
5
|
+
require_relative './navigation_attribute'
|
4
6
|
|
5
|
-
module
|
7
|
+
module Safrano
|
6
8
|
module Media
|
7
9
|
# base class for Media Handler
|
8
10
|
class Handler
|
11
|
+
def check_before_create(data:,
|
12
|
+
entity:,
|
13
|
+
filename:)
|
14
|
+
Contract::OK
|
15
|
+
end
|
9
16
|
end
|
10
17
|
|
11
18
|
# Simple static File/Directory based media store handler
|
12
19
|
# similar to Rack::Static
|
13
20
|
# with a flat directory structure
|
14
21
|
class Static < Handler
|
15
|
-
|
16
|
-
def initialize(root: nil)
|
22
|
+
def initialize(root: nil, mediaklass:)
|
17
23
|
@root = File.absolute_path(root || Dir.pwd)
|
18
24
|
@file_server = ::Rack::File.new(@root)
|
25
|
+
@media_class = mediaklass
|
26
|
+
@media_dir_name = mediaklass.to_s
|
27
|
+
register
|
28
|
+
end
|
29
|
+
|
30
|
+
def register
|
31
|
+
@abs_klass_dir = File.absolute_path(@media_dir_name, @root)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_abs_class_dir
|
35
|
+
FileUtils.makedirs @abs_klass_dir unless Dir.exist?(@abs_klass_dir)
|
19
36
|
end
|
20
37
|
|
21
|
-
|
22
|
-
|
23
|
-
abs_klass_dir = File.absolute_path(klass.type_name, @root)
|
24
|
-
FileUtils.makedirs abs_klass_dir unless Dir.exists?(abs_klass_dir)
|
38
|
+
def finalize
|
39
|
+
create_abs_class_dir
|
25
40
|
end
|
26
|
-
|
41
|
+
|
27
42
|
# minimal working implementation...
|
28
43
|
# Note: @file_server works relative to @root directory
|
29
44
|
def odata_get(request:, entity:)
|
@@ -37,35 +52,33 @@ module OData
|
|
37
52
|
fsret
|
38
53
|
end
|
39
54
|
|
40
|
-
# TODO perf: this can be precalculated and cached on MediaModelKlass level
|
41
|
-
# and passed as argument to save_file
|
42
|
-
# eg. /@root/Photo
|
43
|
-
def abs_klass_dir(entity)
|
44
|
-
File.absolute_path(entity.klass_dir, @root)
|
45
|
-
end
|
46
|
-
|
47
55
|
# this is relative to @root
|
48
56
|
# eg. Photo/1
|
49
57
|
def media_path(entity)
|
50
|
-
File.join(
|
58
|
+
File.join(@media_dir_name, media_directory(entity))
|
51
59
|
end
|
52
|
-
|
60
|
+
|
53
61
|
# relative to @root
|
54
|
-
# eg Photo/1/
|
62
|
+
# eg Photo/1/1
|
55
63
|
def filename(entity)
|
56
64
|
Dir.chdir(abs_path(entity)) do
|
57
65
|
# simple design: one file per directory, and the directory
|
58
66
|
# contains the media entity-id --> implicit link between the media
|
59
67
|
# entity
|
60
|
-
File.join(media_path(entity), Dir.glob('*').
|
68
|
+
File.join(media_path(entity), Dir.glob('*').max)
|
61
69
|
end
|
62
70
|
end
|
63
|
-
|
71
|
+
|
64
72
|
# /@root/Photo/1
|
65
73
|
def abs_path(entity)
|
66
74
|
File.absolute_path(media_path(entity), @root)
|
67
75
|
end
|
68
76
|
|
77
|
+
# absolute filename
|
78
|
+
def abs_filename(entity)
|
79
|
+
File.absolute_path(filename(entity), @root)
|
80
|
+
end
|
81
|
+
|
69
82
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
70
83
|
# simplest implementation is media_directory = entity.media_path_id
|
71
84
|
# --> we get a 1 level depth flat directory structure
|
@@ -75,23 +88,23 @@ module OData
|
|
75
88
|
|
76
89
|
def in_media_directory(entity)
|
77
90
|
mpi = media_directory(entity)
|
78
|
-
Dir.mkdir mpi unless Dir.
|
91
|
+
Dir.mkdir mpi unless Dir.exist?(mpi)
|
79
92
|
Dir.chdir mpi do
|
80
93
|
yield
|
81
94
|
end
|
82
95
|
end
|
83
96
|
|
84
|
-
def odata_delete(
|
85
|
-
Dir.chdir(abs_klass_dir
|
97
|
+
def odata_delete(entity:)
|
98
|
+
Dir.chdir(@abs_klass_dir) do
|
86
99
|
in_media_directory(entity) do
|
87
100
|
Dir.glob('*').each { |oldf| File.delete(oldf) }
|
88
101
|
end
|
89
|
-
end
|
102
|
+
end
|
90
103
|
end
|
91
104
|
|
92
105
|
# Here as well, MVP implementation
|
93
106
|
def save_file(data:, filename:, entity:)
|
94
|
-
Dir.chdir(abs_klass_dir
|
107
|
+
Dir.chdir(@abs_klass_dir) do
|
95
108
|
in_media_directory(entity) do
|
96
109
|
filename = '1'
|
97
110
|
File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
@@ -103,19 +116,22 @@ module OData
|
|
103
116
|
# after each upload, so that clients get informed about new versions
|
104
117
|
# of the same media ressource
|
105
118
|
def ressource_version(entity)
|
106
|
-
Dir.chdir(abs_klass_dir
|
119
|
+
Dir.chdir(@abs_klass_dir) do
|
107
120
|
in_media_directory(entity) do
|
108
|
-
Dir.glob('*').
|
121
|
+
Dir.glob('*').max
|
109
122
|
end
|
110
|
-
end
|
123
|
+
end
|
111
124
|
end
|
112
|
-
|
125
|
+
|
113
126
|
# Here as well, MVP implementation
|
114
127
|
def replace_file(data:, filename:, entity:)
|
115
|
-
Dir.chdir(abs_klass_dir
|
128
|
+
Dir.chdir(@abs_klass_dir) do
|
116
129
|
in_media_directory(entity) do
|
117
130
|
version = nil
|
118
|
-
Dir.glob('*').each
|
131
|
+
Dir.glob('*').sort.each do |oldf|
|
132
|
+
version = oldf
|
133
|
+
File.delete(oldf)
|
134
|
+
end
|
119
135
|
filename = (version.to_i + 1).to_s
|
120
136
|
File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
121
137
|
end
|
@@ -125,107 +141,105 @@ module OData
|
|
125
141
|
# Simple static File/Directory based media store handler
|
126
142
|
# similar to Rack::Static
|
127
143
|
# with directory Tree structure
|
128
|
-
|
144
|
+
|
129
145
|
class StaticTree < Static
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
def
|
134
|
-
ids.map{|id| id.to_s.chars.join('/')}.join(SEP)
|
146
|
+
SEP = '/00/'.freeze
|
147
|
+
VERS = '/v'.freeze
|
148
|
+
|
149
|
+
def self.path_builder(ids)
|
150
|
+
ids.map { |id| id.to_s.chars.join('/') }.join(SEP) << VERS
|
135
151
|
end
|
136
|
-
|
152
|
+
|
137
153
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
138
154
|
# tree-structure
|
139
|
-
# media_path_ids = 1 --> 1
|
140
|
-
# media_path_ids = 15 --> 1/5
|
141
|
-
# media_path_ids = 555 --> 5/5/5
|
142
|
-
# media_path_ids = 5,5,5 --> 5/00/5/00/5
|
143
|
-
# media_path_ids = 5,00,5 --> 5/00/0/0/00/5
|
144
|
-
# media_path_ids = 5,xyz,5 --> 5/00/x/y/z/00/5
|
155
|
+
# media_path_ids = 1 --> 1/v
|
156
|
+
# media_path_ids = 15 --> 1/5/v
|
157
|
+
# media_path_ids = 555 --> 5/5/5/v
|
158
|
+
# media_path_ids = 5,5,5 --> 5/00/5/00/5/v
|
159
|
+
# media_path_ids = 5,00,5 --> 5/00/0/0/00/5/v
|
160
|
+
# media_path_ids = 5,xyz,5 --> 5/00/x/y/z/00/5/v
|
145
161
|
def media_directory(entity)
|
146
162
|
StaticTree.path_builder(entity.media_path_ids)
|
147
|
-
# entity.media_path_ids.map{|id| id.to_s.chars.join('/')}.join(@sep)
|
148
163
|
end
|
149
164
|
|
150
165
|
def in_media_directory(entity)
|
151
166
|
mpi = media_directory(entity)
|
152
|
-
FileUtils.makedirs mpi unless Dir.
|
153
|
-
Dir.chdir
|
154
|
-
yield
|
155
|
-
end
|
167
|
+
FileUtils.makedirs mpi unless Dir.exist?(mpi)
|
168
|
+
Dir.chdir(mpi) { yield }
|
156
169
|
end
|
157
170
|
|
158
|
-
def odata_delete(
|
159
|
-
Dir.chdir(abs_klass_dir
|
171
|
+
def odata_delete(entity:)
|
172
|
+
Dir.chdir(@abs_klass_dir) do
|
160
173
|
in_media_directory(entity) do
|
161
|
-
Dir.glob('*').each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
174
|
+
Dir.glob('*').sort.each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
162
175
|
end
|
163
|
-
end
|
176
|
+
end
|
164
177
|
end
|
165
|
-
|
178
|
+
|
179
|
+
# Here as well, MVP implementation
|
180
|
+
# def replace_file(data:, filename:, entity:)
|
181
|
+
# Dir.chdir(abs_klass_dir(entity)) do
|
182
|
+
# in_media_directory(entity) do
|
183
|
+
# Dir.glob('*').each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
184
|
+
# File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# end
|
166
188
|
# Here as well, MVP implementation
|
167
|
-
# def replace_file(data:, filename:, entity:)
|
168
|
-
# Dir.chdir(abs_klass_dir(entity)) do
|
169
|
-
# in_media_directory(entity) do
|
170
|
-
# Dir.glob('*').each { |oldf| File.delete(oldf) if File.file?(oldf) }
|
171
|
-
# File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
172
|
-
# end
|
173
|
-
# end
|
174
|
-
# end
|
175
|
-
# Here as well, MVP implementation
|
176
189
|
def replace_file(data:, filename:, entity:)
|
177
|
-
Dir.chdir(abs_klass_dir
|
190
|
+
Dir.chdir(@abs_klass_dir) do
|
178
191
|
in_media_directory(entity) do
|
179
192
|
version = nil
|
180
|
-
Dir.glob('*').each
|
193
|
+
Dir.glob('*').sort.each do |oldf|
|
194
|
+
version = oldf
|
195
|
+
File.delete(oldf)
|
196
|
+
end
|
181
197
|
filename = (version.to_i + 1).to_s
|
182
198
|
File.open(filename, 'wb') { |f| IO.copy_stream(data, f) }
|
183
199
|
end
|
184
200
|
end
|
185
201
|
end
|
186
|
-
|
187
202
|
end
|
188
|
-
|
189
203
|
end
|
190
204
|
|
191
205
|
# special handling for media entity
|
192
206
|
module EntityClassMedia
|
193
207
|
attr_reader :media_handler
|
194
208
|
attr_reader :slug_field
|
195
|
-
|
209
|
+
|
196
210
|
# API method for defining the media handler
|
197
211
|
# eg.
|
198
212
|
# publish_media_model photos do
|
199
|
-
# use
|
213
|
+
# use Safrano::Media::Static, :root => '/media_root'
|
200
214
|
# end
|
201
215
|
|
202
216
|
def set_default_media_handler
|
203
|
-
@media_handler =
|
217
|
+
@media_handler = Safrano::Media::Static.new(mediaklass: self)
|
218
|
+
end
|
219
|
+
|
220
|
+
def finalize_media
|
221
|
+
@media_handler.finalize
|
204
222
|
end
|
205
223
|
|
206
224
|
def use(klass, args)
|
225
|
+
args[:mediaklass] = self
|
207
226
|
@media_handler = klass.new(**args)
|
227
|
+
@media_handler.create_abs_class_dir
|
208
228
|
end
|
209
229
|
|
210
230
|
# API method for setting the model field mapped to SLUG on upload
|
211
231
|
def slug(inp)
|
212
|
-
@slug_field = inp
|
232
|
+
@slug_field = inp
|
213
233
|
end
|
214
|
-
|
234
|
+
|
215
235
|
def api_check_media_fields
|
216
|
-
unless
|
217
|
-
raise OData::API::MediaModelError, self
|
218
|
-
end
|
219
|
-
# unless self.db_schema.has_key?(:media_src)
|
220
|
-
# raise OData::API::MediaModelError, self
|
221
|
-
# end
|
236
|
+
raise(Safrano::API::MediaModelError, self) unless db_schema.key?(:content_type)
|
222
237
|
end
|
223
238
|
|
224
|
-
# END API methods
|
239
|
+
# END API methods
|
225
240
|
|
226
241
|
def new_media_entity(mimetype:)
|
227
|
-
nh = {}
|
228
|
-
nh['content_type'] = mimetype
|
242
|
+
nh = { 'content_type' => mimetype }
|
229
243
|
new_from_hson_h(nh)
|
230
244
|
end
|
231
245
|
|
@@ -238,11 +252,11 @@ module OData
|
|
238
252
|
# NOTE: we will implement this first in a MVP way. There will be plenty of
|
239
253
|
# potential future enhancements (performance, scallability, flexibility... with added complexity)
|
240
254
|
# 4. create relation to parent if needed
|
241
|
-
def odata_create_entity_and_relation(req, assoc, parent)
|
255
|
+
def odata_create_entity_and_relation(req, assoc = nil, parent = nil)
|
242
256
|
req.with_media_data do |data, mimetype, filename|
|
243
257
|
## future enhancement: validate allowed mimetypes ?
|
244
258
|
# if (invalid = invalid_media_mimetype(mimetype))
|
245
|
-
# ::
|
259
|
+
# ::Safrano::Request::ON_CGST_ERROR.call(req)
|
246
260
|
# return [422, {}, ['Invalid mime type: ', invalid.to_s]]
|
247
261
|
# end
|
248
262
|
|
@@ -252,25 +266,44 @@ module OData
|
|
252
266
|
|
253
267
|
if slug_field
|
254
268
|
|
255
|
-
new_entity.set_fields({ slug_field =>
|
256
|
-
|
257
|
-
|
258
|
-
end
|
259
|
-
|
260
|
-
# to_one rels are create with FK data set on the parent entity
|
261
|
-
if parent
|
262
|
-
odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
|
263
|
-
else
|
264
|
-
# in-changeset requests get their own transaction
|
265
|
-
new_entity.save(transaction: !req.in_changeset)
|
269
|
+
new_entity.set_fields({ slug_field => filename },
|
270
|
+
data_fields,
|
271
|
+
missing: :skip)
|
266
272
|
end
|
267
273
|
|
268
|
-
|
269
|
-
new_entity.
|
274
|
+
# call before_create_entity media hook
|
275
|
+
new_entity.before_create_media_entity(data: data, mimetype: mimetype) if new_entity.respond_to? :before_create_media_entity
|
276
|
+
|
277
|
+
media_handler.check_before_create(data: data,
|
278
|
+
entity: new_entity,
|
279
|
+
filename: filename).if_valid { |_ret|
|
280
|
+
# to_one rels are create with FK data set on the parent entity
|
281
|
+
if parent
|
282
|
+
odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
|
283
|
+
else
|
284
|
+
# in-changeset requests get their own transaction
|
285
|
+
new_entity.save(transaction: !req.in_changeset)
|
286
|
+
end
|
287
|
+
|
288
|
+
req.register_content_id_ref(new_entity)
|
289
|
+
new_entity.copy_request_infos(req)
|
290
|
+
|
291
|
+
# call before_create_media hook
|
292
|
+
new_entity.before_create_media if new_entity.respond_to? :before_create_media
|
293
|
+
|
294
|
+
media_handler.save_file(data: data,
|
295
|
+
entity: new_entity,
|
296
|
+
filename: filename)
|
270
297
|
|
271
|
-
|
298
|
+
# call after_create_media hook
|
299
|
+
new_entity.after_create_media if new_entity.respond_to? :after_create_media
|
300
|
+
|
301
|
+
# json is default content type so we dont need to specify it here again
|
302
|
+
# Contract.valid([201, EMPTY_HASH, new_entity.to_odata_post_json(service: req.service)])
|
303
|
+
# TODO quirks array mode !
|
304
|
+
Contract.valid([201, EMPTY_HASH, new_entity.to_odata_create_json(request: req)])
|
305
|
+
}.tap_error { |e| return e.odata_get(req) }.result
|
272
306
|
|
273
|
-
[201, CT_JSON, new_entity.to_odata_post_json(service: req.service)]
|
274
307
|
else # TODO: other formats
|
275
308
|
415
|
276
309
|
end
|
@@ -278,3 +311,26 @@ module OData
|
|
278
311
|
end
|
279
312
|
end
|
280
313
|
end
|
314
|
+
|
315
|
+
# deprecated
|
316
|
+
# REMOVE 0.6
|
317
|
+
module OData
|
318
|
+
module Media
|
319
|
+
class Static < ::Safrano::Media::Static
|
320
|
+
def initialize(root: nil, mediaklass:)
|
321
|
+
::Safrano::Deprecation.deprecate('OData::Media::Static',
|
322
|
+
'Use Safrano::Media::Static instead')
|
323
|
+
|
324
|
+
super
|
325
|
+
end
|
326
|
+
end
|
327
|
+
class StaticTree < ::Safrano::Media::StaticTree
|
328
|
+
def initialize(root: nil, mediaklass:)
|
329
|
+
::Safrano::Deprecation.deprecate('OData::Media::StaticTree',
|
330
|
+
'Use Safrano::Media::StaticTree instead')
|
331
|
+
|
332
|
+
super
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|