castor-extractor 0.5.3__py3-none-any.whl → 0.5.6__py3-none-any.whl

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.

Potentially problematic release.


This version of castor-extractor might be problematic. Click here for more details.

Files changed (85) hide show
  1. CHANGELOG.md +15 -0
  2. castor_extractor/commands/extract_bigquery.py +3 -1
  3. castor_extractor/commands/extract_looker.py +4 -1
  4. castor_extractor/commands/extract_metabase_api.py +3 -1
  5. castor_extractor/commands/extract_metabase_db.py +6 -2
  6. castor_extractor/commands/extract_mode.py +6 -2
  7. castor_extractor/commands/extract_powerbi.py +4 -1
  8. castor_extractor/commands/extract_snowflake.py +4 -2
  9. castor_extractor/commands/extract_tableau.py +4 -1
  10. castor_extractor/commands/file_check.py +3 -1
  11. castor_extractor/commands/upload.py +5 -2
  12. castor_extractor/file_checker/file_test.py +6 -3
  13. castor_extractor/file_checker/templates/generic_warehouse.py +4 -2
  14. castor_extractor/transformation/dbt/client/credentials.py +1 -1
  15. castor_extractor/types.py +2 -1
  16. castor_extractor/uploader/upload.py +4 -2
  17. castor_extractor/uploader/upload_test.py +0 -1
  18. castor_extractor/utils/deprecate.py +1 -1
  19. castor_extractor/utils/files_test.py +2 -2
  20. castor_extractor/utils/formatter_test.py +0 -1
  21. castor_extractor/utils/pager.py +4 -2
  22. castor_extractor/utils/pager_test.py +1 -1
  23. castor_extractor/utils/retry.py +1 -1
  24. castor_extractor/utils/safe.py +1 -1
  25. castor_extractor/utils/string_test.py +0 -1
  26. castor_extractor/utils/validation.py +4 -3
  27. castor_extractor/visualization/looker/api/client.py +26 -9
  28. castor_extractor/visualization/looker/api/client_test.py +3 -2
  29. castor_extractor/visualization/looker/api/constants.py +3 -1
  30. castor_extractor/visualization/looker/api/utils.py +3 -2
  31. castor_extractor/visualization/looker/assets.py +1 -0
  32. castor_extractor/visualization/looker/constant.py +1 -1
  33. castor_extractor/visualization/looker/extract.py +6 -1
  34. castor_extractor/visualization/metabase/client/api/client.py +2 -1
  35. castor_extractor/visualization/metabase/client/api/credentials.py +1 -1
  36. castor_extractor/visualization/metabase/client/db/client.py +4 -3
  37. castor_extractor/visualization/metabase/client/db/credentials.py +2 -2
  38. castor_extractor/visualization/metabase/client/decryption_test.py +0 -1
  39. castor_extractor/visualization/metabase/extract.py +4 -4
  40. castor_extractor/visualization/mode/client/client.py +2 -1
  41. castor_extractor/visualization/mode/client/client_test.py +4 -3
  42. castor_extractor/visualization/mode/client/credentials.py +2 -2
  43. castor_extractor/visualization/powerbi/client/constants.py +1 -1
  44. castor_extractor/visualization/powerbi/client/credentials.py +0 -1
  45. castor_extractor/visualization/powerbi/client/credentials_test.py +11 -3
  46. castor_extractor/visualization/powerbi/client/rest.py +15 -5
  47. castor_extractor/visualization/powerbi/client/rest_test.py +40 -13
  48. castor_extractor/visualization/powerbi/extract.py +4 -3
  49. castor_extractor/visualization/qlik/client/engine/client.py +3 -1
  50. castor_extractor/visualization/qlik/client/engine/json_rpc.py +4 -1
  51. castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -1
  52. castor_extractor/visualization/qlik/client/master.py +11 -4
  53. castor_extractor/visualization/qlik/client/rest_test.py +3 -2
  54. castor_extractor/visualization/sigma/client/client.py +7 -3
  55. castor_extractor/visualization/sigma/client/client_test.py +4 -2
  56. castor_extractor/visualization/sigma/client/credentials.py +2 -2
  57. castor_extractor/visualization/sigma/constants.py +1 -1
  58. castor_extractor/visualization/sigma/extract.py +3 -1
  59. castor_extractor/visualization/tableau/client/client.py +7 -5
  60. castor_extractor/visualization/tableau/client/client_utils.py +6 -3
  61. castor_extractor/visualization/tableau/client/credentials.py +6 -4
  62. castor_extractor/visualization/tableau/client/project.py +3 -1
  63. castor_extractor/visualization/tableau/client/safe_mode.py +2 -1
  64. castor_extractor/visualization/tableau/extract.py +7 -7
  65. castor_extractor/visualization/tableau/gql_fields.py +4 -4
  66. castor_extractor/visualization/tableau/tests/unit/graphql/paginated_object_test.py +2 -1
  67. castor_extractor/visualization/tableau/tests/unit/rest_api/auth_test.py +6 -3
  68. castor_extractor/visualization/tableau/tests/unit/rest_api/credentials_test.py +1 -1
  69. castor_extractor/visualization/tableau/tests/unit/rest_api/usages_test.py +2 -1
  70. castor_extractor/warehouse/abstract/extract.py +3 -2
  71. castor_extractor/warehouse/abstract/time_filter_test.py +0 -1
  72. castor_extractor/warehouse/bigquery/client_test.py +1 -1
  73. castor_extractor/warehouse/bigquery/extract.py +3 -2
  74. castor_extractor/warehouse/bigquery/query.py +4 -3
  75. castor_extractor/warehouse/postgres/extract.py +5 -3
  76. castor_extractor/warehouse/redshift/client_test.py +0 -1
  77. castor_extractor/warehouse/redshift/extract.py +5 -3
  78. castor_extractor/warehouse/snowflake/client.py +1 -1
  79. castor_extractor/warehouse/snowflake/client_test.py +1 -1
  80. castor_extractor/warehouse/snowflake/extract.py +5 -3
  81. castor_extractor/warehouse/synapse/extract.py +1 -1
  82. {castor_extractor-0.5.3.dist-info → castor_extractor-0.5.6.dist-info}/METADATA +2 -2
  83. {castor_extractor-0.5.3.dist-info → castor_extractor-0.5.6.dist-info}/RECORD +85 -85
  84. {castor_extractor-0.5.3.dist-info → castor_extractor-0.5.6.dist-info}/WHEEL +0 -0
  85. {castor_extractor-0.5.3.dist-info → castor_extractor-0.5.6.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,6 @@ from .decryption import decrypt
2
2
 
3
3
 
4
4
  def test_decrypt():
5
-
6
5
  key = "8iY5gyHxPH0YYyBgOd2AvwT1pcHl3EGtvN5jAi9JwoA"
7
6
  enc_message = "sIqpGK6UgzKOkThtlvBGqkb046EtB+HxcBsO3nDiKZAJcszfqqxTgSyH+SXAALznfuMSnZjdX9yzpGe77+ByYuCVlXHkMilkUe6tkFsFkBPW5CPirp0kqLdyp1yHXrv3NmXCtGZcef2fC0v89huRMSgFcm8M6Zf3JjSDEludLUo="
8
7
  dec_message = '{"db":"zip:/app/metabase.jar!/sample-dataset.db;USER=GUEST;PASSWORD=guest"}'
@@ -25,18 +25,18 @@ def iterate_all_data(
25
25
 
26
26
  yield MetabaseAsset.USER, deep_serialize(client.fetch(MetabaseAsset.USER))
27
27
  yield MetabaseAsset.COLLECTION, deep_serialize(
28
- client.fetch(MetabaseAsset.COLLECTION)
28
+ client.fetch(MetabaseAsset.COLLECTION),
29
29
  )
30
30
  yield MetabaseAsset.DATABASE, deep_serialize(
31
- client.fetch(MetabaseAsset.DATABASE)
31
+ client.fetch(MetabaseAsset.DATABASE),
32
32
  )
33
33
  yield MetabaseAsset.TABLE, deep_serialize(client.fetch(MetabaseAsset.TABLE))
34
34
  yield MetabaseAsset.CARD, deep_serialize(client.fetch(MetabaseAsset.CARD))
35
35
  yield MetabaseAsset.DASHBOARD, deep_serialize(
36
- client.fetch(MetabaseAsset.DASHBOARD)
36
+ client.fetch(MetabaseAsset.DASHBOARD),
37
37
  )
38
38
  yield MetabaseAsset.DASHBOARD_CARDS, deep_serialize(
39
- client.fetch(MetabaseAsset.DASHBOARD_CARDS)
39
+ client.fetch(MetabaseAsset.DASHBOARD_CARDS),
40
40
  )
41
41
 
42
42
 
@@ -168,7 +168,8 @@ class Client:
168
168
  # why without workspace? because users can belong to several companies
169
169
  # example: https://modeanalytics.com/api/john_doe
170
170
  result = self._call(
171
- resource_name=mb["member_username"], with_workspace=False
171
+ resource_name=mb["member_username"],
172
+ with_workspace=False,
172
173
  )
173
174
  members.append(cast(Dict, result))
174
175
  return members
@@ -9,7 +9,7 @@ _WORKSPACE = "castor"
9
9
 
10
10
 
11
11
  def _dummy_client() -> Client:
12
- return Client(
12
+ return Client( # noqa: S106
13
13
  host=_HOST,
14
14
  workspace=_WORKSPACE,
15
15
  token="dummy-token",
@@ -41,10 +41,11 @@ def test__post_processing():
41
41
  client = _dummy_client()
42
42
  raw = load_file("client_test.json", __file__)
43
43
  result = client._post_processing(
44
- asset=ModeAnalyticsAsset.COLLECTION, data=[json.loads(raw)]
44
+ asset=ModeAnalyticsAsset.COLLECTION,
45
+ data=[json.loads(raw)],
45
46
  )
46
47
  collection = result[0]
47
48
  assert set(collection.keys()) == set(
48
- EXPORTED_FIELDS[ModeAnalyticsAsset.COLLECTION]
49
+ EXPORTED_FIELDS[ModeAnalyticsAsset.COLLECTION],
49
50
  )
50
51
  assert collection["creator"] == "john_doe"
@@ -9,8 +9,8 @@ from ....utils import from_env
9
9
  class CredentialsKey(Enum):
10
10
  HOST = "host"
11
11
  WORKSPACE = "workspace"
12
- TOKEN = "token"
13
- SECRET = "secret"
12
+ TOKEN = "token" # noqa: S105
13
+ SECRET = "secret" # noqa: S105
14
14
 
15
15
 
16
16
  CREDENTIALS_ENV: Dict[CredentialsKey, str] = {
@@ -58,7 +58,7 @@ class QueryParams:
58
58
 
59
59
 
60
60
  class Keys:
61
- ACCESS_TOKEN = "access_token"
61
+ ACCESS_TOKEN = "access_token" # noqa: S105
62
62
  ACTIVITY_EVENT_ENTITIES = "activityEventEntities"
63
63
  CONTINUATION_URI = "continuationUri"
64
64
  ID = "id"
@@ -14,7 +14,6 @@ class Credentials:
14
14
  secret: str,
15
15
  scopes: Optional[List[str]] = None,
16
16
  ):
17
-
18
17
  if scopes is None:
19
18
  scopes = [Urls.DEFAULT_SCOPE]
20
19
  self.tenant_id = tenant_id
@@ -9,19 +9,27 @@ def test_credentials():
9
9
 
10
10
  # no scopes provided
11
11
  credentials = Credentials(
12
- tenant_id=tenant_id, client_id=client_id, secret=secret
12
+ tenant_id=tenant_id,
13
+ client_id=client_id,
14
+ secret=secret,
13
15
  )
14
16
  assert credentials.scopes == [Urls.DEFAULT_SCOPE]
15
17
 
16
18
  # empty scopes
17
19
  credentials = Credentials(
18
- tenant_id=tenant_id, client_id=client_id, secret=secret, scopes=[]
20
+ tenant_id=tenant_id,
21
+ client_id=client_id,
22
+ secret=secret,
23
+ scopes=[],
19
24
  )
20
25
  assert credentials.scopes == []
21
26
 
22
27
  # with scopes
23
28
  scopes = ["foo"]
24
29
  credentials = Credentials(
25
- tenant_id=tenant_id, client_id=client_id, secret=secret, scopes=scopes
30
+ tenant_id=tenant_id,
31
+ client_id=client_id,
32
+ secret=secret,
33
+ scopes=scopes,
26
34
  )
27
35
  assert credentials.scopes == scopes
@@ -104,7 +104,11 @@ class Client:
104
104
  """
105
105
  logger.debug(f"Calling {method} on {url}")
106
106
  result = requests.request(
107
- method, url, headers=self._header(), params=params, data=data
107
+ method,
108
+ url,
109
+ headers=self._header(),
110
+ params=params,
111
+ data=data,
108
112
  )
109
113
  result.raise_for_status()
110
114
 
@@ -131,11 +135,16 @@ class Client:
131
135
  processor: Optional[Callable] = None,
132
136
  ) -> Any:
133
137
  return self._call(
134
- url, POST, params=params, data=data, processor=processor
138
+ url,
139
+ POST,
140
+ params=params,
141
+ data=data,
142
+ processor=processor,
135
143
  )
136
144
 
137
145
  def _workspace_ids(
138
- self, modified_since: Optional[datetime] = None
146
+ self,
147
+ modified_since: Optional[datetime] = None,
139
148
  ) -> List[str]:
140
149
  """
141
150
  Get workspaces ids from powerBI admin API.
@@ -191,7 +200,7 @@ class Client:
191
200
  break
192
201
  waiting_seconds += sleep_seconds
193
202
  logger.info(
194
- f"Waiting {sleep_seconds} sec for scan {scan_id} to be ready…"
203
+ f"Waiting {sleep_seconds} sec for scan {scan_id} to be ready…",
195
204
  )
196
205
  sleep(sleep_seconds)
197
206
  return False
@@ -249,7 +258,8 @@ class Client:
249
258
  return self._get(Urls.DASHBOARD)[Keys.VALUE]
250
259
 
251
260
  def _metadata(
252
- self, modified_since: Optional[datetime] = None
261
+ self,
262
+ modified_since: Optional[datetime] = None,
253
263
  ) -> Iterator[List[Dict]]:
254
264
  """
255
265
  Fetch metadata by workspace.
@@ -15,7 +15,9 @@ FAKE_SECRET = "MeThree"
15
15
 
16
16
  def _client() -> Client:
17
17
  creds = Credentials(
18
- tenant_id=FAKE_TENANT_ID, client_id=FAKE_CLIENT_ID, secret=FAKE_SECRET
18
+ tenant_id=FAKE_TENANT_ID,
19
+ client_id=FAKE_CLIENT_ID,
20
+ secret=FAKE_SECRET,
19
21
  )
20
22
  return Client(creds)
21
23
 
@@ -26,7 +28,6 @@ def _raise_http_error() -> None:
26
28
 
27
29
  @patch.object(msal, "ConfidentialClientApplication")
28
30
  def test__access_token(mock_app):
29
-
30
31
  # init mocks
31
32
  valid_response = {"access_token": "mock_token"}
32
33
  returning_valid_token = Mock(return_value=valid_response)
@@ -85,7 +86,7 @@ def test__get(mocked_access_token, mocked_request):
85
86
  def test__workspace_ids(_, mocked_request):
86
87
  client = _client()
87
88
  mocked_request.return_value = Mock(
88
- json=lambda: [{"id": 1000}, {"id": 1001}, {"id": 1003}]
89
+ json=lambda: [{"id": 1000}, {"id": 1001}, {"id": 1003}],
89
90
  )
90
91
  ids = client._workspace_ids()
91
92
  assert ids == [1000, 1001, 1003]
@@ -103,7 +104,11 @@ def test__workspace_ids(_, mocked_request):
103
104
  }
104
105
 
105
106
  mocked_request.assert_called_with(
106
- GET, Urls.WORKSPACE_IDS, data=None, headers=ANY, params=params
107
+ GET,
108
+ Urls.WORKSPACE_IDS,
109
+ data=None,
110
+ headers=ANY,
111
+ params=params,
107
112
  )
108
113
 
109
114
 
@@ -116,7 +121,11 @@ def test__post_default(_, mocked_request):
116
121
  data = {"bonjour": "hello"}
117
122
  client._post(url, params=params, data=data)
118
123
  mocked_request.assert_called_with(
119
- POST, url, headers=ANY, params=QueryParams.METADATA_SCAN, data=data
124
+ POST,
125
+ url,
126
+ headers=ANY,
127
+ params=QueryParams.METADATA_SCAN,
128
+ data=data,
120
129
  )
121
130
 
122
131
 
@@ -129,7 +138,10 @@ def test__post_with_processor(_, mocked_request):
129
138
  data = {"bonjour": "hello"}
130
139
  mocked_request.return_value = Mock(json=lambda: {"id": 1000})
131
140
  result = client._post(
132
- url, params=params, data=data, processor=lambda x: x.json()["id"]
141
+ url,
142
+ params=params,
143
+ data=data,
144
+ processor=lambda x: x.json()["id"],
133
145
  )
134
146
  assert result == 1000
135
147
 
@@ -139,11 +151,15 @@ def test__post_with_processor(_, mocked_request):
139
151
  def test__datasets(_, mocked_request):
140
152
  client = _client()
141
153
  mocked_request.return_value = Mock(
142
- json=lambda: {"value": [{"id": 1, "type": "dataset"}]}
154
+ json=lambda: {"value": [{"id": 1, "type": "dataset"}]},
143
155
  )
144
156
  datasets = client._datasets()
145
157
  mocked_request.assert_called_with(
146
- GET, Urls.DATASETS, data=None, headers=ANY, params=None
158
+ GET,
159
+ Urls.DATASETS,
160
+ data=None,
161
+ headers=ANY,
162
+ params=None,
147
163
  )
148
164
  assert datasets == [{"id": 1, "type": "dataset"}]
149
165
 
@@ -153,11 +169,15 @@ def test__datasets(_, mocked_request):
153
169
  def test__reports(_, mocked_request):
154
170
  client = _client()
155
171
  mocked_request.return_value = Mock(
156
- json=lambda: {"value": [{"id": 1, "type": "report"}]}
172
+ json=lambda: {"value": [{"id": 1, "type": "report"}]},
157
173
  )
158
174
  reports = client._reports()
159
175
  mocked_request.assert_called_with(
160
- GET, Urls.REPORTS, data=None, headers=ANY, params=None
176
+ GET,
177
+ Urls.REPORTS,
178
+ data=None,
179
+ headers=ANY,
180
+ params=None,
161
181
  )
162
182
  assert reports == [{"id": 1, "type": "report"}]
163
183
 
@@ -167,11 +187,15 @@ def test__reports(_, mocked_request):
167
187
  def test__dashboards(_, mocked_request):
168
188
  client = _client()
169
189
  mocked_request.return_value = Mock(
170
- json=lambda: {"value": [{"id": 1, "type": "dashboard"}]}
190
+ json=lambda: {"value": [{"id": 1, "type": "dashboard"}]},
171
191
  )
172
192
  dashboards = client._dashboards()
173
193
  mocked_request.assert_called_with(
174
- GET, Urls.DASHBOARD, data=None, headers=ANY, params=None
194
+ GET,
195
+ Urls.DASHBOARD,
196
+ data=None,
197
+ headers=ANY,
198
+ params=None,
175
199
  )
176
200
  assert dashboards == [{"id": 1, "type": "dashboard"}]
177
201
 
@@ -181,7 +205,10 @@ def test__dashboards(_, mocked_request):
181
205
  @patch.object(Client, "_wait_for_scan_result")
182
206
  @patch.object(Client, "_get_scan")
183
207
  def test__metadata(
184
- mocked_get_scan, mocked_wait, mocked_create_scan, mocked_workspace_ids
208
+ mocked_get_scan,
209
+ mocked_wait,
210
+ mocked_create_scan,
211
+ mocked_workspace_ids,
185
212
  ):
186
213
  mocked_workspace_ids.return_value = list(range(200))
187
214
  mocked_create_scan.return_value = 314
@@ -16,9 +16,7 @@ from .client import Client, Credentials
16
16
  def iterate_all_data(
17
17
  client: Client,
18
18
  ) -> Iterable[Tuple[PowerBiAsset, Union[List, dict]]]:
19
-
20
19
  for asset in PowerBiAsset:
21
-
22
20
  if asset in METADATA_ASSETS:
23
21
  continue
24
22
 
@@ -39,7 +37,10 @@ def extract_all(
39
37
  """
40
38
  _output_directory = output_directory or from_env(OUTPUT_DIR)
41
39
  creds = Credentials(
42
- tenant_id=tenant_id, client_id=client_id, secret=secret, scopes=scopes
40
+ tenant_id=tenant_id,
41
+ client_id=client_id,
42
+ secret=secret,
43
+ scopes=scopes,
43
44
  )
44
45
  client = Client(creds)
45
46
  ts = current_timestamp()
@@ -69,7 +69,9 @@ class EngineApiClient:
69
69
  return _list_measures(client, app_id_)
70
70
 
71
71
  with open_websocket(
72
- app_id=app_id, server_url=self.server_url, api_key=self.api_key
72
+ app_id=app_id,
73
+ server_url=self.server_url,
74
+ api_key=self.api_key,
73
75
  ) as websocket:
74
76
  json_rpc_client = JsonRpcClient(websocket=websocket)
75
77
  return _call(json_rpc_client, app_id)
@@ -25,7 +25,10 @@ class JsonRpcClient:
25
25
  self.call_id += 1
26
26
 
27
27
  def _format_message(
28
- self, method: JsonRpcMethod, handle: int, params: Optional[list] = None
28
+ self,
29
+ method: JsonRpcMethod,
30
+ handle: int,
31
+ params: Optional[list] = None,
29
32
  ) -> dict:
30
33
  params = params or list()
31
34
  message = {
@@ -6,7 +6,6 @@ from .websocket import WebsocketConnection
6
6
 
7
7
 
8
8
  def test_json_rpc_client__send_message():
9
-
10
9
  dummy_server_url = "toto.fr"
11
10
  dummy_api_key = "IAmNotATrueSecretKey"
12
11
  ws = WebsocketConnection(server_url=dummy_server_url, api_key=dummy_api_key)
@@ -21,7 +21,8 @@ class MissingAppsScopeError(Exception):
21
21
 
22
22
 
23
23
  def _include_app_external_id(
24
- data: ListedData, app_external_id: str
24
+ data: ListedData,
25
+ app_external_id: str,
25
26
  ) -> ListedData:
26
27
  _data = data.copy()
27
28
  for element in _data:
@@ -30,7 +31,9 @@ def _include_app_external_id(
30
31
 
31
32
 
32
33
  def _fetch_children_on_apps(
33
- apps: ListedData, fetch_callback: Callable, display_progress: bool
34
+ apps: ListedData,
35
+ fetch_callback: Callable,
36
+ display_progress: bool,
34
37
  ) -> ListedData:
35
38
  all_apps_data: ListedData = list()
36
39
  apps_iterator = apps if not display_progress else tqdm(apps)
@@ -66,7 +69,8 @@ class QlikMasterClient:
66
69
  )
67
70
 
68
71
  self.engine_api_client = EngineApiClient(
69
- server_url=self._server_url, api_key=self._api_key
72
+ server_url=self._server_url,
73
+ api_key=self._api_key,
70
74
  )
71
75
 
72
76
  def _fetch_lineage(self, apps: ListedData) -> ListedData:
@@ -78,7 +82,10 @@ class QlikMasterClient:
78
82
  return _fetch_children_on_apps(apps, callback, self.display_progress)
79
83
 
80
84
  def fetch(
81
- self, asset: QlikAsset, *, apps: Optional[ListedData] = None
85
+ self,
86
+ asset: QlikAsset,
87
+ *,
88
+ apps: Optional[ListedData] = None,
82
89
  ) -> ListedData:
83
90
  """
84
91
  Given a QlikAsset, returns the corresponding data using the
@@ -5,7 +5,9 @@ from .rest import RestApiClient
5
5
 
6
6
 
7
7
  def _check_called_once(
8
- client: RestApiClient, first_page_url: str, return_value: Optional[dict]
8
+ client: RestApiClient,
9
+ first_page_url: str,
10
+ return_value: Optional[dict],
9
11
  ):
10
12
  with patch.object(RestApiClient, "_call") as mock_call:
11
13
  mock_call.return_value = return_value
@@ -18,7 +20,6 @@ def _check_called_once(
18
20
 
19
21
 
20
22
  def test_rest_api_client_pager():
21
-
22
23
  dummy_server_url = "https://clic.kom"
23
24
  dummy_api_key = "i-am-the-key-dont-let-others-know-about"
24
25
 
@@ -25,7 +25,8 @@ class SigmaClient:
25
25
  self.headers: Optional[Dict[str, str]] = None
26
26
 
27
27
  def _get_token(self, token_api_path: str) -> Dict[str, str]:
28
- token_response = requests.post(
28
+ # TODO: ADD TIMEOUT
29
+ token_response = requests.post( # noqa: S113
29
30
  token_api_path,
30
31
  data={
31
32
  CredentialsKey.GRANT_TYPE.value: "client_credentials",
@@ -52,7 +53,8 @@ class SigmaClient:
52
53
 
53
54
  def _get(self, endpoint_url: str) -> dict:
54
55
  url = urljoin(self.host, endpoint_url)
55
- result = requests.get(url, headers=self._get_headers())
56
+ # TODO: add timeout
57
+ result = requests.get(url, headers=self._get_headers()) # noqa: S113
56
58
  try:
57
59
  return result.json()
58
60
  except:
@@ -77,7 +79,9 @@ class SigmaClient:
77
79
  yield from self._get_with_pagination(endpoint)
78
80
 
79
81
  def _per_page_get_elements(
80
- self, workbook_id: str, page_id: str
82
+ self,
83
+ workbook_id: str,
84
+ page_id: str,
81
85
  ) -> Iterator[dict]:
82
86
  endpoint = EndpointFactory.elements(workbook_id, page_id)
83
87
  yield from self._get_with_pagination(endpoint)
@@ -2,8 +2,10 @@ from unittest.mock import Mock, patch
2
2
 
3
3
  from .client import SigmaClient, SigmaCredentials
4
4
 
5
- FAKE_CREDENTIALS = SigmaCredentials(
6
- host="IamFake", client_id="MeTwo", api_token="MeThree"
5
+ FAKE_CREDENTIALS = SigmaCredentials( # noqa: S106
6
+ host="IamFake",
7
+ client_id="MeTwo",
8
+ api_token="MeThree",
7
9
  )
8
10
 
9
11
 
@@ -5,11 +5,11 @@ from enum import Enum
5
5
  class CredentialsKey(Enum):
6
6
  """Value enum object for the credentials"""
7
7
 
8
- CLIENT_SECRET = "client_secret"
8
+ CLIENT_SECRET = "client_secret" # noqa: S105
9
9
  CLIENT_ID = "client_id"
10
10
  HOST = "host"
11
11
  GRANT_TYPE = "grant_type"
12
- API_TOKEN = "api_token"
12
+ API_TOKEN = "api_token" # noqa: S105
13
13
 
14
14
 
15
15
  CLIENT_ALLOWED_KEYS = (
@@ -1,4 +1,4 @@
1
1
  # environment variable names
2
2
  HOST = "CASTOR_SIGMA_HOST"
3
- API_TOKEN = "CASTOR_SIGMA_API_TOKEN"
3
+ API_TOKEN = "CASTOR_SIGMA_API_TOKEN" # noqa: S105
4
4
  CLIENT_ID = "CASTOR_SIGMA_CLIENT_ID"
@@ -68,7 +68,9 @@ def extract_all(
68
68
  _api_token = api_token or from_env(API_TOKEN)
69
69
 
70
70
  credentials = SigmaCredentials(
71
- host=_host, client_id=_client_id, api_token=_api_token
71
+ host=_host,
72
+ client_id=_client_id,
73
+ api_token=_api_token,
72
74
  )
73
75
  client = SigmaClient(credentials=credentials)
74
76
 
@@ -30,7 +30,9 @@ class ApiClient:
30
30
  user=get_value(CredentialsKey.TABLEAU_USER, kwargs, True),
31
31
  password=get_value(CredentialsKey.TABLEAU_PASSWORD, kwargs, True),
32
32
  token_name=get_value(
33
- CredentialsKey.TABLEAU_TOKEN_NAME, kwargs, True
33
+ CredentialsKey.TABLEAU_TOKEN_NAME,
34
+ kwargs,
35
+ True,
34
36
  ),
35
37
  token=get_value(CredentialsKey.TABLEAU_TOKEN, kwargs, True),
36
38
  server_url=get_value(CredentialsKey.TABLEAU_SERVER_URL, kwargs),
@@ -54,7 +56,7 @@ class ApiClient:
54
56
  self._credentials.user,
55
57
  self._credentials.password,
56
58
  site_id=self._credentials.site_id,
57
- )
59
+ ),
58
60
  )
59
61
 
60
62
  def _pat_login(self) -> None:
@@ -64,7 +66,7 @@ class ApiClient:
64
66
  self._credentials.token_name,
65
67
  self._credentials.token,
66
68
  site_id=self._credentials.site_id,
67
- )
69
+ ),
68
70
  )
69
71
 
70
72
  def login(self) -> None:
@@ -80,7 +82,7 @@ class ApiClient:
80
82
 
81
83
  raise ValueError(
82
84
  """Wrong authentication: you should provide either user and password
83
- or personal access token"""
85
+ or personal access token""",
84
86
  )
85
87
 
86
88
  def base_url(self) -> str:
@@ -119,7 +121,7 @@ class ApiClient:
119
121
  [
120
122
  extract_asset(project, TableauAsset.PROJECT)
121
123
  for project in TSC.Pager(self._server.projects)
122
- ]
124
+ ],
123
125
  )
124
126
 
125
127
  def _fetch_workbooks_to_datasource(self) -> SerializedAsset:
@@ -23,9 +23,10 @@ RESOURCE_TEMPLATE = "{resource}Connection"
23
23
 
24
24
 
25
25
  def get_paginated_objects(
26
- server, asset: TableauAsset, page_size: int
26
+ server,
27
+ asset: TableauAsset,
28
+ page_size: int,
27
29
  ) -> SerializedAsset:
28
-
29
30
  assets: SerializedAsset = []
30
31
  for query in QUERY_FIELDS[asset]:
31
32
  fields = query["fields"].value
@@ -43,7 +44,9 @@ def get_paginated_objects(
43
44
 
44
45
 
45
46
  def query_scroll(
46
- server, query: str, resource: str
47
+ server,
48
+ query: str,
49
+ resource: str,
47
50
  ) -> Iterator[SerializedAsset]:
48
51
  """build a tableau query iterator handling pagination and cursor"""
49
52
 
@@ -13,9 +13,9 @@ class CredentialsKey(Enum):
13
13
  """Value enum object for the credentials"""
14
14
 
15
15
  TABLEAU_USER = "user"
16
- TABLEAU_PASSWORD = "password"
17
- TABLEAU_TOKEN_NAME = "token_name"
18
- TABLEAU_TOKEN = "token"
16
+ TABLEAU_PASSWORD = "password" # noqa: S105
17
+ TABLEAU_TOKEN_NAME = "token_name" # noqa: S105
18
+ TABLEAU_TOKEN = "token" # noqa: S105
19
19
  TABLEAU_SITE_ID = "site_id"
20
20
  TABLEAU_SERVER_URL = "server_url"
21
21
 
@@ -31,7 +31,9 @@ CREDENTIALS_ENV: Dict[CredentialsKey, str] = {
31
31
 
32
32
 
33
33
  def get_value(
34
- key: CredentialsKey, kwargs: dict, optional: bool = False
34
+ key: CredentialsKey,
35
+ kwargs: dict,
36
+ optional: bool = False,
35
37
  ) -> Optional[str]:
36
38
  """
37
39
  Returns the value of the given key:
@@ -4,7 +4,9 @@ from ....utils import SerializedAsset
4
4
 
5
5
 
6
6
  def _folder_path(
7
- projects: SerializedAsset, project: dict, root: Optional[str] = ""
7
+ projects: SerializedAsset,
8
+ project: dict,
9
+ root: Optional[str] = "",
8
10
  ) -> str:
9
11
  """Recursive function to compute folder path with list of projects"""
10
12
  path = "/" + str(project["name"]) + (root or "")
@@ -17,7 +17,8 @@ logger = logging.getLogger(__name__)
17
17
  def _paginated_option(page_number: int) -> TSC.RequestOptions:
18
18
  """Set up the Paginated option for TSC.RequestOptions"""
19
19
  return TSC.RequestOptions(
20
- pagesize=SAFE_MODE_PAGE_SIZE, pagenumber=page_number
20
+ pagesize=SAFE_MODE_PAGE_SIZE,
21
+ pagenumber=page_number,
21
22
  )
22
23
 
23
24