castor-extractor 0.20.7__py3-none-any.whl → 0.21.3__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,26 @@
1
1
 
2
2
  # Changelog
3
3
 
4
+ ## 0.21.3 - 2024-11-07
5
+
6
+ * Tableau: Fix metrics definition url
7
+
8
+ ## 0.21.2 - 2024-11-06
9
+
10
+ * Adding fetch method for confluence client
11
+
12
+ ## 0.21.1 - 2024-10-23
13
+
14
+ * Warning message to deprecate python < 3.9
15
+
16
+ ## 0.21.0 - 2024-10-23
17
+
18
+ * Confluence: Added Confluence extractor
19
+
20
+ ## 0.20.8 - 2024-10-19
21
+
22
+ * bump dependencies (minor and patches)
23
+
4
24
  ## 0.20.7 - 2024-10-18
5
25
 
6
26
  * Metabase: fix `require_ssl` type in credentials
@@ -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,74 @@
1
+ from functools import partial
2
+ from http import HTTPStatus
3
+ from typing import Iterator, Optional
4
+
5
+ from ....utils import (
6
+ APIClient,
7
+ BasicAuth,
8
+ RequestSafeMode,
9
+ fetch_all_pages,
10
+ )
11
+ from ..assets import (
12
+ ConfluenceAsset,
13
+ )
14
+ from .credentials import ConfluenceCredentials
15
+ from .endpoints import ConfluenceEndpointFactory
16
+ from .pagination import ConfluencePagination
17
+
18
+ _HEADERS = {
19
+ "Accept": "application/json",
20
+ "Content-Type": "application/json",
21
+ }
22
+
23
+ _MAX_ERROR_IGNORED_COUNT = 10
24
+ _IGNORED_ERROR_CODES = (HTTPStatus.BAD_GATEWAY,)
25
+ _SAFE_MODE = RequestSafeMode(
26
+ max_errors=_MAX_ERROR_IGNORED_COUNT,
27
+ status_codes=_IGNORED_ERROR_CODES,
28
+ )
29
+
30
+
31
+ class ConfluenceClient(APIClient):
32
+ def __init__(
33
+ self,
34
+ credentials: ConfluenceCredentials,
35
+ safe_mode: Optional[RequestSafeMode] = None,
36
+ ):
37
+ self.account_id = credentials.account_id
38
+ auth = BasicAuth(
39
+ username=credentials.username, password=credentials.password
40
+ )
41
+ super().__init__(
42
+ auth=auth,
43
+ host=credentials.base_url,
44
+ headers=_HEADERS,
45
+ safe_mode=safe_mode or _SAFE_MODE,
46
+ )
47
+
48
+ def pages(self):
49
+ request = partial(
50
+ self._get,
51
+ endpoint=ConfluenceEndpointFactory.pages(),
52
+ params={"body-format": "atlas_doc_format"},
53
+ )
54
+ yield from fetch_all_pages(request, ConfluencePagination)
55
+
56
+ def users(self):
57
+ request_body = {"accountIds": [self.account_id]}
58
+ request = partial(
59
+ self._post,
60
+ endpoint=ConfluenceEndpointFactory.users(),
61
+ data=request_body,
62
+ )
63
+ yield from fetch_all_pages(request, ConfluencePagination)
64
+
65
+ def fetch(self, asset: ConfluenceAsset) -> Iterator[dict]:
66
+ """Returns the needed metadata for the queried asset"""
67
+ if asset == ConfluenceAsset.PAGES:
68
+ yield from self.pages()
69
+
70
+ elif asset == ConfluenceAsset.USERS:
71
+ yield from self.users()
72
+
73
+ else:
74
+ raise ValueError(f"This asset {asset} is unknown")
@@ -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)
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
12
12
 
13
13
  _PULSE_API = "api/-/pulse"
14
14
 
15
- _METRICS_URL = "{base}/pulse/site/{site}/metrics/{definition_id}"
15
+ _METRICS_DEFINITION_URL = "{base}/pulse/site/{site}/{definition_id}"
16
16
 
17
17
 
18
18
  def _pick(
@@ -89,7 +89,7 @@ class TableauClientRestApi:
89
89
  site = self._get_site_name()
90
90
  base_url = self._server.server_address.strip("/")
91
91
  for row in data:
92
- row["metadata"]["url"] = _METRICS_URL.format(
92
+ row["metadata"]["url"] = _METRICS_DEFINITION_URL.format(
93
93
  base=base_url,
94
94
  site=site,
95
95
  definition_id=row["metadata"]["id"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.20.7
3
+ Version: 0.21.3
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,26 @@ For any questions or bug report, contact us at [support@castordoc.com](mailto:su
208
208
 
209
209
  # Changelog
210
210
 
211
+ ## 0.21.3 - 2024-11-07
212
+
213
+ * Tableau: Fix metrics definition url
214
+
215
+ ## 0.21.2 - 2024-11-06
216
+
217
+ * Adding fetch method for confluence client
218
+
219
+ ## 0.21.1 - 2024-10-23
220
+
221
+ * Warning message to deprecate python < 3.9
222
+
223
+ ## 0.21.0 - 2024-10-23
224
+
225
+ * Confluence: Added Confluence extractor
226
+
227
+ ## 0.20.8 - 2024-10-19
228
+
229
+ * bump dependencies (minor and patches)
230
+
211
231
  ## 0.20.7 - 2024-10-18
212
232
 
213
233
  * Metabase: fix `require_ssl` type in credentials
@@ -1,11 +1,12 @@
1
- CHANGELOG.md,sha256=8s0QcdkmJwFZParMHTjOoICh4-G6RD_sQ2JVFwktPPA,14155
1
+ CHANGELOG.md,sha256=BtUYfthvsdEMiRDdId1T0Z0UYp_JshR9jaXq0OQ0SgE,14487
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=ZBabhxlaatbfT19ijGTmF0UJdWSANz7_q5IU7AmB4PM,2090
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
@@ -276,7 +285,7 @@ castor_extractor/visualization/tableau_revamp/assets.py,sha256=8sJsK6Qixao6xVmVa
276
285
  castor_extractor/visualization/tableau_revamp/client/__init__.py,sha256=wmS9uLtUiqNYVloi0-DgD8d2qzu3RVZEAtWiaDp6G_M,90
277
286
  castor_extractor/visualization/tableau_revamp/client/client.py,sha256=oaxvPsCccAcTWooXmDQNcJ6RFUVsCUzl6HxaHIwh5kU,7564
278
287
  castor_extractor/visualization/tableau_revamp/client/client_metadata_api.py,sha256=yNnGR3Tk32TUmaDejaz5fkw2p9DtmMeCv5rsZNOHUfY,3047
279
- castor_extractor/visualization/tableau_revamp/client/client_rest_api.py,sha256=0g8AddrhzirRCUWR2jrudPR02mk4Of5YilWth7zJO-g,4016
288
+ castor_extractor/visualization/tableau_revamp/client/client_rest_api.py,sha256=HZ6KdNJ6sqhWElfpYlUwbZDJXxoKkNc3p-YAKExzDxM,4030
280
289
  castor_extractor/visualization/tableau_revamp/client/client_tsc.py,sha256=BBwIOqK2zU66udFRmLGmB_3J1ILGhVOY5Hq4nmsonF0,1853
281
290
  castor_extractor/visualization/tableau_revamp/client/credentials.py,sha256=qA-EaX-4rbQRsn8v4zWh5Kh784ndHLjJaoZwnkQgCyo,1905
282
291
  castor_extractor/visualization/tableau_revamp/client/errors.py,sha256=dTe1shqmWmAXpDpCz-E24m8dGYjt6rvIGV9qQb4jnvI,150
@@ -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.7.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
419
- castor_extractor-0.20.7.dist-info/METADATA,sha256=XGGqjLLLfjnVXfoEfuRb3kao81OQ8c5VdwjdSockixk,21373
420
- castor_extractor-0.20.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
421
- castor_extractor-0.20.7.dist-info/entry_points.txt,sha256=IVGy_oM8VjzADMAxzmiNJTYYidTCsI98MpO_mkXjkqE,1573
422
- castor_extractor-0.20.7.dist-info/RECORD,,
427
+ castor_extractor-0.21.3.dist-info/LICENCE,sha256=sL-IGa4hweyya1HgzMskrRdybbIa2cktzxb5qmUgDg8,8254
428
+ castor_extractor-0.21.3.dist-info/METADATA,sha256=InLxkmUU0Esx_QcW1lM9_XmiwHjtfAnCiolwoTosseE,21707
429
+ castor_extractor-0.21.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
430
+ castor_extractor-0.21.3.dist-info/entry_points.txt,sha256=7aVSxc-_2dicp28Ow-S4y0p4wGoTm9zGmVptMvfLdw8,1649
431
+ castor_extractor-0.21.3.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