forked from tableau/document-api-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconnection.py
More file actions
311 lines (241 loc) · 8.22 KB
/
connection.py
File metadata and controls
311 lines (241 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
import xml.etree.ElementTree as ET
from tableaudocumentapi.dbclass import is_valid_dbclass
from functools import wraps
def property_is_boolean(func):
@wraps(func)
def wrapper(self, value):
if not isinstance(value, bool):
error = "Boolean expected for {0} flag.".format(func.__name__)
raise ValueError(error)
return func(self, value)
return wrapper
class Connection(object):
"""A class representing connections inside Data Sources."""
def __init__(self, connxml):
"""Connection is usually instantiated by passing in connection elements
in a Data Source. If creating a connection from scratch you can call
`from_attributes` passing in the connection attributes.
"""
self._connectionXML = connxml
self._dbname = connxml.get('dbname')
self._server = connxml.get('server')
self._username = connxml.get('username')
self._password = connxml.get('password')
self._embed_password = connxml.get('embed_password')
self._authentication = connxml.get('authentication')
self._warehouse = connxml.get('warehouse')
self._service = connxml.get('service')
self._class = connxml.get('class')
self._port = connxml.get('port', None)
self._query_band = connxml.get('query-band-spec', None)
self._initial_sql = connxml.get('one-time-sql', None)
def __repr__(self):
return "'<Connection server='{}' dbname='{}' @ {}>'".format(self._server, self._dbname, hex(id(self)))
@classmethod
def from_attributes(cls, server, dbname, username, dbclass, password=None, embed_password=True, port=None, query_band=None,
initial_sql=None, authentication='', warehouse=None, service=None):
"""Creates a new connection that can be added into a Data Source.
defaults to `''` which will be treated as 'prompt' by Tableau."""
root = ET.Element('connection', authentication=authentication)
xml = cls(root)
xml.server = server
xml.dbname = dbname
xml.username = username
xml.password = password
xml.embed_password = embed_password
xml.dbclass = dbclass
xml.port = port
xml.query_band = query_band
xml.initial_sql = initial_sql
xml.warehouse = warehouse
xml.service = service
return xml
@property
def dbname(self):
"""Database name for the connection. Not the table name."""
return self._dbname
@dbname.setter
def dbname(self, value):
"""
Set the connection's database name property.
Args:
value: New name of the database. String.
Returns:
Nothing.
"""
self._dbname = value
self._connectionXML.set('dbname', value)
@property
def warehouse(self):
"""Database name for the connection. Not the table name."""
return self._warehouse
@warehouse.setter
def warehouse(self, value):
"""
Set the connection's warehouse name property.
Args:
value: New name of the warehouse. String.
Returns:
Nothing.
"""
self._warehouse = value
self._connectionXML.set('warehouse', value)
@property
def service(self):
"""Database role service for the connection. Not the table name."""
return self._service
@service.setter
def service(self, value):
"""
Set the connection's sercie name property.
Args:
value: New name of the service. String.
Returns:
Nothing.
"""
self._service = value
self._connectionXML.set('service', value)
@property
def server(self):
"""Hostname or IP address of the database server. May also be a URL in some connection types."""
return self._server
@server.setter
def server(self, value):
"""
Set the connection's server property.
Args:
value: New server. String.
Returns:
Nothing.
"""
self._server = value
self._connectionXML.set('server', value)
@property
def username(self):
"""Username used to authenticate to the database."""
return self._username
@username.setter
def username(self, value):
"""
Set the connection's username property.
Args:
value: New username value. String.
Returns:
Nothing.
"""
self._username = value
self._connectionXML.set('username', value)
@property
def password(self):
"""Password used to authenticate to the database."""
return self._password
@password.setter
def password(self, value):
"""
Set the connection's password property.
Args:
value: New password value. String.
Returns:
Nothing.
"""
self._password = value
self._connectionXML.set('password', value)
@property
def embed_password(self):
"""Password used to authenticate is embedded."""
return self._embed_password
@embed_password.setter
@property_is_boolean
def embed_password(self, value):
"""
Set the connection's embed_password property.
Args:
value: New embed_password value. Bool.
Returns:
Nothing.
"""
self._embed_password = value
self._connectionXML.set('embed_password', value)
@property
def authentication(self):
return self._authentication
@property
def dbclass(self):
"""The type of connection (e.g. 'MySQL', 'Postgresql'). A complete list
can be found in dbclass.py"""
return self._class
@dbclass.setter
def dbclass(self, value):
"""Set the connection's dbclass property.
Args:
value: New dbclass value. String.
Returns:
Nothing.
"""
if not is_valid_dbclass(value):
raise AttributeError("'{}' is not a valid database type".format(value))
self._class = value
self._connectionXML.set('class', value)
@property
def port(self):
"""Port used to connect to the database."""
return self._port
@port.setter
def port(self, value):
"""Set the connection's port property.
Args:
value: New port value. String.
Returns:
Nothing.
"""
self._port = value
# If port is None we remove the element and don't write it to XML
if value is None:
try:
del self._connectionXML.attrib['port']
except KeyError:
pass
else:
self._connectionXML.set('port', value)
@property
def query_band(self):
"""Query band passed on connection to database."""
return self._query_band
@query_band.setter
def query_band(self, value):
"""Set the connection's query_band property.
Args:
value: New query_band value. String.
Returns:
Nothing.
"""
self._query_band = value
# If query band is None we remove the element and don't write it to XML
if value is None:
try:
del self._connectionXML.attrib['query-band-spec']
except KeyError:
pass
else:
self._connectionXML.set('query-band-spec', value)
@property
def initial_sql(self):
"""Initial SQL to be run."""
return self._initial_sql
@initial_sql.setter
def initial_sql(self, value):
"""Set the connection's initial_sql property.
Args:
value: New initial_sql value. String.
Returns:
Nothing.
"""
self._initial_sql = value
# If initial_sql is None we remove the element and don't write it to XML
if value is None:
try:
del self._connectionXML.attrib['one-time-sql']
except KeyError:
pass
else:
self._connectionXML.set('one-time-sql', value)