mondrian-rest 0.6.0-java → 0.7.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.md +2 -0
- data/Rakefile +6 -1
- data/lib/jars/com/codepoetics/protonpack/1.9/protonpack-1.9.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/core/jackson-annotations/2.8.0/jackson-annotations-2.8.0.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/core/jackson-core/2.8.5/jackson-core-2.8.5.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/core/jackson-databind/2.8.5/jackson-databind-2.8.5.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-guava/2.8.5/jackson-datatype-guava-2.8.5.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.8.5/jackson-datatype-jdk8-2.8.5.jar +0 -0
- data/lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.8.5/jackson-datatype-jsr310-2.8.5.jar +0 -0
- data/lib/jars/com/google/guava/guava/19.0/guava-19.0.jar +0 -0
- data/lib/jars/me/yanaga/guava-stream/1.0/guava-stream-1.0.jar +0 -0
- data/lib/jars/mondrian-rest_jars.rb +28 -0
- data/lib/jars/no/ssb/jsonstat/json-stat-java/0.2.2/json-stat-java-0.2.2.jar +0 -0
- data/lib/mondrian_rest/api.rb +60 -45
- data/lib/mondrian_rest/api_formatters.rb +6 -76
- data/lib/mondrian_rest/api_helpers.rb +14 -0
- data/lib/mondrian_rest/formatters/aggregation_json.rb +10 -0
- data/lib/mondrian_rest/formatters/csv.rb +21 -0
- data/lib/mondrian_rest/formatters/excel.rb +30 -0
- data/lib/mondrian_rest/formatters/jsonrecords.rb +31 -0
- data/lib/mondrian_rest/formatters/jsonstat.rb +50 -0
- data/lib/mondrian_rest/mondrian_ext.rb +12 -3
- data/lib/mondrian_rest/query_helper.rb +8 -3
- data/lib/mondrian_rest.rb +2 -0
- data/mondrian-rest.gemspec +6 -3
- metadata +37 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9884ded4baf72e8778db0f38bd66689a098ba4c1
|
4
|
+
data.tar.gz: 41004cd22953933eb63c74f335c2ee55b9dfea1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7af0aa6def1e41782c5b517cef5199545a0be1f69baff8400499b5d5f1231f3f5a93d2bfec6a5c8f6f98c83c421ebada332b5e798a8104158721a7430a38ba7f
|
7
|
+
data.tar.gz: 8280d75f3484daba329486853a32aa306ec6b3935511345ad3887405de0b50c1879470e26e4aa2ded4d57426a9744c4cb84aab6d4cf945189294869cd2d0afa2
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,8 @@ See [`mondrian-rest-demo`](https://github.com/jazzido/mondrian-rest-demo) and [`
|
|
8
8
|
|
9
9
|
[`mondrian-rest-ui`](https://github.com/jazzido/mondrian-rest-ui) is an experimental visualization tool for `mondrian-rest`, inspired by [CubesViewer](https://github.com/jjmontesl/cubesviewer) and [Polestar](https://github.com/vega/polestar).
|
10
10
|
|
11
|
+
*NOTE*: `mondrian-rest` expect the `mondrian.olap.SsasCompatibleNaming` Java property is set to `true`. Set a environment variable `JAVA_OPTS="-Dmondrian.olap.SsasCompatibleNaming=true""` to the process that uses `mondrian-rest`.
|
12
|
+
|
11
13
|
## Credits
|
12
14
|
|
13
15
|
Initial inspiration for this project came from [Stefan Urbanek](https://github.com/Stiivi)'s [`cubes`](http://cubes.databrewery.org/) OLAP server. `mondrian-rest` uses [Raimonds Simanovskis](https://github.com/rsim)'s fantastic [`mondrian-olap`](https://github.com/rsim/mondrian-olap) library.
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "rspec/core/rake_task"
|
2
|
+
require 'jars/installer'
|
2
3
|
|
3
|
-
require_relative './lib/mondrian_rest.rb'
|
4
4
|
|
5
5
|
desc "Run specs"
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
@@ -11,6 +11,7 @@ end
|
|
11
11
|
|
12
12
|
desc "API Routes"
|
13
13
|
task :routes do
|
14
|
+
require_relative './lib/mondrian_rest.rb'
|
14
15
|
Mondrian::REST::Api.routes.each do |api|
|
15
16
|
method = api.route_method.ljust(10)
|
16
17
|
path = api.route_path
|
@@ -18,4 +19,8 @@ task :routes do
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
22
|
+
task :install_jars do
|
23
|
+
Jars::Installer.vendor_jars!("lib/jars")
|
24
|
+
end
|
25
|
+
|
21
26
|
task :default => [:spec]
|
Binary file
|
data/lib/jars/com/fasterxml/jackson/core/jackson-annotations/2.8.0/jackson-annotations-2.8.0.jar
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# this is a generated file, to avoid over-writing it just delete this comment
|
2
|
+
begin
|
3
|
+
require 'jar_dependencies'
|
4
|
+
rescue LoadError
|
5
|
+
require 'com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.8.5/jackson-datatype-jdk8-2.8.5.jar'
|
6
|
+
require 'com/fasterxml/jackson/core/jackson-annotations/2.8.0/jackson-annotations-2.8.0.jar'
|
7
|
+
require 'me/yanaga/guava-stream/1.0/guava-stream-1.0.jar'
|
8
|
+
require 'com/fasterxml/jackson/core/jackson-databind/2.8.5/jackson-databind-2.8.5.jar'
|
9
|
+
require 'no/ssb/jsonstat/json-stat-java/0.2.2/json-stat-java-0.2.2.jar'
|
10
|
+
require 'com/fasterxml/jackson/core/jackson-core/2.8.5/jackson-core-2.8.5.jar'
|
11
|
+
require 'com/fasterxml/jackson/datatype/jackson-datatype-guava/2.8.5/jackson-datatype-guava-2.8.5.jar'
|
12
|
+
require 'com/google/guava/guava/19.0/guava-19.0.jar'
|
13
|
+
require 'com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.8.5/jackson-datatype-jsr310-2.8.5.jar'
|
14
|
+
require 'com/codepoetics/protonpack/1.9/protonpack-1.9.jar'
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined? Jars
|
18
|
+
require_jar( 'com.fasterxml.jackson.datatype', 'jackson-datatype-jdk8', '2.8.5' )
|
19
|
+
require_jar( 'com.fasterxml.jackson.core', 'jackson-annotations', '2.8.0' )
|
20
|
+
require_jar( 'me.yanaga', 'guava-stream', '1.0' )
|
21
|
+
require_jar( 'com.fasterxml.jackson.core', 'jackson-databind', '2.8.5' )
|
22
|
+
require_jar( 'no.ssb.jsonstat', 'json-stat-java', '0.2.2' )
|
23
|
+
require_jar( 'com.fasterxml.jackson.core', 'jackson-core', '2.8.5' )
|
24
|
+
require_jar( 'com.fasterxml.jackson.datatype', 'jackson-datatype-guava', '2.8.5' )
|
25
|
+
require_jar( 'com.google.guava', 'guava', '19.0' )
|
26
|
+
require_jar( 'com.fasterxml.jackson.datatype', 'jackson-datatype-jsr310', '2.8.5' )
|
27
|
+
require_jar( 'com.codepoetics', 'protonpack', '1.9' )
|
28
|
+
end
|
data/lib/mondrian_rest/api.rb
CHANGED
@@ -21,11 +21,16 @@ module Mondrian::REST
|
|
21
21
|
resource :mdx do
|
22
22
|
content_type :xls, "application/vnd.ms-excel"
|
23
23
|
formatter :xls, Mondrian::REST::Formatters::XLS
|
24
|
+
|
24
25
|
content_type :csv, "text/csv"
|
25
26
|
formatter :csv, Mondrian::REST::Formatters::CSV
|
27
|
+
|
26
28
|
content_type :json, "application/json"
|
27
29
|
formatter :json, Mondrian::REST::Formatters::AggregationJSON
|
28
30
|
|
31
|
+
content_type :jsonrecords, "application/x-jsonrecords"
|
32
|
+
formatter :jsonrecords, Mondrian::REST::Formatters::JSONRecords
|
33
|
+
|
29
34
|
desc "Execute an MDX query against a cube"
|
30
35
|
content_type :txt, "text/plain"
|
31
36
|
post do
|
@@ -37,22 +42,22 @@ module Mondrian::REST
|
|
37
42
|
end
|
38
43
|
|
39
44
|
resource :flush do
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
params do
|
46
|
+
requires :secret, type: String, desc: "Secret key"
|
47
|
+
end
|
48
|
+
content_type :json, "application/json"
|
49
|
+
desc "Flush the schema cache"
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
get do
|
52
|
+
if ENV['MONDRIAN_REST_SECRET'].nil?
|
53
|
+
error!("Please set MONDRIAN_REST_SECRET to use this endpoint", 403)
|
54
|
+
end
|
55
|
+
if params[:secret] != ENV['MONDRIAN_REST_SECRET']
|
56
|
+
error!("Invalid secret key.", 403)
|
57
|
+
end
|
58
|
+
{
|
59
|
+
'status' => olap_flush
|
60
|
+
}
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
@@ -78,25 +83,25 @@ module Mondrian::REST
|
|
78
83
|
end
|
79
84
|
|
80
85
|
resource :members do
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
desc "return a member by its full name"
|
87
|
+
params do
|
88
|
+
requires :full_name,
|
89
|
+
type: String,
|
90
|
+
regexp: /[a-z0-9\.,\-\s%\[\]\(\)]+/i
|
91
|
+
end
|
92
|
+
get do
|
93
|
+
member_full_name = URI.decode(params[:full_name])
|
89
94
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
m.to_h.merge({
|
96
|
-
ancestors: m.ancestors.map(&:to_h),
|
97
|
-
dimension: m.dimension_info
|
98
|
-
})
|
95
|
+
m = get_member(get_cube_or_404(params[:cube_name]),
|
96
|
+
member_full_name)
|
97
|
+
if m.nil?
|
98
|
+
error!("Member `#{member_full_name}` not found in cube `#{params[:cube_name]}`", 404)
|
99
99
|
end
|
100
|
+
m.to_h.merge({
|
101
|
+
ancestors: m.ancestors.map(&:to_h),
|
102
|
+
dimension: m.dimension_info
|
103
|
+
})
|
104
|
+
end
|
100
105
|
end
|
101
106
|
|
102
107
|
|
@@ -113,6 +118,9 @@ module Mondrian::REST
|
|
113
118
|
content_type :jsonrecords, "application/x-jsonrecords"
|
114
119
|
formatter :jsonrecords, Mondrian::REST::Formatters::JSONRecords
|
115
120
|
|
121
|
+
content_type :jsonstat, "application/x-jsonstat"
|
122
|
+
formatter :jsonstat, Mondrian::REST::Formatters::JSONStat
|
123
|
+
|
116
124
|
rescue_from PropertyError do |e|
|
117
125
|
error!({error: e}, 400)
|
118
126
|
end
|
@@ -129,18 +137,13 @@ module Mondrian::REST
|
|
129
137
|
optional :properties, type: Array, desc: "Include member properties"
|
130
138
|
optional :caption, type: Array, desc: "Replace caption with property", default: []
|
131
139
|
end
|
132
|
-
get do
|
133
|
-
cube = get_cube_or_404(params[:cube_name])
|
134
|
-
query = build_query(cube, params)
|
135
|
-
mdx_query = query.to_mdx
|
136
140
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
result.caption_properties = params[:caption]
|
141
|
-
result.cube = cube
|
141
|
+
get do
|
142
|
+
run_from_params(params)
|
143
|
+
end
|
142
144
|
|
143
|
-
|
145
|
+
post do
|
146
|
+
run_from_params(params)
|
144
147
|
end
|
145
148
|
end
|
146
149
|
|
@@ -164,6 +167,8 @@ module Mondrian::REST
|
|
164
167
|
|
165
168
|
params do
|
166
169
|
optional :member_properties, type: Array, default: []
|
170
|
+
optional :caption, type: String, desc: "Replace caption with property", default: nil
|
171
|
+
optional :children, type: Boolean, default: false
|
167
172
|
end
|
168
173
|
|
169
174
|
get do
|
@@ -179,13 +184,21 @@ module Mondrian::REST
|
|
179
184
|
error!("level #{params[:level_name]} not found in dimension #{params[:dimension_name]}")
|
180
185
|
end
|
181
186
|
|
182
|
-
level.to_h(params[:member_properties])
|
187
|
+
level.to_h(params[:member_properties], params[:children], params[:caption])
|
183
188
|
end
|
184
189
|
|
185
190
|
route_param :member_key,
|
186
191
|
type: String,
|
187
|
-
requirements: { member_key: /[
|
192
|
+
requirements: { member_key: /[A-Za-z0-9\.\-\s%]+/i } do
|
193
|
+
|
194
|
+
params do
|
195
|
+
optional :caption, type: String, desc: "Replace caption with property", default: nil
|
196
|
+
optional :member_properties, type: Array, default: []
|
197
|
+
optional :children, type: Boolean, default: false
|
198
|
+
end
|
199
|
+
|
188
200
|
get do
|
201
|
+
puts params.inspect
|
189
202
|
cube = get_cube_or_404(params[:cube_name])
|
190
203
|
dimension = cube.dimension(params[:dimension_name])
|
191
204
|
level = dimension.hierarchies[0].level(params[:level_name])
|
@@ -194,7 +207,9 @@ module Mondrian::REST
|
|
194
207
|
m.property_value('MEMBER_KEY').to_s == params[:member_key]
|
195
208
|
}
|
196
209
|
error!('member not found', 404) if member.nil?
|
197
|
-
member
|
210
|
+
member
|
211
|
+
.to_h(params[:member_properties], params[:caption], params[:children])
|
212
|
+
.merge({ancestors: member.ancestors.map(&:to_h)})
|
198
213
|
end
|
199
214
|
end
|
200
215
|
end
|
@@ -1,81 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative './formatters/aggregation_json'
|
2
|
+
require_relative './formatters/csv'
|
3
|
+
require_relative './formatters/excel'
|
4
|
+
require_relative './formatters/jsonrecords'
|
5
|
+
require_relative './formatters/jsonstat'
|
3
6
|
|
4
|
-
module Mondrian::REST::Formatters
|
5
|
-
|
6
|
-
module AggregationJSON
|
7
|
-
def self.call(result, env)
|
8
|
-
add_parents = env['rack.request.query_hash']['parents'] == 'true'
|
9
|
-
debug = env['rack.request.query_hash']['debug'] == 'true'
|
10
|
-
|
11
|
-
result.to_h(add_parents, debug).to_json
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module XLS
|
16
|
-
def self.call(result, env)
|
17
|
-
add_parents = env['rack.request.query_hash']['parents'] == 'true'
|
18
|
-
debug = env['rack.request.query_hash']['debug'] == 'true'
|
19
|
-
properties = env['rack.request.query_hash']['properties'] || []
|
20
|
-
|
21
|
-
out = StringIO.new
|
22
|
-
book = WriteExcel.new(out)
|
23
|
-
sheet = book.add_worksheet
|
24
|
-
|
25
|
-
Mondrian::REST::Formatters
|
26
|
-
.tidy(result,
|
27
|
-
add_parents: add_parents,
|
28
|
-
debug: debug,
|
29
|
-
properties: properties)
|
30
|
-
.each_with_index do |row, i|
|
31
|
-
row.each_with_index { |cell, j|
|
32
|
-
sheet.write(i, j, cell)
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
book.close
|
37
|
-
out.string
|
38
|
-
end
|
39
|
-
end
|
40
7
|
|
41
|
-
|
42
|
-
def self.call(result, env)
|
43
|
-
add_parents = env['rack.request.query_hash']['parents'] == 'true'
|
44
|
-
debug = env['rack.request.query_hash']['debug'] == 'true'
|
45
|
-
properties = env['rack.request.query_hash']['properties'] || []
|
46
|
-
|
47
|
-
rows = Mondrian::REST::Formatters.tidy(result,
|
48
|
-
add_parents: add_parents,
|
49
|
-
debug: debug,
|
50
|
-
properties: properties)
|
51
|
-
|
52
|
-
::CSV.generate do |csv|
|
53
|
-
rows.each { |row| csv << row }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
module JSONRecords
|
59
|
-
def self.call(result, env)
|
60
|
-
add_parents = env['rack.request.query_hash']['parents'] == 'true'
|
61
|
-
debug = env['rack.request.query_hash']['debug'] == 'true'
|
62
|
-
properties = env['rack.request.query_hash']['properties'] || []
|
63
|
-
|
64
|
-
rows = Mondrian::REST::Formatters.tidy(result,
|
65
|
-
add_parents: add_parents,
|
66
|
-
debug: debug,
|
67
|
-
properties: properties).lazy
|
68
|
-
keys = rows.next
|
69
|
-
|
70
|
-
{
|
71
|
-
data: rows.with_index.with_object([]) { |(row, i), data|
|
72
|
-
next if i == 0
|
73
|
-
data << Hash[keys.zip(row)]
|
74
|
-
}
|
75
|
-
}.to_json
|
76
|
-
|
77
|
-
end
|
78
|
-
end
|
8
|
+
module Mondrian::REST::Formatters
|
79
9
|
|
80
10
|
##
|
81
11
|
# Generate 'tidy data' (http://vita.had.co.nz/papers/tidy-data.pdf)
|
@@ -50,6 +50,20 @@ module Mondrian::REST
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def run_from_params(params)
|
54
|
+
cube = get_cube_or_404(params[:cube_name])
|
55
|
+
query = build_query(cube, params)
|
56
|
+
mdx_query = query.to_mdx
|
57
|
+
|
58
|
+
result = mdx(query.to_mdx)
|
59
|
+
result.mdx = mdx_query if params[:debug]
|
60
|
+
result.properties = params[:properties]
|
61
|
+
result.caption_properties = params[:caption]
|
62
|
+
result.cube = cube
|
63
|
+
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
53
67
|
NEST = Mondrian::REST::Nest.new
|
54
68
|
.key { |d| d[0] }
|
55
69
|
.key { |d| d[1] }
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Mondrian::REST::Formatters
|
2
|
+
module AggregationJSON
|
3
|
+
def self.call(result, env)
|
4
|
+
add_parents = env['rack.request.query_hash']['parents'] == 'true'
|
5
|
+
debug = env['rack.request.query_hash']['debug'] == 'true'
|
6
|
+
|
7
|
+
result.to_h(add_parents, debug).to_json
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Mondrian::REST::Formatters
|
4
|
+
module CSV
|
5
|
+
def self.call(result, env)
|
6
|
+
qh = env['rack.request.query_hash']
|
7
|
+
add_parents = qh['parents'] == 'true'
|
8
|
+
debug = qh['debug'] == 'true'
|
9
|
+
properties = qh['properties'] || []
|
10
|
+
|
11
|
+
rows = Mondrian::REST::Formatters.tidy(result,
|
12
|
+
add_parents: add_parents,
|
13
|
+
debug: debug,
|
14
|
+
properties: properties)
|
15
|
+
|
16
|
+
::CSV.generate do |csv|
|
17
|
+
rows.each { |row| csv << row }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'writeexcel'
|
2
|
+
|
3
|
+
module Mondrian::REST::Formatters
|
4
|
+
module XLS
|
5
|
+
def self.call(result, env)
|
6
|
+
qh = env['rack.request.query_hash']
|
7
|
+
add_parents = qh['parents'] == 'true'
|
8
|
+
debug = qh['debug'] == 'true'
|
9
|
+
properties = qh['properties'] || []
|
10
|
+
|
11
|
+
out = StringIO.new
|
12
|
+
book = WriteExcel.new(out)
|
13
|
+
sheet = book.add_worksheet
|
14
|
+
|
15
|
+
Mondrian::REST::Formatters
|
16
|
+
.tidy(result,
|
17
|
+
add_parents: add_parents,
|
18
|
+
debug: debug,
|
19
|
+
properties: properties)
|
20
|
+
.each_with_index do |row, i|
|
21
|
+
row.each_with_index { |cell, j|
|
22
|
+
sheet.write(i, j, cell)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
book.close
|
27
|
+
out.string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mondrian::REST::Formatters
|
2
|
+
module JSONRecords
|
3
|
+
def self.call(result, env)
|
4
|
+
qh = env['rack.request.query_hash']
|
5
|
+
add_parents = qh['parents'] == 'true'
|
6
|
+
debug = qh['debug'] == 'true'
|
7
|
+
properties = qh['properties'] || []
|
8
|
+
format = qh['format'] == 'array' ? 'array' : 'object'
|
9
|
+
|
10
|
+
rows = Mondrian::REST::Formatters.tidy(result,
|
11
|
+
add_parents: add_parents,
|
12
|
+
debug: debug,
|
13
|
+
properties: properties).lazy
|
14
|
+
keys = rows.first
|
15
|
+
|
16
|
+
if format == 'array'
|
17
|
+
{
|
18
|
+
variables: keys,
|
19
|
+
data: rows.drop(1).to_a
|
20
|
+
}.to_json
|
21
|
+
else
|
22
|
+
{
|
23
|
+
data: rows.with_index.with_object([]) { |(row, i), data|
|
24
|
+
next if i == 0
|
25
|
+
data << Hash[keys.zip(row)]
|
26
|
+
}
|
27
|
+
}.to_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module Mondrian::REST::Formatters
|
4
|
+
module JSONStat
|
5
|
+
|
6
|
+
def self.call(result, env)
|
7
|
+
|
8
|
+
mapper = Java::ComFasterxmlJacksonDatabind::ObjectMapper.new
|
9
|
+
mapper.registerModule(Java::NoSsbJsonstat::JsonStatModule.new)
|
10
|
+
mapper.registerModule(Java::ComFasterxmlJacksonDatatypeJdk8::Jdk8Module.new.configureAbsentsAsNulls(true))
|
11
|
+
mapper.setSerializationInclusion(Java::ComFasterxmlJacksonAnnotation::JsonInclude::Include::NON_NULL)
|
12
|
+
mapper.registerModule(Java::ComFasterxmlJacksonDatatypeGuava::GuavaModule.new.configureAbsentsAsNulls(false))
|
13
|
+
|
14
|
+
rs = result.to_h
|
15
|
+
rdims = rs[:axis_dimensions].reverse
|
16
|
+
|
17
|
+
builder = Java::NoSsbJsonstatV2::Dataset
|
18
|
+
.create
|
19
|
+
.withLabel('Aggregation: ')
|
20
|
+
.withSource(env['REQUEST_URI'] || '')
|
21
|
+
|
22
|
+
dimensions = rdims.map.with_index do |d, i|
|
23
|
+
dim = Java::NoSsbJsonstatV2::Dimension
|
24
|
+
.create(d[:name])
|
25
|
+
|
26
|
+
if d[:type] == :measures
|
27
|
+
dim = dim.withMetricRole
|
28
|
+
elsif d[:type] == :time
|
29
|
+
dim = dim.withTimeRole
|
30
|
+
end
|
31
|
+
|
32
|
+
dim.withLabel(d[:caption])
|
33
|
+
.withIndexedLabels(
|
34
|
+
Java::ComGoogleCommonCollect::ImmutableMap.copyOf(
|
35
|
+
Hash[*(rs[:axes][-1 - i][:members].map { |m| [ m[:key].to_s, m[:caption] ] }.flatten)].to_java
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
dataset = builder
|
41
|
+
.withDimensions(dimensions)
|
42
|
+
.withValues(rs[:values].flatten)
|
43
|
+
.build
|
44
|
+
|
45
|
+
mapper.writeValueAsString(dataset)
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -102,6 +102,10 @@ module Mondrian
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
class Hierarchy
|
106
|
+
attr_reader :dimension
|
107
|
+
end
|
108
|
+
|
105
109
|
INTERNAL_PROPS = Set.new(['CATALOG_NAME', 'SCHEMA_NAME', 'CUBE_NAME', 'DIMENSION_UNIQUE_NAME', 'HIERARCHY_UNIQUE_NAME', 'LEVEL_UNIQUE_NAME', 'LEVEL_NUMBER', 'MEMBER_ORDINAL', 'MEMBER_NAME', 'MEMBER_UNIQUE_NAME', 'MEMBER_TYPE', 'MEMBER_GUID', 'MEMBER_CAPTION', 'CHILDREN_CARDINALITY', 'PARENT_LEVEL', 'PARENT_UNIQUE_NAME', 'PARENT_COUNT', 'DESCRIPTION', '$visible', 'MEMBER_KEY', 'IS_PLACEHOLDERMEMBER', 'IS_DATAMEMBER', 'DEPTH', 'DISPLAY_INFO', 'VALUE', '$scenario', 'CELL_FORMATTER', 'CELL_FORMATTER_SCRIPT', 'CELL_FORMATTER_SCRIPT_LANGUAGE', 'DISPLAY_FOLDER', 'FORMAT_EXP', 'KEY', '$name']).freeze
|
106
110
|
|
107
111
|
class Level
|
@@ -111,13 +115,17 @@ module Mondrian
|
|
111
115
|
@full_name ||= @raw_level.getUniqueName
|
112
116
|
end
|
113
117
|
|
114
|
-
def
|
118
|
+
def unique_name
|
119
|
+
"#{Java::MondrianOlap::Util.quoteMdxIdentifier(hierarchy.dimension.name)}.#{Java::MondrianOlap::Util.quoteMdxIdentifier(hierarchy.name)}.#{Java::MondrianOlap::Util.quoteMdxIdentifier(self.name)}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_h(member_properties=[], get_children=false, member_caption=nil)
|
115
123
|
{
|
116
124
|
name: self.name,
|
117
125
|
caption: self.caption,
|
118
126
|
members: self.members
|
119
127
|
.uniq { |m| m.property_value('MEMBER_KEY') }
|
120
|
-
.map { |m| m.to_h(member_properties) },
|
128
|
+
.map { |m| m.to_h(member_properties, member_caption, get_children) },
|
121
129
|
:properties => self.own_props.map { |p|
|
122
130
|
p.getName
|
123
131
|
}
|
@@ -140,7 +148,7 @@ module Mondrian
|
|
140
148
|
@raw_member.getLevel
|
141
149
|
end
|
142
150
|
|
143
|
-
def to_h(properties=[], caption_property=nil)
|
151
|
+
def to_h(properties=[], caption_property=nil, get_children=false)
|
144
152
|
kv = [:name, :full_name, :all_member?,
|
145
153
|
:drillable?, :depth].map { |m|
|
146
154
|
[m, self.send(m)]
|
@@ -150,6 +158,7 @@ module Mondrian
|
|
150
158
|
kv << [:num_children, self.property_value('CHILDREN_CARDINALITY')]
|
151
159
|
kv << [:parent_name, self.property_value('PARENT_UNIQUE_NAME')]
|
152
160
|
kv << [:level_name, self.raw_level.name]
|
161
|
+
kv << [:children, get_children ? self.children.map { |c| c.to_h([], nil, get_children)} : []]
|
153
162
|
|
154
163
|
if properties.size > 0
|
155
164
|
kv << [
|
@@ -39,6 +39,11 @@ module Mondrian::REST
|
|
39
39
|
set_members = p.getArgList.map { |id_node|
|
40
40
|
get_member(cube, unparse_node(id_node))
|
41
41
|
}
|
42
|
+
|
43
|
+
if set_members.any? { |m| m.nil? }
|
44
|
+
error!("Illegal cut. Unknown member in cut set", 400)
|
45
|
+
end
|
46
|
+
|
42
47
|
ls = set_members.map(&:raw_level).uniq
|
43
48
|
unless ls.size == 1
|
44
49
|
error!("Illegal cut: " + cut_expr, 400)
|
@@ -107,7 +112,7 @@ module Mondrian::REST
|
|
107
112
|
|
108
113
|
if s.size > 1
|
109
114
|
if s.size == 3 # 3 parts, means that a hierarchy was provided
|
110
|
-
hierarchy = dimension.hierarchies.find { |h_| h_.name ==
|
115
|
+
hierarchy = dimension.hierarchies.find { |h_| h_.name == s[1].name }
|
111
116
|
if hierarchy.nil?
|
112
117
|
error!("Hierarchy `#{s[1].name}` does not exist in dimension #{dimension.name}", 404)
|
113
118
|
end
|
@@ -136,7 +141,7 @@ module Mondrian::REST
|
|
136
141
|
cm_names = measure_members.map(&:name)
|
137
142
|
|
138
143
|
options['measures'].each { |m|
|
139
|
-
error!("Measure #{m} does not exist in cube #{cube.name}",
|
144
|
+
error!("Measure #{m} does not exist in cube #{cube.name}", 400) unless cm_names.include?(m)
|
140
145
|
}
|
141
146
|
|
142
147
|
# measures go in axis(0) of the resultset
|
@@ -190,7 +195,7 @@ module Mondrian::REST
|
|
190
195
|
raise "Unsupported operation"
|
191
196
|
end
|
192
197
|
else
|
193
|
-
qa.
|
198
|
+
qa.unique_name + '.Members'
|
194
199
|
end
|
195
200
|
end
|
196
201
|
|
data/lib/mondrian_rest.rb
CHANGED
data/mondrian-rest.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = "mondrian-rest"
|
4
|
-
s.version = '0.
|
4
|
+
s.version = '0.7.0'
|
5
5
|
s.authors = ["Manuel Aristarán"]
|
6
6
|
s.email = ["manuel@jazzido.com"]
|
7
7
|
s.homepage = "https://github.com/jazzido/mondrian-rest"
|
@@ -12,12 +12,15 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.platform = 'java'
|
13
13
|
|
14
14
|
s.files = `git ls-files`.split("\n").reject { |f| f =~ /^spec\// }
|
15
|
-
s.require_paths = ["lib"]
|
15
|
+
s.require_paths = ["lib", "lib/jars"]
|
16
|
+
|
17
|
+
s.requirements << 'jar no.ssb.jsonstat:json-stat-java, 0.2.2'
|
16
18
|
|
17
19
|
s.add_runtime_dependency 'mondrian-olap', ["~> 0.8.0"]
|
18
|
-
s.add_runtime_dependency 'grape', ["~> 0.
|
20
|
+
s.add_runtime_dependency 'grape', ["~> 1.0.0"]
|
19
21
|
s.add_runtime_dependency 'writeexcel', '~> 1.0', '>= 1.0.5'
|
20
22
|
|
23
|
+
s.add_development_dependency "jar-dependencies", "~> 0.3.2"
|
21
24
|
s.add_development_dependency 'rake'
|
22
25
|
s.add_development_dependency 'rspec'
|
23
26
|
s.add_development_dependency 'jdbc-derby'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mondrian-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Manuel Aristarán
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
32
|
+
version: 1.0.0
|
33
33
|
name: grape
|
34
34
|
prerelease: false
|
35
35
|
type: :runtime
|
@@ -37,7 +37,7 @@ dependencies:
|
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 1.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
@@ -58,6 +58,20 @@ dependencies:
|
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 1.0.5
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.3.2
|
67
|
+
name: jar-dependencies
|
68
|
+
prerelease: false
|
69
|
+
type: :development
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.3.2
|
61
75
|
- !ruby/object:Gem::Dependency
|
62
76
|
requirement: !ruby/object:Gem::Requirement
|
63
77
|
requirements:
|
@@ -154,10 +168,26 @@ files:
|
|
154
168
|
- Gemfile
|
155
169
|
- README.md
|
156
170
|
- Rakefile
|
171
|
+
- lib/jars/com/codepoetics/protonpack/1.9/protonpack-1.9.jar
|
172
|
+
- lib/jars/com/fasterxml/jackson/core/jackson-annotations/2.8.0/jackson-annotations-2.8.0.jar
|
173
|
+
- lib/jars/com/fasterxml/jackson/core/jackson-core/2.8.5/jackson-core-2.8.5.jar
|
174
|
+
- lib/jars/com/fasterxml/jackson/core/jackson-databind/2.8.5/jackson-databind-2.8.5.jar
|
175
|
+
- lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-guava/2.8.5/jackson-datatype-guava-2.8.5.jar
|
176
|
+
- lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.8.5/jackson-datatype-jdk8-2.8.5.jar
|
177
|
+
- lib/jars/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.8.5/jackson-datatype-jsr310-2.8.5.jar
|
178
|
+
- lib/jars/com/google/guava/guava/19.0/guava-19.0.jar
|
179
|
+
- lib/jars/me/yanaga/guava-stream/1.0/guava-stream-1.0.jar
|
180
|
+
- lib/jars/mondrian-rest_jars.rb
|
181
|
+
- lib/jars/no/ssb/jsonstat/json-stat-java/0.2.2/json-stat-java-0.2.2.jar
|
157
182
|
- lib/mondrian_rest.rb
|
158
183
|
- lib/mondrian_rest/api.rb
|
159
184
|
- lib/mondrian_rest/api_formatters.rb
|
160
185
|
- lib/mondrian_rest/api_helpers.rb
|
186
|
+
- lib/mondrian_rest/formatters/aggregation_json.rb
|
187
|
+
- lib/mondrian_rest/formatters/csv.rb
|
188
|
+
- lib/mondrian_rest/formatters/excel.rb
|
189
|
+
- lib/mondrian_rest/formatters/jsonrecords.rb
|
190
|
+
- lib/mondrian_rest/formatters/jsonstat.rb
|
161
191
|
- lib/mondrian_rest/mondrian_ext.rb
|
162
192
|
- lib/mondrian_rest/nest.rb
|
163
193
|
- lib/mondrian_rest/query_helper.rb
|
@@ -171,6 +201,7 @@ post_install_message:
|
|
171
201
|
rdoc_options: []
|
172
202
|
require_paths:
|
173
203
|
- lib
|
204
|
+
- lib/jars
|
174
205
|
required_ruby_version: !ruby/object:Gem::Requirement
|
175
206
|
requirements:
|
176
207
|
- - ">="
|
@@ -181,7 +212,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
212
|
- - ">="
|
182
213
|
- !ruby/object:Gem::Version
|
183
214
|
version: '0'
|
184
|
-
requirements:
|
215
|
+
requirements:
|
216
|
+
- jar no.ssb.jsonstat:json-stat-java, 0.2.2
|
185
217
|
rubyforge_project:
|
186
218
|
rubygems_version: 2.6.8
|
187
219
|
signing_key:
|