Skip to content

Commit 02b3e7d

Browse files
authored
Merge pull request #111 from jpoullet2000/table-qn-helpers
Implements the helper functions for table qualified names
2 parents 3bb6eec + 2bc5b4a commit 02b3e7d

File tree

7 files changed

+193
-4
lines changed

7 files changed

+193
-4
lines changed

Diff for: HISTORY.rst

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
History
33
=======
44

5+
1.0.0 (2019-08-10)
6+
------------------
7+
* Adds the helper functions to parse the qualified name
8+
* Updates the version to 1.x to get some confidence from community as the module is pretty stable now
9+
510
0.1.8 (2019-08-08)
611
------------------
712
* Add support for Atlas' Admin Metrics REST API

Diff for: atlasclient/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.8'
1+
__version__ = '1.0.0'

Diff for: atlasclient/utils.py

+71-1
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@
1818
except ImportError:
1919
from logging import Handler
2020

21+
2122
class NullHandler(Handler):
2223
def emit(self, record):
2324
pass
2425

25-
2626
DEFAULT_PORTS = {
2727
'http': 80,
2828
'https': 443,
2929
}
3030

31+
DEFAULT_TABLE_QN_REGEX = re.compile(r"""
32+
^(?P<db_name>.*?)\.(?P<table_name>.*)@(?P<cluster_name>.*?)$
33+
""", re.X)
34+
35+
DEFAULT_DB_CLUSTER = 'default'
36+
3137

3238
def normalize_underscore_case(name):
3339
"""Normalize an underscore-separated descriptor to something more readable.
@@ -78,6 +84,7 @@ def version_str(version):
7884
else:
7985
raise ValueError("Invalid version: %s" % version)
8086

87+
8188
def generate_http_basic_token(username, password):
8289
"""
8390
Generates a HTTP basic token from username and password
@@ -87,6 +94,7 @@ def generate_http_basic_token(username, password):
8794
token = base64.b64encode('{}:{}'.format(username, password).encode('utf-8')).decode('utf-8')
8895
return token
8996

97+
9098
def generate_base_url(host, protocol=None, port=None):
9199
matches = re.match(r'^(([^:]+)://)?([^/:]+)(:([^/]+))?', host)
92100
(_, derived_proto, derived_host, _, derived_port) = matches.groups()
@@ -106,3 +114,65 @@ def generate_base_url(host, protocol=None, port=None):
106114
'port': str(derived_port),
107115
}
108116
return "{protocol}://{host}:{port}".format(**url_params)
117+
118+
119+
def parse_table_qualified_name(qualified_name, qn_regex=DEFAULT_TABLE_QN_REGEX):
120+
"""
121+
Parses the Atlas' table qualified name
122+
:param qualified_name: Qualified Name of the table
123+
:return: A dictionary consisting of database name,
124+
table name and cluster name of the table.
125+
If database or cluster name not found,
126+
then uses the 'atlas_default' as both of them.
127+
"""
128+
def apply_qn_regex(name, table_qn_regex):
129+
return table_qn_regex.match(name)
130+
131+
_regex_result = apply_qn_regex(qualified_name, qn_regex)
132+
133+
if not _regex_result:
134+
qn_regex = re.compile(r"""
135+
^(?P<table_name>.*)@(?P<cluster_name>.*?)$
136+
""", re.X)
137+
_regex_result = apply_qn_regex(qualified_name, qn_regex)
138+
139+
if not _regex_result:
140+
qn_regex = re.compile(r"""
141+
^(?P<db_name>.*?)\.(?P<table_name>.*)$
142+
""", re.X)
143+
_regex_result = apply_qn_regex(qualified_name, qn_regex)
144+
145+
if not _regex_result:
146+
qn_regex = re.compile(r"""
147+
^(?P<table_name>.*)$
148+
""", re.X)
149+
_regex_result = apply_qn_regex(qualified_name, qn_regex)
150+
151+
_regex_result = _regex_result.groupdict()
152+
153+
qn_dict = {
154+
'table_name': _regex_result.get('table_name', qualified_name),
155+
'db_name': _regex_result.get('db_name', DEFAULT_DB_CLUSTER),
156+
'cluster_name': _regex_result.get('cluster_name', DEFAULT_DB_CLUSTER),
157+
}
158+
159+
return qn_dict
160+
161+
162+
def make_table_qualified_name(table_name, cluster=None, db=None):
163+
"""
164+
Based on the given parameters, generate the Atlas' table qualified Name
165+
:param db: Database Name of the table
166+
:param table_name: Table Name
167+
:param cluster: Cluster Name of the table
168+
:return: A string i.e., Qualified Name of the table
169+
If database or cluster name are 'atlas_default', then simply strips that part.
170+
"""
171+
qualified_name = table_name
172+
if db and db != DEFAULT_DB_CLUSTER:
173+
qualified_name = '{}.{}'.format(db, qualified_name)
174+
175+
if cluster and cluster != DEFAULT_DB_CLUSTER:
176+
qualified_name = '{}@{}'.format(qualified_name, cluster)
177+
178+
return qualified_name

Diff for: docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Contents:
1414
installation
1515
readme
1616
usage
17+
utils
1718
authors
1819
contributing
1920
history

Diff for: docs/utils.rst

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=================
2+
Utility / Helpers
3+
=================
4+
5+
6+
parse_table_qualified_name()
7+
----------------------------
8+
`atlasclient` provides helper function to parse the table qualified name and returns a dictionary
9+
containing `db_name`, `table_name` and `cluster_name` as keys::
10+
11+
from atlasclient.utils import parse_table_qualified_name
12+
13+
# Happy Scenario
14+
qualified_name = 'database.table@cluster'
15+
qn_dict = parse_table_qualified_name(qualified_name)
16+
print(qn_dict["db_name"])
17+
# Output: database
18+
19+
print(qn_dict["table_name"])
20+
# Output: table
21+
22+
print(qn_dict["cluster_name"])
23+
# Output: cluster
24+
25+
26+
In case if the entity is created manually and somehow does not fully satisfies the atlas qualified name
27+
pattern, this helper function handles the edge cases::
28+
29+
qualified_name = 'table@cluster'
30+
qn_dict = parse_table_qualified_name(qualified_name)
31+
print(qn_dict["db_name"])
32+
# Output: default
33+
34+
print(qn_dict["table_name"])
35+
# Output: table
36+
37+
print(qn_dict["cluster_name"])
38+
# Output: cluster
39+
40+
make_table_qualified_name()
41+
----------------------------
42+
There is also a function to make the table qualified name, back from the parsed result.
43+
It verifies if all three i.e., `table_name`, `cluster` and `db` parameters are there and not `default`.
44+
If the value is default or not available, then this helper handles the edge case accordingly::
45+
46+
from atlasclient.utils import make_table_qualified_name
47+
48+
# Happy Scenario
49+
qualified_name = make_table_qualified_name('table', 'cluster', 'database')
50+
print(qualified_name)
51+
# Output: 'database.table@cluster'

Diff for: setup.py

-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
with open(setup_requirements_path) as setup_requirements_file:
3232
setup_requirements = setup_requirements_file.readlines()
3333

34-
setup_args = {}
3534
setup_args = dict(
3635
name='atlasclient',
3736
version=version,
@@ -59,7 +58,6 @@
5958
],
6059
test_suite='tests',
6160
tests_require=test_requirements,
62-
#setup_requires=setup_requirements,
6361
)
6462

6563
setup(**setup_args)

Diff for: tests/test_utils.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from atlasclient.utils import (parse_table_qualified_name, make_table_qualified_name,
2+
DEFAULT_DB_CLUSTER)
3+
4+
DB = 'database_name'
5+
CL = 'cluster_name'
6+
TB = 'table_name'
7+
8+
9+
class TestUtils():
10+
def test_parse_table_qn(self):
11+
qualified_name = '{}.{}@{}'.format(DB, TB, CL)
12+
qn_dict = parse_table_qualified_name(qualified_name)
13+
assert qn_dict['db_name'] == DB
14+
assert qn_dict['cluster_name'] == CL
15+
assert qn_dict['table_name'] == TB
16+
17+
def test_parse_table_qn_without_db(self):
18+
qualified_name = '{}@{}'.format(TB, CL)
19+
qn_dict = parse_table_qualified_name(qualified_name)
20+
assert qn_dict['db_name'] == DEFAULT_DB_CLUSTER
21+
assert qn_dict['cluster_name'] == CL
22+
assert qn_dict['table_name'] == TB
23+
24+
def test_parse_table_qn_without_cluster(self):
25+
qualified_name = '{}.{}'.format(DB, TB)
26+
qn_dict = parse_table_qualified_name(qualified_name)
27+
assert qn_dict['db_name'] == DB
28+
assert qn_dict['cluster_name'] == DEFAULT_DB_CLUSTER
29+
assert qn_dict['table_name'] == TB
30+
31+
def test_parse_table_qn_only_table(self):
32+
qualified_name = '{}'.format(TB)
33+
qn_dict = parse_table_qualified_name(qualified_name)
34+
assert qn_dict['db_name'] == DEFAULT_DB_CLUSTER
35+
assert qn_dict['cluster_name'] == DEFAULT_DB_CLUSTER
36+
assert qn_dict['table_name'] == TB
37+
38+
def test_make_table_qn(self):
39+
qn = make_table_qualified_name(TB, CL, DB)
40+
assert qn == '{}.{}@{}'.format(DB, TB, CL)
41+
42+
def test_make_table_qn_with_default_database(self):
43+
qn = make_table_qualified_name(TB, CL, DEFAULT_DB_CLUSTER)
44+
assert qn == '{}@{}'.format(TB, CL)
45+
46+
def test_make_table_qn_with_default_cluster(self):
47+
qn = make_table_qualified_name(TB, DEFAULT_DB_CLUSTER, DB)
48+
assert qn == '{}.{}'.format(DB, TB)
49+
50+
def test_make_table_qn_only_table_with_default_db_cluster(self):
51+
qn = make_table_qualified_name(TB, DEFAULT_DB_CLUSTER, DEFAULT_DB_CLUSTER)
52+
assert qn == '{}'.format(TB)
53+
54+
def test_make_table_qn_without_database(self):
55+
qn = make_table_qualified_name(TB, CL)
56+
assert qn == '{}@{}'.format(TB, CL)
57+
58+
def test_make_table_qn_without_cluster(self):
59+
qn = make_table_qualified_name(TB, db=DB)
60+
assert qn == '{}.{}'.format(DB, TB)
61+
62+
def test_make_table_qn_only_table(self):
63+
qn = make_table_qualified_name(TB)
64+
assert qn == '{}'.format(TB)

0 commit comments

Comments
 (0)