restful-portfolios 0.13.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: afdeee7dc34ce903cb034e7db4c531411206f2211fe9c931cd7d85fdc46ef6a8
4
+ data.tar.gz: 39c6de3caae664a82a9b630c6c05449962ab436ac6a62461292539b46defb334
5
+ SHA512:
6
+ metadata.gz: e398a4378657e62781dae9633fc4ba0f718f5b5af5ec5229ec26bcd30a5884558affa60eaff81a65e777c32e874f5f2d1b198262ac5137dec91e4f366e9b385e
7
+ data.tar.gz: f76d75976e8744eebce9321e7edb87222207e78256d97a55e09f576d27497ed3ab92fc3f75a5d87b7b4ef37da4585cb3ba89657876633dd661a48cd0fab44db8
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # ######################################################################## #
5
+ #
6
+ # Main module/entry file for the Portfolios Microservice
7
+ #
8
+ # Copyright (c) 2017 Razor Risk Technologies Pty Limited. All rights reserved.
9
+ #
10
+ # ######################################################################## #
11
+
12
+
13
+ # ##########################################################################
14
+
15
+ # Microservices: Portfolios (portfolios)
16
+ #
17
+ # Supported:
18
+ #
19
+ # - [GET] / {Unsecured}
20
+ # - [DELETE] /delete/:id {Secured}
21
+ # - [GET] /get {Secured}
22
+ # - [GET] /get/:id {Secured}
23
+ # - [PUT] /put/:id {Secured}
24
+
25
+ # ##########################################################################
26
+ # requires
27
+
28
+ require 'razor_risk/cassini/diagnostics/zeroth_include'
29
+
30
+ require 'razor_risk/cassini/applications/microservices/restful/portfolios'
31
+
32
+ require 'razor_risk/cassini/applications/route_verb_adaptors/portfolios'
33
+
34
+ require 'razor_risk/cassini/main'
35
+
36
+ require 'razor_risk/cassini/applications/rest_framework/route_verb_dispatcher'
37
+ require 'razor_risk/cassini/applications/secured_microservice'
38
+ require 'razor_risk/cassini/authorisation'
39
+ require 'razor_risk/cassini/common/version'
40
+ require 'razor_risk/cassini/util/version_util'
41
+
42
+ require 'razor_risk/razor/connectivity/razor_3/header_maker'
43
+ require 'razor_risk/razor/connectivity/razor_3/razor_requester'
44
+ require 'razor_risk/razor/connectivity/version'
45
+
46
+ require 'razor_risk/core/diagnostics/extensions/libclimate'
47
+ require 'razor_risk/core/diagnostics/logger'
48
+
49
+ require 'active_support/core_ext/hash'
50
+ require 'pantheios'
51
+ require 'xqsr3/version'
52
+
53
+ require 'csv'
54
+ require 'json'
55
+ require 'nokogiri'
56
+
57
+ # ##########################################################################
58
+ # includes
59
+
60
+ include ::RazorRisk::Cassini::Applications
61
+ include ::RazorRisk::Cassini::Applications::RESTFramework
62
+ include ::RazorRisk::Cassini::Applications::RouteVerbAdaptors
63
+ include ::RazorRisk::Cassini::Authorisation::SecurityModelHelpers
64
+ include ::RazorRisk::Cassini::Constants
65
+ include ::RazorRisk::Cassini::Util::SecretsUtil
66
+ include ::RazorRisk::Cassini::Util::VersionUtil
67
+
68
+ include ::RazorRisk::Razor::Connectivity::Razor3
69
+
70
+ include ::RazorRisk::Cassini::Diagnostics
71
+
72
+ include ::Pantheios
73
+
74
+ include ::RazorRisk::Core::Diagnostics::Logger
75
+
76
+ # ##########################################################################
77
+ # constants
78
+
79
+ PROGRAM_VERSION = ::RazorRisk::Cassini::Applications::Microservices::RESTful::Portfolios::VERSION
80
+
81
+ SUPPORTED_ROUTES = [
82
+
83
+ [ '/delete/:id', :delete, 'deletes the specified portfolio', ],
84
+ [ '/get', :get, 'gets the collection of portfolios', ],
85
+ [ '/get/:id', :get, 'gets the specified portfolio', ],
86
+ [ '/put/:id', :put, 'puts the specified portfolio', ],
87
+ ]
88
+
89
+ HTTP_ACCEPTS = %w{ text/html application/json application/xml text/xml text/csv text/plain text/tab-separated-values text/tsv }
90
+
91
+ # ##########################################################################
92
+ # compatibility checks
93
+
94
+ check_version_compatibility ::RazorRisk::Cassini::Common, [ 0, 21 ], 'RazorRisk.Cassini.Common'
95
+ check_version_compatibility ::RazorRisk::Razor::Connectivity, [ 0, 11, 2 ], 'RazorRisk.Razor.Connectivity'
96
+ check_version_compatibility ::LibCLImate, '0.10'
97
+ check_version_compatibility ::Pantheios, '0.20'
98
+ check_version_compatibility ::Xqsr3::VERSION, '0.30'
99
+
100
+ # ##########################################################################
101
+ # functions
102
+
103
+ def make_CSV_routes routes
104
+
105
+ CSV.generate do |csv|
106
+
107
+ routes.each do |ar|
108
+
109
+ csv << ar
110
+ end
111
+ end
112
+ end
113
+
114
+ def make_HTML_routes routes, **options
115
+
116
+ # TODO: use erb
117
+
118
+ routes = routes.map do |ar|
119
+
120
+ <<END_OF_tr
121
+ <tr>
122
+ <td>#{ar[0]}</td>
123
+ <td>#{ar[1]}</td>
124
+ <td>#{ar[2]}</td>
125
+ </tr>
126
+ END_OF_tr
127
+ end
128
+
129
+ <<END_OF_html
130
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.we.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
131
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
132
+ <head>
133
+ <title>Razor Risk Web Service API - Internal Microservice - Portfolio</title>
134
+ <meta name="revisit-after" content="24 hours" />
135
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
136
+ </head>
137
+ <body>
138
+ <h1>Routes</h1>
139
+ <table>
140
+ <tr>
141
+ <th>Route</th>
142
+ <th>Verb</th>
143
+ <th>Description</th>
144
+ </tr>
145
+ #{routes.map { |r| r.chomp("\n") }.join("\n")}
146
+ </table>
147
+ </body>
148
+ </html>
149
+ END_OF_html
150
+ end
151
+
152
+ def make_JSON_routes routes, **options
153
+
154
+ r = {
155
+
156
+ 'routes' => []
157
+ }
158
+
159
+ r['routes'] = SUPPORTED_ROUTES.map { |ar| { route: ar[0], verb: ar[1].to_s.upcase, description: ar[2] } }
160
+
161
+ r.to_json
162
+ end
163
+
164
+ def make_Plain_routes routes
165
+
166
+ make_TSV_routes routes
167
+ end
168
+
169
+ def make_TSV_routes routes
170
+
171
+ routes.map { |ar| "#{ar[0]}\t#{ar[1]}\t#{ar[2]}\n" }
172
+ end
173
+
174
+ def make_XML_routes routes
175
+
176
+ <<END_OF_xml
177
+ <?xml version="1.0">
178
+ <routes>#{routes.map { |ar| %Q{ <route route="#{ar[0]}" verb="#{ar[1].to_s.upcase}" description="#{ar[2]}"/>}}.join(%Q{\n})}
179
+ </routes>
180
+ END_OF_xml
181
+ end
182
+
183
+ # ##########################################################################
184
+ # static set-up
185
+
186
+ # ##########################################################################
187
+ # lambdas
188
+
189
+ Lambdas_XML_to_JSON = lambda do |xml|
190
+
191
+ ConversionUtil.convert_XML_to_JSON xml
192
+ end
193
+
194
+ # ##########################################################################
195
+ # application
196
+
197
+ class PortfoliosApp < SecuredMicroservice
198
+
199
+ include RouteVerbDispatch
200
+
201
+ include ::Pantheios
202
+
203
+
204
+ FULL_DESIGNATION = 'Portfolios'
205
+ SHORT_DESIGNATION = 'portfolios'
206
+ SERVICE_TYPE = :microservice
207
+
208
+ PROGRAM_FEATURES = {
209
+
210
+ has_web_server: true,
211
+
212
+ has_host_and_port: true,
213
+
214
+ has_razor_connectivity: true,
215
+
216
+ authentication: :with_credentials_algorithm,
217
+
218
+ copyright_year: 2017,
219
+ }
220
+
221
+
222
+ def self.on_init_service options
223
+
224
+ trace ParamNames[ :options ], options
225
+
226
+ raise ArgumentError, 'missing keyword: razor_requester' unless options.has_key? :razor_requester
227
+
228
+ set :razor_requester, options[:razor_requester]
229
+ end
230
+
231
+
232
+
233
+ private
234
+ def sec
235
+
236
+ return '' if credentials.empty?
237
+ " (for #{credentials.join(':')})"
238
+ end
239
+ public
240
+
241
+
242
+ # functional routes (DELETE, GET, PUT)
243
+
244
+ delete '/delete/:id/?' do
245
+
246
+ # trace
247
+
248
+ trace ParamNames[ :request, :params ], request, params
249
+
250
+ dispatch Portfolios::ItemDelete
251
+ end
252
+
253
+ # === Query Parameters
254
+ #
255
+ # +page-base+:: [Numeric] (0) Specifies the (0-based) index index of the
256
+ # first element to be presented.
257
+ # +page-extent+:: [Numeric] (1000) Specifies the number of records to
258
+ # return in the retrieval window.
259
+ # +key-search+:: [String] Specifies a string inf the form of a series of
260
+ # keys and values to search for.
261
+ # +filter-name+:: [String] The name of a filter to use.
262
+ get '/get/hierarchy/?' do
263
+
264
+ trace ParamNames[ :request, :params ], request, params
265
+
266
+ dispatch Portfolios::HierarchyGet
267
+ end
268
+
269
+ # params:
270
+ # +result-form+:: Specifies the form of the result: +default+ gets the
271
+ # full Razor body, and is the default if none specified; +summary+ gets
272
+ # the portfolio-summary form; +portfolio+ gets the portfolio form
273
+ get '/get/:id/?' do
274
+
275
+ # trace
276
+
277
+ trace ParamNames[ :request, :params ], request, params
278
+
279
+ dispatch Portfolios::ItemGet
280
+ end
281
+
282
+ # params:
283
+ # +page-base+:: Specifies the (0-based) index index of the first element
284
+ # to be presented. Defaults to 0 if not specified
285
+ # +page-extent+:: Specifies the number of records to return in the
286
+ # retrieval window. Defaults to 1000 if not specified
287
+ # +quick-search+:: Specifies a quick-search string
288
+ # +key-search+:: Specifies a string inf the form of a series of keys and values to search for
289
+ get '/get/?' do
290
+
291
+ trace ParamNames[ :request, :params ], request, params
292
+
293
+ dispatch Portfolios::CollectionGet
294
+ end
295
+
296
+ post '/post/?' do
297
+
298
+ trace ParamNames[ :request, :params ], request, params
299
+
300
+ dispatch Portfolios::ItemPost
301
+ end
302
+
303
+ put '/put/:id/?' do
304
+
305
+ trace ParamNames[ :request, :params ], request, params
306
+
307
+ dispatch Portfolios::ItemPut
308
+ end
309
+
310
+ get '/' do
311
+
312
+ HTTP_ACCEPTS.each do |accept_type|
313
+
314
+ if request.accept? accept_type
315
+
316
+ content_type accept_type
317
+
318
+ case accept_type
319
+ when 'application/json'
320
+
321
+ r = make_JSON_routes SUPPORTED_ROUTES
322
+ when 'application/xml', 'text/xml'
323
+
324
+ r = make_XML_routes SUPPORTED_ROUTES
325
+ when 'text/csv'
326
+
327
+ r = make_CSV_routes SUPPORTED_ROUTES
328
+ when 'text/html'
329
+
330
+ r = make_HTML_routes SUPPORTED_ROUTES
331
+ when 'text/plain'
332
+
333
+ r = make_Plain_routes SUPPORTED_ROUTES
334
+ when 'text/tsv', 'text/tab-separated-values'
335
+
336
+ r = make_TSV_routes SUPPORTED_ROUTES
337
+ else
338
+
339
+ log :violation, 'unrecognised accept type \'', accept_type, '\''
340
+
341
+ halt *[ 500, {}, 'internal server failure' ]
342
+ end
343
+
344
+ return r
345
+ end
346
+ end
347
+
348
+ halt *[ 406, {}, "supports only the Accept types #{HTTP_ACCEPTS.map { |t| %Q<'#{t}'> }.join(', ')}" ]
349
+ end
350
+
351
+ define_catch_all_handlers
352
+ end
353
+
354
+ TheApp = PortfoliosApp
355
+
356
+ # now define the OPTIONS support
357
+
358
+ supported_routes = Hash.new { |h, k| h[k] = [] }
359
+
360
+ SUPPORTED_ROUTES.each do |ar|
361
+
362
+ supported_routes[ar[0]].push ar[1]
363
+ end
364
+
365
+ supported_routes.each do |k, v|
366
+
367
+ if k =~ /:id(?:\/\?)?$/
368
+
369
+ route = "#$`*"
370
+ else
371
+
372
+ route = k
373
+ end
374
+
375
+ TheApp.options route do
376
+
377
+ verbs = v.map { |t| t.upcase }.join(',')
378
+
379
+ halt *[200, { 'Allow' => verbs }, [ verbs ] ]
380
+ end
381
+ end
382
+
383
+ TheApp.options '/' do
384
+
385
+ halt *[200, { 'Allow' => 'GET' }, '' ]
386
+ end
387
+
388
+ # ############################## end of file ############################# #
389
+
390
+
@@ -0,0 +1,51 @@
1
+ # encoding: UTF-8
2
+
3
+ # ######################################################################## #
4
+ #
5
+ # Version for RazorRisk.Cassini.Microservices.RESTful.Portfolios library
6
+ #
7
+ # Copyright (c) 2019 Razor Risk Technologies Pty Limited. All rights reserved.
8
+ #
9
+ # ######################################################################## #
10
+
11
+
12
+ module RazorRisk
13
+ module Cassini
14
+ module Applications
15
+ module Microservices
16
+ module RESTful
17
+
18
+ module Portfolios
19
+
20
+ # Current version of the RazorRisk.Cassini.Microservices.RESTful.Portfolios library
21
+ VERSION = '0.13.17'
22
+
23
+ private
24
+ VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
25
+ public
26
+ # Major version of the RazorRisk.Cassini.Microservices.RESTful.Portfolios library
27
+ VERSION_MAJOR = VERSION_PARTS_[0] # :nodoc:
28
+ # Minor version of the RazorRisk.Cassini.Microservices.RESTful.Portfolios library
29
+ VERSION_MINOR = VERSION_PARTS_[1] # :nodoc:
30
+ # Patch version of the RazorRisk.Cassini.Microservices.RESTful.Portfolios library
31
+ VERSION_PATCH = VERSION_PARTS_[2] # :nodoc:
32
+ # Commit version of the RazorRisk.Cassini.Microservices.RESTful.Portfolios library
33
+ VERSION_COMMIT = VERSION_PARTS_[3] || 0 # :nodoc:
34
+
35
+
36
+ # The description of the framework
37
+ DESCRIPTION = "Razor Risk's Cassini Web-framework's Portfolios RESTful microservice"
38
+
39
+ # [DEPRECATED] Instead use +DESCRIPTION+
40
+ FRAMEWORK_DESCRIPTION = DESCRIPTION
41
+ end # module Portfolios
42
+
43
+ end # module RESTful
44
+ end # module Microservices
45
+ end # module Applications
46
+ end # module Cassini
47
+ end # module RazorRisk
48
+
49
+ # ############################## end of file ############################# #
50
+
51
+
@@ -0,0 +1,2 @@
1
+
2
+ require 'razor_risk/cassini/applications/microservices/restful/portfolios/version'
@@ -0,0 +1,253 @@
1
+ # encoding: UTF-8
2
+
3
+ # ######################################################################## #
4
+ #
5
+ # Adaptor for Portfolios microservice's collection GET verb
6
+ #
7
+ # Copyright (c) 2018 Razor Risk Technologies Pty Limited. All rights reserved.
8
+ #
9
+ # ######################################################################## #
10
+
11
+
12
+ # ##########################################################################
13
+ # requires
14
+
15
+ require 'razor_risk/cassini/applications/rest_framework/verb_handler'
16
+ require 'razor_risk/cassini/applications/route_verb_adaptors/utilities/collection_get_helper'
17
+ require 'razor_risk/cassini/applications/route_verb_adaptors/utilities/portfolios'
18
+
19
+ require 'razor_risk/razor/connectivity/entity_connectors/exceptions'
20
+ require 'razor_risk/razor/connectivity/razor_3/entity_connectors/portfolios_connector'
21
+
22
+ require 'razor_risk/core/diagnostics/logger'
23
+
24
+ require 'pantheios'
25
+ require 'xqsr3/conversion/integer_parser'
26
+ require 'xmlhasher'
27
+
28
+ # ##########################################################################
29
+ # module
30
+
31
+ module RazorRisk
32
+ module Cassini
33
+ module Applications
34
+ module RouteVerbAdaptors
35
+ module Portfolios
36
+
37
+ # ##########################################################################
38
+ # classes
39
+
40
+ class CollectionGet < RESTFramework::VerbHandler
41
+
42
+ include ::RazorRisk::Cassini::Applications::RouteVerbAdaptors::Utilities::CollectionGetHelper
43
+ include ::RazorRisk::Cassini::Applications::RouteVerbAdaptors::Utilities::Portfolios
44
+
45
+ include ::RazorRisk::Razor::Connectivity::EntityConnectors::Exceptions
46
+ include ::RazorRisk::Razor::Connectivity::Razor3::EntityConnectors
47
+
48
+ include ::Pantheios
49
+ include ::RazorRisk::Core::Diagnostics::Logger
50
+
51
+ HTTP_VERB = :get
52
+
53
+ HTTP_ACCEPTS = %w{ application/xml application/json text/xml }
54
+
55
+ QUERY_PARAMETERS = %w{
56
+
57
+ filter-name
58
+
59
+ page-base
60
+ page-extent
61
+
62
+ quick-search
63
+
64
+ key-search
65
+
66
+ linked-to-trade
67
+ }
68
+
69
+ def handle env, params, request, response
70
+
71
+ trace ParamNames[ :env, :params, :request, :response ], env, params, request, response
72
+
73
+ # params
74
+
75
+ filter_name = params['filter-name'].to_s.strip
76
+ filter_name = nil if filter_name.empty?
77
+ page_base = ::Xqsr3::Conversion::IntegerParser.to_integer(params['page-base'] || 0) { |x, arg|
78
+ halt 422, {}, "invalid page specifier: 'page-base'=#{arg}"
79
+ }
80
+ page_extent = ::Xqsr3::Conversion::IntegerParser.to_integer(params['page-extent'] || 1000) { |x, arg|
81
+ halt 422, {}, "invalid page specifier: 'page-extent'=#{arg}"
82
+ }
83
+ quick_search = params['quick-search']
84
+ quick_search = nil if String === quick_search && quick_search.strip.empty?
85
+ key_search = params['key-search']
86
+ key_search = nil if String === key_search && key_search.strip.empty?
87
+ linked_to_trade = params['linked-to-trade'].to_s.strip
88
+ linked_to_trade = nil if linked_to_trade.empty?
89
+
90
+ rf = case rf = (params['result-form'] || '').to_s
91
+ when '', 'default'
92
+ :body
93
+ when 'children'
94
+ :array
95
+ else
96
+ log(:warning) { "unrecognised result-form '#{rf}'" }
97
+ :body
98
+ end
99
+
100
+ if page_base < 0 || page_extent < 1
101
+
102
+ halt 416, {}, "invalid page specifier: 'page-base'=#{page_base}; 'page-extent'=#{page_extent}"
103
+ end
104
+
105
+ if filter_name and quick_search
106
+
107
+ halt 422, {}, "invalid search specifier: cannot specify 'filter-name' and 'quick-search' together"
108
+ end
109
+
110
+ if key_search
111
+ begin
112
+ key_search = MultiValueConjunctionParser.new.parse_conjunction_list key_search
113
+ rescue MultiValueConjunctionParser::SyntaxError => x
114
+ halt 422, {'Content-Type' => 'text/plain'}, x.message
115
+ end
116
+ end
117
+
118
+ rq_opts = {
119
+
120
+ :filter_name => filter_name,
121
+ :page_base => page_base,
122
+ :page_extent => page_extent,
123
+ :quick_search => quick_search,
124
+ :key_search => key_search,
125
+ :linked_to_trade => linked_to_trade,
126
+ }
127
+
128
+ # get credentials
129
+
130
+ cr = get_required_credentials
131
+
132
+ # get requester
133
+
134
+ rr = settings.razor_requester
135
+
136
+ # get connector
137
+
138
+ ec = PortfoliosConnector.new(rr, credentials: cr, **rq_opts)
139
+
140
+ # issue request
141
+
142
+ begin
143
+
144
+ qr = ec.get_portfolios indicate_result_by: :qualified_result, result_form: rf
145
+ rescue EntityConnectorInternalStepException => x
146
+
147
+ log :failure, "exception (#{x.class}): #{x}"
148
+
149
+ headers = {
150
+
151
+ 'Content-Type' => 'text/plain',
152
+ }
153
+
154
+ code = 500
155
+ msg = x.message
156
+
157
+ halt(code, headers, msg)
158
+ end
159
+
160
+ log :debug1, "qr(#{qr.class})='#{qr}'"
161
+
162
+ unless qr.succeeded?
163
+
164
+ if false
165
+
166
+ ;
167
+ elsif :no_such_filter == qr.result
168
+
169
+ log :debug1, "failed to retrieve portfolios: no such filter '#{filter_name}'"
170
+
171
+ halt 422, {}, "no such filter '#{filter_name}'"
172
+ elsif r = qr.failure_qualifier.reasons.lookup?('RZ0011')
173
+
174
+ log :debug1, 'failed to retrieve portfolios'
175
+
176
+ halt 404, {}, "#{r.message}: #{r.details}"
177
+ else
178
+
179
+ log :warning, 'failed to retrieve portfolios'
180
+
181
+ halt 500, {}, qr.failure_qualifier.reasons.join("\n")
182
+ end
183
+ end
184
+
185
+ r = case rf
186
+ when :array
187
+
188
+ xml_doc = ::Nokogiri.XML('<body/>')
189
+ root_node = xml_doc.children.first
190
+
191
+ qr.result.each do |pf_element|
192
+
193
+ root_node.add_child pf_element.to_s
194
+ end
195
+
196
+ xml_doc.to_s
197
+ else
198
+
199
+ qr.result.to_s
200
+ end
201
+
202
+ # return result
203
+
204
+ status 200
205
+
206
+ if false
207
+ elsif request.accept?('application/xml')
208
+
209
+ log :debug1, 'application/xml'
210
+
211
+ content_type 'application/xml'
212
+
213
+ r
214
+ elsif request.accept?('application/json')
215
+
216
+ log :debug1, 'application/json'
217
+
218
+ content_type 'application/json'
219
+
220
+ resp_hash = XmlHasher.parse(r)
221
+
222
+ #resp_hash = Hash.from_xml r
223
+ resp_json = resp_hash
224
+
225
+ JSON.generate resp_json
226
+ elsif request.accept?('text/xml')
227
+
228
+ log :debug1, 'text/xml'
229
+
230
+ content_type 'text/xml'
231
+
232
+ r
233
+ else
234
+
235
+ log :violation, "unexpected failure to match given 'Accept' header '#{request.accept}'"
236
+
237
+ status 500
238
+ end
239
+ end
240
+ end # class CollectionGet
241
+
242
+ # ##########################################################################
243
+ # module
244
+
245
+ end # module Portfolios
246
+ end # module RouteVerbAdaptors
247
+ end # module Applications
248
+ end # module Cassini
249
+ end # module RazorRisk
250
+
251
+ # ############################## end of file ############################# #
252
+
253
+