power-bi 2.3.0 → 2.4.0

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: c120eeef2ac35b02afcfff32690f003efad7fbfd59c7f2bd714a6d0659b1ed44
4
- data.tar.gz: ee077e4fa8f6faae52ad5c1107fd96f9cb8bae5e7c8f0ef47a83009e7c80ce0d
3
+ metadata.gz: 90d415a89ed5860c82824f7e152e4c58d42054bc293f5534f6669774682a1ffe
4
+ data.tar.gz: 536d4c7cea48c859c7b780db751da15375b060a45c59946a2335887d8e948494
5
5
  SHA512:
6
- metadata.gz: c9c742362808fb3fa08ad663ebbdaf34d916f932fa5bed8f66d657caf769c230a7694d82cd05555a642d41098ccb9fcf6eed9b6f89233ca344ff3f1c3d02b6fb
7
- data.tar.gz: dc28a5e89d26365b7b48d5f6538bd1e8c12a9fe45c81b4f216ae63074ced3b728f31395c98751ee571a9e7234315693af5f6a7cffe4d86c398f6ec6683cfeafe
6
+ metadata.gz: d31db71feb4b03d7810d5805b3d48016e89ead1782bfae66269aa2a0c55340e6cf21031525ffa6d5b917cdc36041670ef52c1ccff499ebdfc7d0051d8005b6a8
7
+ data.tar.gz: 1ea49ae1a15e45b16c21b9da86c8ac20abb9e96a6eb63e6a0ed9a7dfcbca25dfff8431896703f2d55736a148668cf4b85ac591eb20c68bf1b2d9f6baee455e3a
data/README.md CHANGED
@@ -62,7 +62,9 @@ Once authenticated, you can set profiles like this:
62
62
  pbi.profile = profile
63
63
  ```
64
64
 
65
- Every action executed after setting the profile, will be executed _through the eyes of the profile_. This way, you can create an isolated multi-tenant setup. Using profiles, simplifies the internal organization of Power BI and allows faster interaction with Power BI. This also lifts the 1000-workspaces limit that is imposed on Master Userss and Service Principals
65
+ Every action executed after setting the profile, will be executed _through the eyes of the profile_. This way, you can create an isolated multi-tenant setup. Using profiles, simplifies the internal organization of Power BI and allows faster interaction with Power BI. This also lifts the 1000-workspaces limit that is imposed on Master Users and Service Principals
66
+
67
+ Note: when working with Service principal profiles (SPP), you need to add the SPP to the gateway datasource before binding the gateway datasource to the dataset.
66
68
 
67
69
  # Supported endpoints
68
70
 
@@ -122,6 +124,11 @@ Note 2: to limit the number of API calls, it is best to directly use the _getter
122
124
  * Create a new gateway datasource: `gateway.gateway_datasource.create(name, credentials, db_server, db_name)`
123
125
  * Delete a new gateway datasource: `gateway_datasource.delete`
124
126
 
127
+ ## Gateway datasource users
128
+
129
+ * List datasource users in a gateway datasource: `gateway_datasource.gateway_datasource_users`
130
+ * Add a Service principal profile to a gateway datasource: `gateway_datasource.add_service_principal_profile_user(profile_id, principal_object_id)`
131
+
125
132
  ## Capacities
126
133
 
127
134
  Note: Capacities are Azure creatures, you can't create them in Power BI.
@@ -8,7 +8,7 @@ module PowerBI
8
8
  end
9
9
 
10
10
  def get_data(id)
11
- @tenant.get("/gateways/#{id}")
11
+ @tenant.get("/gateways/#{id}", use_profile: false)
12
12
  end
13
13
 
14
14
  def data_to_attributes(data)
@@ -32,7 +32,7 @@ module PowerBI
32
32
  end
33
33
 
34
34
  def get_data
35
- @tenant.get("/gateways")[:value]
35
+ @tenant.get("/gateways", use_profile: false)[:value]
36
36
  end
37
37
  end
38
38
  end
@@ -1,14 +1,15 @@
1
1
  module PowerBI
2
2
  class GatewayDatasource < Object
3
- attr_reader :gateway
3
+ attr_reader :gateway, :gateway_datasource_users
4
4
 
5
5
  def initialize(tenant, parent, id = nil)
6
6
  super(tenant, id)
7
7
  @gateway = parent
8
+ @gateway_datasource_users = GatewayDatasourceUserArray.new(@tenant, self)
8
9
  end
9
10
 
10
11
  def get_data(id)
11
- @tenant.get("/gateways/#{@gateway.id}/datasources/#{id}")
12
+ @tenant.get("/gateways/#{@gateway.id}/datasources/#{id}", use_profile: false)
12
13
  end
13
14
 
14
15
  def data_to_attributes(data)
@@ -24,7 +25,7 @@ module PowerBI
24
25
  end
25
26
 
26
27
  def update_credentials(encrypted_credentials)
27
- @tenant.patch("/gateways/#{gateway.id}/datasources/#{id}") do |req|
28
+ @tenant.patch("/gateways/#{gateway.id}/datasources/#{id}", use_profile: false) do |req|
28
29
  req.body = {
29
30
  credentialDetails: {
30
31
  credentialType: "Basic",
@@ -41,7 +42,7 @@ module PowerBI
41
42
  end
42
43
 
43
44
  def delete
44
- @tenant.delete("/gateways/#{gateway.id}/datasources/#{id}")
45
+ @tenant.delete("/gateways/#{gateway.id}/datasources/#{id}", use_profile: false)
45
46
  @gateway.gateway_datasources.reload
46
47
  true
47
48
  end
@@ -61,7 +62,7 @@ module PowerBI
61
62
 
62
63
  # only MySQL type is currently supported
63
64
  def create(name, encrypted_credentials, db_server, db_name)
64
- data = @tenant.post("/gateways/#{@gateway.id}/datasources",) do |req|
65
+ data = @tenant.post("/gateways/#{@gateway.id}/datasources", use_profile: false) do |req|
65
66
  req.body = {
66
67
  connectionDetails: {server: db_server, database: db_name}.to_json,
67
68
  credentialDetails: {
@@ -82,7 +83,7 @@ module PowerBI
82
83
  end
83
84
 
84
85
  def get_data
85
- @tenant.get("/gateways/#{@gateway.id}/datasources")[:value]
86
+ @tenant.get("/gateways/#{@gateway.id}/datasources", use_profile: false)[:value]
86
87
  end
87
88
  end
88
89
  end
@@ -0,0 +1,51 @@
1
+ module PowerBI
2
+ class GatewayDatasourceUser < Object
3
+ attr_reader :gateway_datasource
4
+
5
+ def initialize(tenant, parent, id = nil)
6
+ super(tenant, id)
7
+ @gateway_datasource = parent
8
+ end
9
+
10
+ def data_to_attributes(data)
11
+ {
12
+ datasource_access_right: data[:datasourceAccessRight],
13
+ display_name: data[:displayName],
14
+ email_address: data[:emailAddress],
15
+ identifier: data[:identifier],
16
+ principal_type: data[:principalType],
17
+ profile: data[:profile],
18
+ }
19
+ end
20
+
21
+ end
22
+
23
+ class GatewayDatasourceUserArray < Array
24
+
25
+ def initialize(tenant, gateway_datasource)
26
+ super(tenant, gateway_datasource)
27
+ @gateway_datasource = gateway_datasource
28
+ end
29
+
30
+ def self.get_class
31
+ GatewayDatasourceUser
32
+ end
33
+
34
+ # service principal object ID: https://learn.microsoft.com/en-us/power-bi/developer/embedded/embedded-troubleshoot#what-is-the-difference-between-application-object-id-and-principal-object-id
35
+ def add_service_principal_profile_user(profile_id, principal_object_id, datasource_access_right: "Read")
36
+ @tenant.post("/gateways/#{@gateway_datasource.gateway.id}/datasources/#{@gateway_datasource.id}/users", use_profile: false) do |req|
37
+ req.body = {
38
+ datasourceAccessRight: datasource_access_right,
39
+ identifier: principal_object_id,
40
+ principalType: "App",
41
+ profile: {id: profile_id},
42
+ }.to_json
43
+ end
44
+ self.reload
45
+ end
46
+
47
+ def get_data
48
+ @tenant.get("/gateways/#{@gateway_datasource.gateway.id}/datasources/#{@gateway_datasource.id}/users", use_profile: false)[:value]
49
+ end
50
+ end
51
+ end
@@ -6,7 +6,7 @@ module PowerBI
6
6
  end
7
7
 
8
8
  def get_data(id)
9
- @tenant.get("/profiles/#{id}")
9
+ @tenant.get("/profiles/#{id}", use_profile: false)
10
10
  end
11
11
 
12
12
  def data_to_attributes(data)
@@ -17,7 +17,7 @@ module PowerBI
17
17
  end
18
18
 
19
19
  def delete
20
- @tenant.delete("/profiles/#{@id}")
20
+ @tenant.delete("/profiles/#{@id}", use_profile: false)
21
21
  @tenant.profiles.reload
22
22
  true
23
23
  end
@@ -30,7 +30,7 @@ module PowerBI
30
30
  end
31
31
 
32
32
  def create(name)
33
- data = @tenant.post("/profiles") do |req|
33
+ data = @tenant.post("/profiles", use_profile: false) do |req|
34
34
  req.body = {displayName: name}.to_json
35
35
  end
36
36
  self.reload
@@ -38,7 +38,7 @@ module PowerBI
38
38
  end
39
39
 
40
40
  def get_data
41
- @tenant.get("/profiles")[:value]
41
+ @tenant.get("/profiles", use_profile: false)[:value]
42
42
  end
43
43
  end
44
44
  end
@@ -1,6 +1,6 @@
1
1
  module PowerBI
2
2
  class Tenant
3
- attr_reader :workspaces, :gateways, :capacities, :profiles, :profile
3
+ attr_reader :workspaces, :gateways, :capacities, :profiles, :profile_id
4
4
 
5
5
  def initialize(token_generator, retries: 5, logger: nil)
6
6
  @token_generator = token_generator
@@ -9,7 +9,7 @@ module PowerBI
9
9
  @capacities = CapacityArray.new(self)
10
10
  @profiles = ProfileArray.new(self)
11
11
  @logger = logger
12
- @profile = nil
12
+ @profile_id = nil
13
13
 
14
14
  ## WHY RETRIES? ##
15
15
  # It is noticed that once in a while (~0.1% API calls), the Power BI server returns a 500 (internal server error) without apparent reason, just retrying works :-)
@@ -44,12 +44,16 @@ module PowerBI
44
44
  Capacity.new(self, nil, id)
45
45
  end
46
46
 
47
+ def profile(id)
48
+ Profile.new(self, nil, id)
49
+ end
50
+
47
51
  def profile=(profile)
48
- @profile = profile
52
+ @profile_id = profile.is_a?(String) ? profile : profile&.id
49
53
  @workspaces.reload # we need to reload the workspaces because we look through the eyes of the profile
50
54
  end
51
55
 
52
- def get(url, params = {})
56
+ def get(url, params = {}, use_profile: true)
53
57
  t0 = Time.now
54
58
  conn = Faraday.new do |f|
55
59
  f.request :retry, @retry_options
@@ -58,8 +62,8 @@ module PowerBI
58
62
  req.params = params
59
63
  req.headers['Accept'] = 'application/json'
60
64
  req.headers['authorization'] = "Bearer #{token}"
61
- if @profile
62
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
65
+ if use_profile
66
+ add_spp_header(req)
63
67
  end
64
68
  yield req if block_given?
65
69
  end
@@ -75,7 +79,7 @@ module PowerBI
75
79
  end
76
80
  end
77
81
 
78
- def get_raw(url, params = {})
82
+ def get_raw(url, params = {}, use_profile: true)
79
83
  t0 = Time.now
80
84
  conn = Faraday.new do |f|
81
85
  f.request :retry, @retry_options
@@ -83,8 +87,8 @@ module PowerBI
83
87
  response = conn.get(PowerBI::BASE_URL + url) do |req|
84
88
  req.params = params
85
89
  req.headers['authorization'] = "Bearer #{token}"
86
- if @profile
87
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
90
+ if use_profile
91
+ add_spp_header(req)
88
92
  end
89
93
  yield req if block_given?
90
94
  end
@@ -95,7 +99,7 @@ module PowerBI
95
99
  response.body
96
100
  end
97
101
 
98
- def post(url, params = {})
102
+ def post(url, params = {}, use_profile: true)
99
103
  t0 = Time.now
100
104
  conn = Faraday.new do |f|
101
105
  f.request :retry, @retry_options
@@ -105,8 +109,8 @@ module PowerBI
105
109
  req.headers['Accept'] = 'application/json'
106
110
  req.headers['Content-Type'] = 'application/json'
107
111
  req.headers['authorization'] = "Bearer #{token}"
108
- if @profile
109
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
112
+ if use_profile
113
+ add_spp_header(req)
110
114
  end
111
115
  yield req if block_given?
112
116
  end
@@ -119,7 +123,7 @@ module PowerBI
119
123
  end
120
124
  end
121
125
 
122
- def patch(url, params = {})
126
+ def patch(url, params = {}, use_profile: true)
123
127
  t0 = Time.now
124
128
  conn = Faraday.new do |f|
125
129
  f.request :retry, @retry_options
@@ -129,8 +133,8 @@ module PowerBI
129
133
  req.headers['Accept'] = 'application/json'
130
134
  req.headers['Content-Type'] = 'application/json'
131
135
  req.headers['authorization'] = "Bearer #{token}"
132
- if @profile
133
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
136
+ if use_profile
137
+ add_spp_header(req)
134
138
  end
135
139
  yield req if block_given?
136
140
  end
@@ -143,7 +147,7 @@ module PowerBI
143
147
  end
144
148
  end
145
149
 
146
- def delete(url, params = {})
150
+ def delete(url, params = {}, use_profile: true)
147
151
  t0 = Time.now
148
152
  conn = Faraday.new do |f|
149
153
  f.request :retry, @retry_options
@@ -152,8 +156,8 @@ module PowerBI
152
156
  req.params = params
153
157
  req.headers['Accept'] = 'application/json'
154
158
  req.headers['authorization'] = "Bearer #{token}"
155
- if @profile
156
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
159
+ if use_profile
160
+ add_spp_header(req)
157
161
  end
158
162
  yield req if block_given?
159
163
  end
@@ -169,7 +173,7 @@ module PowerBI
169
173
  end
170
174
  end
171
175
 
172
- def post_file(url, file, params = {})
176
+ def post_file(url, file, params = {}, use_profile: true)
173
177
  t0 = Time.now
174
178
  conn = Faraday.new do |f|
175
179
  f.request :multipart
@@ -180,8 +184,8 @@ module PowerBI
180
184
  req.headers['Accept'] = 'application/json'
181
185
  req.headers['Content-Type'] = 'multipart/form-data'
182
186
  req.headers['authorization'] = "Bearer #{token}"
183
- if @profile
184
- req.headers['X-PowerBI-Profile-Id'] = @profile.id
187
+ if use_profile
188
+ add_spp_header(req)
185
189
  end
186
190
  req.body = {value: Faraday::UploadIO.new(file, 'application/octet-stream')}
187
191
  req.options.timeout = 120 # default is 60 seconds Net::ReadTimeout
@@ -195,6 +199,12 @@ module PowerBI
195
199
 
196
200
  private
197
201
 
202
+ def add_spp_header(req)
203
+ if @profile_id
204
+ req.headers['X-PowerBI-Profile-Id'] = @profile_id
205
+ end
206
+ end
207
+
198
208
  def token
199
209
  @token_generator.call
200
210
  end
data/lib/power-bi.rb CHANGED
@@ -22,6 +22,7 @@ require_relative "power-bi/parameter"
22
22
  require_relative "power-bi/refresh"
23
23
  require_relative "power-bi/gateway"
24
24
  require_relative "power-bi/gateway_datasource"
25
+ require_relative "power-bi/gateway_datasource_user"
25
26
  require_relative "power-bi/page"
26
27
  require_relative "power-bi/user"
27
28
  require_relative "power-bi/capacity"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: power-bi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lode Cools
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-04 00:00:00.000000000 Z
11
+ date: 2024-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -95,6 +95,7 @@ files:
95
95
  - lib/power-bi/datasource.rb
96
96
  - lib/power-bi/gateway.rb
97
97
  - lib/power-bi/gateway_datasource.rb
98
+ - lib/power-bi/gateway_datasource_user.rb
98
99
  - lib/power-bi/object.rb
99
100
  - lib/power-bi/page.rb
100
101
  - lib/power-bi/parameter.rb