castor-extractor 0.20.6__py3-none-any.whl → 0.21.1__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.

CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
1
 
2
2
  # Changelog
3
3
 
4
+ ## 0.21.1 - 2024-10-23
5
+
6
+ * Warning message to deprecate python < 3.9
7
+
8
+ ## 0.21.0 - 2024-10-23
9
+
10
+ * Confluence: Added Confluence extractor
11
+
12
+ ## 0.20.8 - 2024-10-19
13
+
14
+ * bump dependencies (minor and patches)
15
+
16
+ ## 0.20.7 - 2024-10-18
17
+
18
+ * Metabase: fix `require_ssl` type in credentials
19
+
4
20
  ## 0.20.6 - 2024-10-15
5
21
 
6
22
  * Tableau: include `site_id` in **workbooks** to build url
@@ -0,0 +1,3 @@
1
+ from castor_extractor.utils import deprecate_python # type: ignore
2
+
3
+ deprecate_python(min_version_supported=(3, 9))
@@ -0,0 +1,19 @@
1
+ import logging
2
+ from argparse import ArgumentParser
3
+
4
+ from castor_extractor.knowledge import confluence # type: ignore
5
+ from castor_extractor.utils import parse_filled_arguments # type: ignore
6
+
7
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
8
+
9
+
10
+ def main():
11
+ parser = ArgumentParser()
12
+
13
+ parser.add_argument("-a", "--account_id", help="Confluence account id")
14
+ parser.add_argument("-b", "--base_url", help="Confluence account base url")
15
+ parser.add_argument("-o", "--output", help="Directory to write to")
16
+ parser.add_argument("-p", "--password", help="Confluence password")
17
+ parser.add_argument("-u", "--username", help="Confluence username")
18
+
19
+ confluence.extract_all(**parse_filled_arguments(parser))
@@ -0,0 +1,3 @@
1
+ from .assets import ConfluenceAsset
2
+ from .client import ConfluenceClient, ConfluenceCredentials
3
+ from .extract import extract_all
@@ -0,0 +1,8 @@
1
+ from ...types import ExternalAsset
2
+
3
+
4
+ class ConfluenceAsset(ExternalAsset):
5
+ """Confluence assets"""
6
+
7
+ PAGES = "pages"
8
+ USERS = "users"
@@ -0,0 +1,2 @@
1
+ from .client import ConfluenceClient
2
+ from .credentials import ConfluenceCredentials
@@ -0,0 +1,60 @@
1
+ from functools import partial
2
+ from http import HTTPStatus
3
+ from typing import Optional
4
+
5
+ from ....utils import (
6
+ APIClient,
7
+ BasicAuth,
8
+ RequestSafeMode,
9
+ fetch_all_pages,
10
+ )
11
+ from .credentials import ConfluenceCredentials
12
+ from .endpoints import ConfluenceEndpointFactory
13
+ from .pagination import ConfluencePagination
14
+
15
+ _HEADERS = {
16
+ "Accept": "application/json",
17
+ "Content-Type": "application/json",
18
+ }
19
+
20
+ _MAX_ERROR_IGNORED_COUNT = 10
21
+ _IGNORED_ERROR_CODES = (HTTPStatus.BAD_GATEWAY,)
22
+ _SAFE_MODE = RequestSafeMode(
23
+ max_errors=_MAX_ERROR_IGNORED_COUNT,
24
+ status_codes=_IGNORED_ERROR_CODES,
25
+ )
26
+
27
+
28
+ class ConfluenceClient(APIClient):
29
+ def __init__(
30
+ self,
31
+ credentials: ConfluenceCredentials,
32
+ safe_mode: Optional[RequestSafeMode] = None,
33
+ ):
34
+ self.account_id = credentials.account_id
35
+ auth = BasicAuth(
36
+ username=credentials.username, password=credentials.password
37
+ )
38
+ super().__init__(
39
+ auth=auth,
40
+ host=credentials.base_url,
41
+ headers=_HEADERS,
42
+ safe_mode=safe_mode or _SAFE_MODE,
43
+ )
44
+
45
+ def pages(self):
46
+ request = partial(
47
+ self._get,
48
+ endpoint=ConfluenceEndpointFactory.pages(),
49
+ params={"body-format": "atlas_doc_format"},
50
+ )
51
+ yield from fetch_all_pages(request, ConfluencePagination)
52
+
53
+ def users(self):
54
+ request_body = {"accountIds": [self.account_id]}
55
+ request = partial(
56
+ self._post,
57
+ endpoint=ConfluenceEndpointFactory.users(),
58
+ data=request_body,
59
+ )
60
+ yield from fetch_all_pages(request, ConfluencePagination)
@@ -0,0 +1,18 @@
1
+ from pydantic_settings import BaseSettings, SettingsConfigDict
2
+
3
+ CASTOR_ENV_PREFIX = "CASTOR_CONFLUENCE_"
4
+
5
+
6
+ class ConfluenceCredentials(BaseSettings):
7
+ """Class to handle Confluence rest API permissions"""
8
+
9
+ model_config = SettingsConfigDict(
10
+ env_prefix=CASTOR_ENV_PREFIX,
11
+ extra="ignore",
12
+ populate_by_name=True,
13
+ )
14
+
15
+ account_id: str
16
+ base_url: str
17
+ password: str
18
+ username: str
@@ -0,0 +1,25 @@
1
+ class ConfluenceEndpointFactory:
2
+ """
3
+ Confluence rest api v2 endpoint factory.
4
+ https://developer.atlassian.com/cloud/confluence/rest/v2/intro/#about
5
+ """
6
+
7
+ API = "wiki/api/v2/"
8
+ PAGES = "pages"
9
+ USERS = "users-bulk"
10
+
11
+ @classmethod
12
+ def pages(cls) -> str:
13
+ """
14
+ Endpoint to fetch all pages.
15
+ More: https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-get
16
+ """
17
+ return f"{cls.API}{cls.PAGES}"
18
+
19
+ @classmethod
20
+ def users(cls) -> str:
21
+ """
22
+ Endpoint to fetch all user.
23
+ More: https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-user/#api-users-bulk-post
24
+ """
25
+ return f"{cls.API}{cls.USERS}"
@@ -0,0 +1,32 @@
1
+ from typing import Optional, Union
2
+
3
+ from pydantic import AliasPath, Field
4
+
5
+ from ....utils import (
6
+ FetchNextPageBy,
7
+ PaginationModel,
8
+ )
9
+
10
+
11
+ class ConfluencePagination(PaginationModel):
12
+ """Class to handle paginated results for confluence"""
13
+
14
+ fetch_by: FetchNextPageBy = FetchNextPageBy.URL
15
+
16
+ results: list = Field(default_factory=list)
17
+ next_url: Optional[str] = Field(
18
+ validation_alias=AliasPath("_links", "next"),
19
+ default=None,
20
+ )
21
+
22
+ def is_last(self) -> bool:
23
+ """Stopping condition for the pagination"""
24
+ return self.next_url is None
25
+
26
+ def next_page_payload(self) -> Union[str, None]:
27
+ """Payload enabling to generate the request for the next page"""
28
+ return self.next_url
29
+
30
+ def page_results(self) -> list:
31
+ """List of results of the current page"""
32
+ return self.results
@@ -0,0 +1,54 @@
1
+ import logging
2
+ from typing import Iterable, Iterator, Tuple, Union
3
+
4
+ from ...utils import (
5
+ OUTPUT_DIR,
6
+ current_timestamp,
7
+ deep_serialize,
8
+ from_env,
9
+ get_output_filename,
10
+ write_json,
11
+ write_summary,
12
+ )
13
+ from .assets import ConfluenceAsset
14
+ from .client import ConfluenceClient, ConfluenceCredentials
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def iterate_all_data(
20
+ client: ConfluenceClient,
21
+ ) -> Iterable[Tuple[ConfluenceAsset, Union[list, Iterator, dict]]]:
22
+ """Iterate over the extracted data from Confluence"""
23
+
24
+ logger.info("Extracting USERS from API")
25
+ users = list(deep_serialize(client.users()))
26
+ yield ConfluenceAsset.USERS, users
27
+ logger.info(f"Extracted {len(users)} users from API")
28
+
29
+ logger.info("Extracting PAGES from API")
30
+ pages = list(deep_serialize(client.pages()))
31
+ yield ConfluenceAsset.PAGES, pages
32
+ logger.info(f"Extracted {len(pages)} pages from API")
33
+
34
+
35
+ def extract_all(**kwargs) -> None:
36
+ """
37
+ Extract data from Confluence API
38
+ Store the output files locally under the given output_directory
39
+ """
40
+
41
+ output_directory = kwargs.get("output") or from_env(OUTPUT_DIR)
42
+
43
+ credentials = ConfluenceCredentials(**kwargs)
44
+ client = ConfluenceClient(credentials=credentials)
45
+
46
+ ts = current_timestamp()
47
+
48
+ for key, data in iterate_all_data(client):
49
+ filename = get_output_filename(key.name.lower(), output_directory, ts)
50
+ write_json(filename, data)
51
+
52
+ es = current_timestamp()
53
+
54
+ write_summary(output_directory, ts, duration_second=es - ts)
@@ -4,7 +4,7 @@ import warnings
4
4
  from typing import Tuple
5
5
 
6
6
 
7
- def deprecate_python(min_version_supported: Tuple[int]):
7
+ def deprecate_python(min_version_supported: Tuple[int, ...]):
8
8
  """raises a warning if python version < min_version_supported"""
9
9
 
10
10
  python_version = (
@@ -22,5 +22,5 @@ def deprecate_python(min_version_supported: Tuple[int]):
22
22
  if python_version < min_version_supported:
23
23
  warnings.warn(message, DeprecationWarning)
24
24
 
25
- # Since warnings are disabled by default, let's add a log as well
26
- logging.warning(message)
25
+ # Since warnings are disabled by default, let's add a log as well
26
+ logging.warning(message)
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from pydantic import Field
3
+ from pydantic import AliasChoices, Field
4
4
  from pydantic_settings import BaseSettings, SettingsConfigDict
5
5
 
6
6
  METABASE_DB_ENV_PREFIX = "CASTOR_METABASE_DB_"
@@ -18,9 +18,15 @@ class MetabaseDbCredentials(BaseSettings):
18
18
  host: str
19
19
  port: str
20
20
  database: str
21
- schema_: str = Field(validation_alias=f"{METABASE_DB_ENV_PREFIX}SCHEMA")
21
+ schema_: str = Field(
22
+ validation_alias=AliasChoices(
23
+ f"{METABASE_DB_ENV_PREFIX}SCHEMA", "schema"
24
+ )
25
+ )
22
26
  user: str = Field(validation_alias=f"{METABASE_DB_ENV_PREFIX}USERNAME")
23
27
  password: str = Field(repr=False)
24
28
 
25
- encryption_secret_key: Optional[str] = Field(repr=False)
26
- require_ssl: Optional[str]
29
+ encryption_secret_key: Optional[str] = Field(repr=False, default=None)
30
+ require_ssl: Optional[bool] = Field(
31
+ validation_alias=f"{METABASE_DB_ENV_PREFIX}REQUIRE_SSL_KEY"
32
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.20.6
3
+ Version: 0.21.1
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -37,7 +37,7 @@ Requires-Dist: google-cloud-core (>=2.1.0,<3.0.0)
37
37
  Requires-Dist: google-cloud-storage (>=2,<3)
38
38
  Requires-Dist: google-resumable-media (>=2.0.3,<3.0.0)
39
39
  Requires-Dist: googleapis-common-protos (>=1.53.0,<2.0.0)
40
- Requires-Dist: looker-sdk (>=24.0.0,<25.0.0) ; extra == "looker" or extra == "all"
40
+ Requires-Dist: looker-sdk (>=24.16.0,<24.17.0) ; extra == "looker" or extra == "all"
41
41
  Requires-Dist: msal (>=1.20.0,<2.0.0) ; extra == "powerbi" or extra == "all"
42
42
  Requires-Dist: numpy (<1.25) ; (python_version >= "3.8" and python_version < "3.9") and (extra == "bigquery" or extra == "databricks" or extra == "all")
43
43
  Requires-Dist: numpy (<2) ; extra == "bigquery" or extra == "databricks" or extra == "all"
@@ -208,6 +208,22 @@ For any questions or bug report, contact us at [support@castordoc.com](mailto:su
208
208
 
209
209
  # Changelog
210
210
 
211
+ ## 0.21.1 - 2024-10-23
212
+
213
+ * Warning message to deprecate python < 3.9
214
+
215
+ ## 0.21.0 - 2024-10-23
216
+
217
+ * Confluence: Added Confluence extractor
218
+
219
+ ## 0.20.8 - 2024-10-19
220
+
221
+ * bump dependencies (minor and patches)
222
+
223
+ ## 0.20.7 - 2024-10-18
224
+
225
+ * Metabase: fix `require_ssl` type in credentials
226
+
211
227
  ## 0.20.6 - 2024-10-15
212
228
 
213
229
  * Tableau: include `site_id` in **workbooks** to build url
@@ -1,11 +1,12 @@
1
- CHANGELOG.md,sha256=Q6RN08q5JpWqwJjuR6cSm2k9vyeHbaNuufiVlS07vDo,14080
1
+ CHANGELOG.md,sha256=U0folo6uva597R9IArkQzRau3UldJIdvq99aWOEWKkA,14355
2
2
  Dockerfile,sha256=xQ05-CFfGShT3oUqaiumaldwA288dj9Yb_pxofQpufg,301
3
3
  DockerfileUsage.md,sha256=2hkJQF-5JuuzfPZ7IOxgM6QgIQW7l-9oRMFVwyXC4gE,998
4
4
  LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
5
5
  README.md,sha256=uF6PXm9ocPITlKVSh9afTakHmpLx3TvawLf-CbMP3wM,3578
6
6
  castor_extractor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- castor_extractor/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ castor_extractor/commands/__init__.py,sha256=qnCeLzbb8dlNJYr-P1ZMqHmNoXyaoojC8MkDUEZkfSk,116
8
8
  castor_extractor/commands/extract_bigquery.py,sha256=dU4OiYO1V0n32orvZnMh1_xtFKF_VxHNXcVsH3otY-g,1269
9
+ castor_extractor/commands/extract_confluence.py,sha256=o4vjN7nxN2MQWyLgdgjbaO9Rd53f2icYst5tv_evwT4,750
9
10
  castor_extractor/commands/extract_databricks.py,sha256=SVKyoa-BBUQAM6HRHf1Wdg9-tpICic2yyvXQwHcNBhA,1264
10
11
  castor_extractor/commands/extract_domo.py,sha256=jvAawUsUTHrwCn_koK6StmQr4n_b5GyvJi6uu6WS0SM,1061
11
12
  castor_extractor/commands/extract_looker.py,sha256=cySLiolLCgrREJ9d0kMrJ7P8K3efHTBTzShalWVfI3A,1214
@@ -39,6 +40,14 @@ castor_extractor/file_checker/file_test_users_valid.csv,sha256=Ek3q7DjUS0neOu1LQ
39
40
  castor_extractor/file_checker/templates/__init__.py,sha256=StVLm4ZGyGVmPzarxEaDR_k08T3nUnyiv8N99sAz6AQ,60
40
41
  castor_extractor/file_checker/templates/generic_warehouse.py,sha256=3LREeiTsYsh5_wkuZost_frmMDp_K-jfiuod02vFMe8,2963
41
42
  castor_extractor/knowledge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ castor_extractor/knowledge/confluence/__init__.py,sha256=pRT615pMDlB7Ifs09erVn2EdpZHgkvX5selemWU3VPE,129
44
+ castor_extractor/knowledge/confluence/assets.py,sha256=zv2G2LB8H0fKDbVJ4kHrAjbqehXI_K-wgd_ghSXGFvs,144
45
+ castor_extractor/knowledge/confluence/client/__init__.py,sha256=ALAzo0JEhxFzH2FnIO6HmtkAGS2_bGY8KXXMcTGV3aE,84
46
+ castor_extractor/knowledge/confluence/client/client.py,sha256=ZgAUlEGwpSN1QZAFGpU-7gob_wWI3wpBh1iTycxUZc4,1667
47
+ castor_extractor/knowledge/confluence/client/credentials.py,sha256=2LUjnCOMwq2NBrkHty99TpWlgyOOsYjwC9NeekZMH84,422
48
+ castor_extractor/knowledge/confluence/client/endpoints.py,sha256=b7PIvw9_W942L4zsEZa__KhZDTo4yt-CdIO0eas69TE,736
49
+ castor_extractor/knowledge/confluence/client/pagination.py,sha256=ty4meiMEujDVSiQyOJTibd-ReYyDyGezdFuk7EAGtMA,862
50
+ castor_extractor/knowledge/confluence/extract.py,sha256=0fnxqEQ1gLtNz0LANCvBHPCBN9hJSRMDWBgr0zNW45I,1545
42
51
  castor_extractor/knowledge/notion/__init__.py,sha256=ZDmh0eNSxHf1zVPm0aYlKPci-vzOXhAgdsWjS2hdjh4,117
43
52
  castor_extractor/knowledge/notion/assets.py,sha256=QHv1-pomt5UeN_prP2L6t_zJ-tDSqB8LgopkGAODYPQ,164
44
53
  castor_extractor/knowledge/notion/client/__init__.py,sha256=CDPorBCethuNTEtpjvHGcWnWeVfqkEq-IbakWjDKATw,76
@@ -94,7 +103,7 @@ castor_extractor/utils/dbt/assets.py,sha256=JY1nKEGySZ84wNoe7dnizwAYw2q0t8NVaIfq
94
103
  castor_extractor/utils/dbt/client.py,sha256=xBjbT-p99TXY850ooEAgjNp33yfGDwjWJRbXzeJoVaI,5538
95
104
  castor_extractor/utils/dbt/client_test.py,sha256=FO_vpnECE-hoK0rZHbqDv17oaJj3-uPhFEqTrMPzUf4,4533
96
105
  castor_extractor/utils/dbt/credentials.py,sha256=pGq7GqFQTw9TwN1DXSHC-0yJ2H6B_wMAbHyQTLqJVh0,543
97
- castor_extractor/utils/deprecate.py,sha256=_uzQiwHiz2yEqQeNMmzvVmBY46IgBhhEbGPhTrVjZU4,817
106
+ castor_extractor/utils/deprecate.py,sha256=caXcCPStw9y97Bk_tu3pYVksfSj1x9ZyxnprNHJqHtA,830
98
107
  castor_extractor/utils/env.py,sha256=TqdtB50U8LE0993WhhEhpy89TJrHbjtIKjvg6KQ-5q0,596
99
108
  castor_extractor/utils/files.py,sha256=3C_u7P-kSZoOABVaKsuaf8lEhldRRxyxD27-K18_dEU,1545
100
109
  castor_extractor/utils/files_test.py,sha256=omRT3XSjaSAywYUoLh1SGWqYzl4UwBYKSYA9_7mXd_E,1542
@@ -167,7 +176,7 @@ castor_extractor/visualization/metabase/client/api/client_test.py,sha256=7Lb5yvr
167
176
  castor_extractor/visualization/metabase/client/api/credentials.py,sha256=SiHWyWWEDy_Ak4ne0hgXPZCSP6mEuHH26wDhsed_9v8,519
168
177
  castor_extractor/visualization/metabase/client/db/__init__.py,sha256=nawDhJ-JGlpM6VMzZZRjf066QXk9kWzZr6l9n6OHTZ0,76
169
178
  castor_extractor/visualization/metabase/client/db/client.py,sha256=s38mubBaun4LfYWXqk_fyObWTk9f69AorDrXyW4HNiw,3369
170
- castor_extractor/visualization/metabase/client/db/credentials.py,sha256=LrtfUd2c1jAAFzxu64suIwvZe2dJnZ8suv9vGTnXtrI,746
179
+ castor_extractor/visualization/metabase/client/db/credentials.py,sha256=rhA6rM7QMTBSpF4BknYXz2VqujFcWAdx5TPR4kmdPy4,918
171
180
  castor_extractor/visualization/metabase/client/db/queries/.sqlfluff,sha256=sOQQOpAa9QMj9cBlulfmt-DZ_kQzMpzSAEnh10QGSB0,76
172
181
  castor_extractor/visualization/metabase/client/db/queries/base_url.sql,sha256=p2EL9kdt-hw_yh3aeCE91AXEB4RrYAbG2QrBBNqQjDE,79
173
182
  castor_extractor/visualization/metabase/client/db/queries/card.sql,sha256=U4-ZrZP5XS7Kssd3CFlpGrABbHAMK09Bh_yeT3z7Yk8,419
@@ -415,8 +424,8 @@ castor_extractor/warehouse/sqlserver/queries/table.sql,sha256=kbBQP-TdG5px1IVgyx
415
424
  castor_extractor/warehouse/sqlserver/queries/user.sql,sha256=gOrZsMVypusR2dc4vwVs4E1a-CliRsr_UjnD2EbXs-A,94
416
425
  castor_extractor/warehouse/sqlserver/query.py,sha256=j_d5-HMnzBouwGfywVZMRSSwbXzPvzDWlFCZmvxcoGQ,539
417
426
  castor_extractor/warehouse/synapse/queries/column.sql,sha256=lNcFoIW3Y0PFOqoOzJEXmPvZvfAsY0AP63Mu2LuPzPo,1351
418
- castor_extractor-0.20.6.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
419
- castor_extractor-0.20.6.dist-info/METADATA,sha256=Vgkknt_--nGtd3FcSAsG5JxA6zvBVlc-lwSOqlXoeTE,21298
420
- castor_extractor-0.20.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
421
- castor_extractor-0.20.6.dist-info/entry_points.txt,sha256=IVGy_oM8VjzADMAxzmiNJTYYidTCsI98MpO_mkXjkqE,1573
422
- castor_extractor-0.20.6.dist-info/RECORD,,
427
+ castor_extractor-0.21.1.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
428
+ castor_extractor-0.21.1.dist-info/METADATA,sha256=28gV2BAN9hz_TdzFHMxezHikWiOLwTczn0BwVKGrCy0,21575
429
+ castor_extractor-0.21.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
430
+ castor_extractor-0.21.1.dist-info/entry_points.txt,sha256=7aVSxc-_2dicp28Ow-S4y0p4wGoTm9zGmVptMvfLdw8,1649
431
+ castor_extractor-0.21.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,5 +1,6 @@
1
1
  [console_scripts]
2
2
  castor-extract-bigquery=castor_extractor.commands.extract_bigquery:main
3
+ castor-extract-confluence=castor_extractor.commands.extract_confluence:main
3
4
  castor-extract-databricks=castor_extractor.commands.extract_databricks:main
4
5
  castor-extract-domo=castor_extractor.commands.extract_domo:main
5
6
  castor-extract-looker=castor_extractor.commands.extract_looker:main