Skip to content

Commit b259a21

Browse files
author
Jeroen van der Heijden
committed
Added access keywords, comments
1 parent 00ac577 commit b259a21

File tree

3 files changed

+222
-29
lines changed

3 files changed

+222
-29
lines changed

thingsdb/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .version import __version__
2+
from .client.protocol import Access

thingsdb/client/buildin.py

Lines changed: 212 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,68 +10,203 @@ class Buildin:
1010
#
1111

1212
async def collection_info(self, collection: U[int, str]) -> dict:
13+
"""Returns information about a specific collection.
14+
15+
This function requires QUERY privileges on the requested collection,
16+
or CHANGE privileges on the @thingsdb scope.
17+
18+
This function does not generate a change.
19+
"""
1320
return await self.query(
14-
f'collection_info(collection)',
21+
'collection_info(collection)',
1522
collection=collection,
1623
scope='@t')
1724

18-
async def collections_info(self):
25+
async def collections_info(self) -> list:
26+
"""Returns collection information about all collections in ThingsDB.
27+
"""
1928
return await self.query('collections_info()', scope='@t')
2029

2130
async def del_collection(self, collection: U[int, str]):
31+
"""Delete a collection.
32+
33+
This function generates a change.
34+
"""
2235
return await self.query(
23-
f'del_collection(collection)',
36+
'del_collection(collection)',
2437
collection=collection,
2538
scope='@t')
2639

2740
async def del_expired(self):
41+
"""Delete all expired tokens.
42+
43+
Requires GRANT privileges on the @thingsdb scope.
44+
45+
This function generates a change.
46+
"""
2847
return await self.query('del_expired()', scope='@t')
2948

3049
async def del_module(self, name: str):
31-
return await self.query(f'del_module(key)', name=name, scope='@t')
50+
"""Delete a module. A SIGTERM signal will be send to the process for
51+
the module which might cancel running futures.
52+
53+
This function generates a change.
54+
"""
55+
return await self.query('del_module(key)', name=name, scope='@t')
3256

3357
async def del_node(self, node_id: int):
34-
return await self.query(f'del_node(id)', id=node_id, scope='@t')
58+
"""Delete a node from ThingsDB.
59+
60+
Before deleting a node, the node must be offline. As long is the node
61+
is active, you are not allowed to delete the node. See shutdown for
62+
shutting down a node by using a query.
63+
64+
This function generates a change.
65+
"""
66+
return await self.query('del_node(id)', id=node_id, scope='@t')
3567

3668
async def del_token(self, key: str):
37-
return await self.query(f'del_token(key)', key=key, scope='@t')
69+
"""Delete a token.
70+
71+
This function requires GRANT privileges on the @thingsdb scope unless
72+
the given token belongs to the logged on user. In the latter case,
73+
only CHANGE privileges are required.
74+
75+
This function generates a change.
76+
"""
77+
return await self.query('del_token(key)', key=key, scope='@t')
3878

3979
async def del_user(self, name: str):
40-
return await self.query(f'del_user(name)', name=name, scope='@t')
80+
"""Delete a user.
4181
42-
# TODO: deploy module
82+
It is not possible to delete your own user account and a bad_data_err()
83+
will be raised in case you try to. Any tokens associated with the user
84+
will also be deleted.
85+
86+
This function requires GRANT privileges on the @thingsdb scope.
87+
88+
This function generates a change.
89+
"""
90+
return await self.query('del_user(name)', name=name, scope='@t')
91+
92+
async def deploy_module(
93+
self,
94+
name: str,
95+
data: Optional[U[bytes, str]] = None):
96+
"""Deploy a module on all nodes.
97+
98+
The module must be configured first, using the new_module() function.
99+
This function is used to write the module data (or plain python code)
100+
to the module. After deploying the code, the module will be restarted
101+
on every node.
102+
103+
Before deploying a module, it is strongly recommended to use a
104+
development environment before deploying the module into production.
105+
106+
When the `data` argument is None, no data will be overwritten but the
107+
module will be restarted on all nodes. This might be useful if you want
108+
to force a module restart on all nodes.
109+
110+
This function generates a change.
111+
"""
112+
return await self.query(
113+
'deploy_module(name, data)',
114+
name=name,
115+
data=data,
116+
scope='@t')
43117

44118
async def grant(self, target: U[int, str], user: str, mask: int):
119+
"""Grant, collection or general, privileges to a user.
120+
121+
Access to a user is provided by setting a bit mask to either the @node,
122+
@thingsdb or a @collection scope.
123+
124+
To use this function, at least CHANGE privileges on the @thingsdb scope
125+
and GRANT privileges on the target scope are required.
126+
127+
It is not possible to set privileges on a specific node scope.
128+
Therefore scope @node will apply to all nodes in ThingsDB.
129+
130+
The following pre-defined masks are available:
131+
(from thingsdb import Access)
132+
133+
Mask | Description
134+
----------------- | ------------
135+
Access.QUERY (1) | Gives read access.
136+
Access.CHANGE (2) | Gives modify access.
137+
Access.GRANT (4) | Gives modify and grant (and revoke) privileges.
138+
Access.JOIN (8) | Gives join (and leave) privileges.
139+
Access.RUN (16) | Gives run procedures access.
140+
Access.FULL (31) | A mask for full privileges.
141+
142+
It is not possible to have GRANT privileges without also having CHANGE
143+
privileges. However, ThingsDB automatically applies the required
144+
privileges so when setting for example GRANT privileges, ThingsDB makes
145+
sure that the user also gets CHANGE privileges.
146+
147+
This function generates a change.
148+
"""
45149
return await self.query(
46-
f'grant(target, user, mask)',
150+
'grant(target, user, mask)',
47151
target=target,
48152
user=user,
49153
mask=mask,
50154
scope='@t')
51155

52156
async def has_collection(self, name: str):
53-
return await self.query(f'has_collection(name)', name=name, scope='@t')
157+
"""Determines if a collection exists in ThingsDB.
158+
159+
This function does not generate a change.
160+
"""
161+
return await self.query('has_collection(name)', name=name, scope='@t')
54162

55163
async def has_module(self, name: str):
56-
return await self.query(f'has_module(name)', name=name, scope='@t')
164+
"""Determines if a module exists in ThingsDB.
165+
166+
The scope restriction of the module has no impact on the result of this
167+
function.
168+
169+
This function does not generate a change.
170+
"""
171+
return await self.query('has_module(name)', name=name, scope='@t')
57172

58173
async def has_node(self, node_id: int):
59-
return await self.query(f'has_node(id)', id=node_id, scope='@t')
174+
"""Determines if a node exists in ThingsDB.
175+
176+
This function does not generate a change.
177+
"""
178+
return await self.query('has_node(id)', id=node_id, scope='@t')
60179

61180
async def has_token(self, token: str):
62-
return await self.query(f'has_token(token)', token=token, scope='@t')
181+
"""Determines if a token exists in ThingsDB.
182+
183+
This function requires GRANT privileges on the @thingsdb scope.
184+
185+
This function does not generate a change.
186+
"""
187+
return await self.query('has_token(token)', token=token, scope='@t')
63188

64189
async def has_user(self, name: str):
65-
return await self.query(f'has_user(name)', name=name, scope='@t')
190+
"""Determines if a user exists in ThingsDB.
191+
192+
This function requires GRANT privileges on the @thingsdb scope.
193+
194+
This function does not generate a change.
195+
"""
196+
return await self.query('has_user(name)', name=name, scope='@t')
66197

67198
async def module_info(self, name: str) -> dict:
68-
return await self.query(f'module_info(name)', name=name, scope='@t')
199+
return await self.query('module_info(name)', name=name, scope='@t')
69200

70201
async def modules_info(self) -> list:
71202
return await self.query('modules_info()', scope='@t')
72203

73204
async def new_collection(self, name: str):
74-
return await self.query(f'new_collection(name)', name=name, scope='@t')
205+
"""Create a new collection.
206+
207+
This function generates a change.
208+
"""
209+
return await self.query('new_collection(name)', name=name, scope='@t')
75210

76211
# TODO: new module
77212
# TODO: new node
@@ -86,35 +221,43 @@ async def new_token(
86221
expiration_time = int(datetime.datetime.timestamp(expiration_time))
87222

88223
return await self.query(
89-
f'new_token(user, expiration_time, description)',
224+
'new_token(user, expiration_time, description)',
90225
user=user,
91226
expiration_time=expiration_time,
92227
description=description,
93228
scope='@t')
94229

95230
async def new_user(self, name: str):
96-
return await self.query(f'new_user(name)', name=name, scope='@t')
231+
"""Creates a new user to ThingsDB. The new user is created without a
232+
password, token and access privileges. You probably want to set a
233+
password or add a new token, and assign some privileges using grant(…).
234+
235+
This function requires GRANT privileges on the @thingsdb scope.
236+
237+
This function generates a change.
238+
"""
239+
return await self.query('new_user(name)', name=name, scope='@t')
97240

98241
async def rename_collection(
99242
self,
100243
collection: U[int, str],
101244
new_name: str) -> None:
102245
return await self.query(
103-
f'rename_collection(collection, new_name)',
246+
'rename_collection(collection, new_name)',
104247
collection=collection,
105248
new_name=new_name,
106249
scope='@t')
107250

108251
async def rename_module(self, name: str, new_name: str) -> None:
109252
return await self.query(
110-
f'rename_module(name, new_name)',
253+
'rename_module(name, new_name)',
111254
name=name,
112255
new_name=new_name,
113256
scope='@t')
114257

115258
async def rename_user(self, name: str, new_name: str) -> None:
116259
return await self.query(
117-
f'rename_user(name, new_name)',
260+
'rename_user(name, new_name)',
118261
name=name,
119262
new_name=new_name,
120263
scope='@t')
@@ -123,7 +266,7 @@ async def rename_user(self, name: str, new_name: str) -> None:
123266

124267
async def revoke(self, target: U[int, str], user: str, mask: int):
125268
return await self.query(
126-
f'revoke(target, user, mask)',
269+
'revoke(target, user, mask)',
127270
target=target,
128271
user=user,
129272
mask=mask,
@@ -134,25 +277,40 @@ async def revoke(self, target: U[int, str], user: str, mask: int):
134277

135278
async def set_password(self, user: str, new_password: str = None) -> None:
136279
return await self.query(
137-
f'set_password(user, new_password)',
280+
'set_password(user, new_password)',
138281
user=user,
139282
new_password=new_password,
140283
scope='@t')
141284

142285
async def set_time_zone(self, collection: U[int, str], zone: str):
286+
"""By default each collection will be created with time zone UTC.
287+
288+
This function can be used to change the time zone for a collection. If
289+
changed, the functions datetime(..) and timeval(..) will use the
290+
collections time zone unless specified otherwise. See time_zones_info()
291+
for a list of all available timezones.
292+
293+
Use collection_info(..) to view the current time zone for a collection.
294+
295+
This function generates a change.
296+
"""
143297
return await self.query(
144-
f'set_time_zone(collection, zone)',
298+
'set_time_zone(collection, zone)',
145299
collection=collection,
146300
zone=zone,
147301
scope='@t')
148302

149303
async def time_zones_info(self) -> list:
304+
"""Returns all available time zones in ThingsDB.
305+
306+
This function does not generate a change.
307+
"""
150308
return await self.query('time_zones_info()', scope='@t')
151309

152310
async def user_info(self, user: Optional[str] = None) -> dict:
153311
if user is None:
154312
return await self.query('user_info()', scope='@t')
155-
return await self.query(f'user_info(user)', user=user, scope='@t')
313+
return await self.query('user_info(user)', user=user, scope='@t')
156314

157315
async def users_info(self) -> list:
158316
return await self.query('users_info()', scope='@t')
@@ -186,22 +344,47 @@ async def has_backup(self, backup_id: int, scope='@n'):
186344

187345
# TODO: new_backup
188346

189-
async def node_info(self, scope='@n'):
347+
async def node_info(self, scope='@n') -> dict:
190348
return await self.query('node_info()', scope=scope)
191349

192350
async def nodes_info(self, scope='@n') -> list:
193351
return await self.query('nodes_info()', scope=scope)
194352

195353
async def reset_counters(self, scope='@n') -> None:
354+
"""Resets the counters for the ThingsDB node you are connected too.
355+
356+
Other nodes are not affected. This will set the started_at counter
357+
value to the current UNIX time-stamp in seconds and all other counters
358+
to 0 (zero).
359+
360+
This function does not generate a change.
361+
"""
196362
return await self.query('reset_counters()', scope=scope)
197363

198-
async def restart_module(self, name: str):
199-
return await self.query(f'restart_module(name)', name=name, scope='@t')
364+
async def restart_module(self, name: str) -> None:
365+
"""Restarts a given module on the select node scope.
366+
367+
If you want to restart the module on all nodes, you can use the
368+
deploy_module(name, None) function with None as second argument.
369+
370+
This function does not generate a change.
371+
"""
372+
return await self.query('restart_module(name)', name=name, scope='@t')
200373

201374
async def set_log_level(self, log_level: str, scope='@n') -> None:
202375
assert log_level in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
203376
return await self.query(
204-
f'set_log_level(log_level)', log_level=log_level, scope=scope)
377+
'set_log_level(log_level)', log_level=log_level, scope=scope)
205378

206379
async def shutdown(self, scope='@n') -> None:
380+
"""Shutdown the node in the selected scope.
381+
382+
This is a clean shutdown, allowing all other nodes (and clients) to
383+
disconnect. Be CAREFUL using this function!!!
384+
385+
At least CHANGE privileges on the @node scope are required to shutdown
386+
a node.
387+
388+
This function does not generate a change.
389+
"""
207390
return await self.query('shutdown()', scope=scope)

thingsdb/client/protocol.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
from ..exceptions import ZeroDivisionError
3030

3131

32+
class Access(enum.IntEnum):
33+
QUERY = 0x01
34+
CHANGE = 0x02
35+
GRANT = 0x04
36+
JOIN = 0x08
37+
RUN = 0x10
38+
FULL = 0x1f
39+
40+
3241
class Proto(enum.IntEnum):
3342
# Events
3443
ON_NODE_STATUS = 0x00

0 commit comments

Comments
 (0)