11# TODO: test
22import logging
33from typing import Optional , Tuple
4+ from enum import Enum
45
56import yaml
67
8+
9+ class AuthType (Enum ):
10+ CREDENTIALS = 1
11+ LOGIN_PASSWORD = 2
12+
13+
714from redash .query_runner import (
815 TYPE_BOOLEAN ,
916 TYPE_DATETIME ,
@@ -68,9 +75,12 @@ class PowerBIDAX(BaseHTTPQueryRunner):
6875 requires_url = False
6976 url_title = "Power BI URL"
7077 username_title = "Username"
71- password_title = "Password"
78+ password_title = "Password/Token "
7279 default_url = "https://api.powerbi.com/v1.0/myorg"
7380 default_scopes = '["https://analysis.windows.net/powerbi/api/.default"]'
81+ default_authority_url = (
82+ "https://login.microsoftonline.com/<tenant name/yourdomain.com>"
83+ )
7484
7585 @classmethod
7686 def configuration_schema (cls ):
@@ -83,7 +93,7 @@ def configuration_schema(cls):
8393 "authority_url" : {
8494 "type" : "string" ,
8595 "title" : cls .authority_url_title ,
86- "default" : "https://login.microsoftonline.com/<tenant name/yourdomain.com>" ,
96+ "default" : cls . default_authority_url ,
8797 },
8898 "scopes" : {
8999 "type" : "string" ,
@@ -96,6 +106,7 @@ def configuration_schema(cls):
96106 "client_id" ,
97107 "authority_url" ,
98108 ]
109+ schema ["required" ].remove ("username" )
99110 return schema
100111
101112 @classmethod
@@ -112,7 +123,6 @@ def __init__(self, *args, **kwargs):
112123 self .configuration ["url" ] = self .configuration .get ("url" , self .default_url )
113124 scopes = self .configuration .get ("scopes" , self .default_scopes )
114125 self .configuration ["scopes" ] = scopes
115- self .configuration ["scopes_array" ] = json_loads (scopes )
116126
117127 def test_connection (self ):
118128 _ , error = self .get_response ("/availableFeatures" )
@@ -122,18 +132,49 @@ def test_connection(self):
122132 def get_auth (self ):
123133 return None
124134
135+ def get_credentials (self ):
136+ username = self .configuration .get ("username" )
137+ password = self .configuration .get ("password" )
138+ if password :
139+ return (username , password )
140+ if self .requires_authentication :
141+ raise ValueError ("Username and Password or Token required" )
142+ else :
143+ return None
144+
125145 def get_authorization (self ):
126146 client_id = self .configuration ["client_id" ]
127147 authority_url = self .configuration ["authority_url" ]
148+ self .configuration ["scopes_array" ] = json_loads (self .configuration ["scopes" ])
128149 scopes = self .configuration ["scopes_array" ]
129- username , password = super ().get_auth ()
130- app = msal .PublicClientApplication (client_id = client_id , authority = authority_url )
131- result = app .acquire_token_by_username_password (
132- username = username ,
133- password = password ,
134- scopes = scopes ,
135- )
150+ username , password = self .get_credentials ()
151+ if self .configuration .get ("username" ) is None :
152+ self .auth_type = AuthType .CREDENTIALS
153+ else :
154+ self .auth_type = AuthType .LOGIN_PASSWORD
155+ if self .auth_type == AuthType .CREDENTIALS :
156+ app = msal .ConfidentialClientApplication (
157+ authority = authority_url ,
158+ client_id = client_id ,
159+ client_credential = password ,
160+ )
161+ result = app .acquire_token_for_client (
162+ scopes = scopes ,
163+ )
164+ elif self .auth_type == AuthType .LOGIN_PASSWORD :
165+ app = msal .PublicClientApplication (
166+ authority = authority_url ,
167+ client_id = client_id ,
168+ )
169+ result = app .acquire_token_by_username_password (
170+ username = username ,
171+ password = password ,
172+ scopes = scopes ,
173+ )
174+ if "error" in result :
175+ raise ValueError (f"Couldn't acquire token: { result } " )
136176 access_token = result ["access_token" ]
177+ logger .debug (result )
137178 return f"Bearer { access_token } "
138179
139180 def get_response (self , url : str , auth = None , http_method = "get" , ** kwargs ):
0 commit comments