Skip to content

Commit 7300646

Browse files
authored
Update client API interface with groups and notes (#184)
1 parent 7eb367b commit 7300646

5 files changed

Lines changed: 275 additions & 4 deletions

File tree

alertaclient/api.py

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from alertaclient.models.alert import Alert
1414
from alertaclient.models.blackout import Blackout
1515
from alertaclient.models.customer import Customer
16+
from alertaclient.models.enums import Scope
17+
from alertaclient.models.group import Group
1618
from alertaclient.models.heartbeat import Heartbeat
1719
from alertaclient.models.history import RichHistory
1820
from alertaclient.models.key import ApiKey
@@ -92,6 +94,12 @@ def update_attributes(self, id, attributes):
9294
}
9395
return self.http.put('/alert/%s/attributes' % id, data)
9496

97+
def add_note(self, id, note):
98+
data = {
99+
'note': note
100+
}
101+
return self.http.put('/alert/%s/note' % id, data)
102+
95103
def delete_alert(self, id):
96104
return self.http.delete('/alert/%s' % id)
97105

@@ -118,13 +126,25 @@ def get_top10_flapping(self, query=None):
118126
counts = self.http.get('/alerts/top10/flapping', query)
119127
return counts['top10']
120128

129+
def get_top10_standing(self, query=None):
130+
counts = self.http.get('/alerts/top10/standing', query)
131+
return counts['top10']
132+
121133
def get_environments(self, query=None):
122-
counts = self.http.get('/environments', query)
123-
return counts['environments']
134+
r = self.http.get('/environments', query)
135+
return r['environments']
124136

125137
def get_services(self, query=None):
126-
counts = self.http.get('/services', query)
127-
return counts['services']
138+
r = self.http.get('/services', query)
139+
return r['services']
140+
141+
def get_groups(self, query=None):
142+
r = self.http.get('/alerts/groups', query)
143+
return r['groups']
144+
145+
def get_tags(self, query=None):
146+
r = self.http.get('/alerts/tags', query)
147+
return r['tags']
128148

129149
# Blackouts
130150
def create_blackout(self, environment, service=None, resource=None, event=None, group=None, tags=None, customer=None, start=None, duration=None, text=None):
@@ -143,10 +163,16 @@ def create_blackout(self, environment, service=None, resource=None, event=None,
143163
r = self.http.post('/blackout', data)
144164
return Blackout.parse(r['blackout'])
145165

166+
def get_blackout(self, id):
167+
return Blackout.parse(self.http.get('/blackout/%s' % id)['blackout'])
168+
146169
def get_blackouts(self, query=None):
147170
r = self.http.get('/blackouts', query)
148171
return [Blackout.parse(b) for b in r['blackouts']]
149172

173+
def update_blackout(self, id, blackout):
174+
self.http.put('/blackout/%s' % id, blackout)
175+
150176
def delete_blackout(self, id):
151177
return self.http.delete('/blackout/%s' % id)
152178

@@ -159,10 +185,16 @@ def create_customer(self, customer, match):
159185
r = self.http.post('/customer', data)
160186
return Customer.parse(r['customer'])
161187

188+
def get_customer(self):
189+
return Customer.parse(self.http.get('/customer/%s' % id)['customer'])
190+
162191
def get_customers(self, query=None):
163192
r = self.http.get('/customers', query)
164193
return [Customer.parse(c) for c in r['customers']]
165194

195+
def update_customer(self, id, customer):
196+
self.http.put('/customer/%s' % id, customer)
197+
166198
def delete_customer(self, id):
167199
return self.http.delete('/customer/%s' % id)
168200

@@ -201,10 +233,22 @@ def create_key(self, username, scopes=None, expires=None, text='', customer=None
201233
r = self.http.post('/key', data)
202234
return ApiKey.parse(r['data'])
203235

236+
def get_key(self):
237+
return ApiKey.parse(self.http.get('/key/%s' % id)['key'])
238+
204239
def get_keys(self, query=None):
205240
r = self.http.get('/keys', query)
206241
return [ApiKey.parse(k) for k in r['keys']]
207242

243+
def update_key(self, id, **kwargs):
244+
data = {
245+
'scopes': kwargs.get('scopes'),
246+
'text': kwargs.get('text'),
247+
'expireTime': kwargs.get('expireTime'),
248+
'customer': kwargs.get('customer')
249+
}
250+
return self.http.put('/key/{}'.format(id), data)
251+
208252
def delete_key(self, id):
209253
return self.http.delete('/key/%s' % id)
210254

@@ -217,13 +261,27 @@ def create_perm(self, role, scopes=None):
217261
r = self.http.post('/perm', data)
218262
return Permission.parse(r['permission'])
219263

264+
def get_perm(self):
265+
return Permission.parse(self.http.get('/perm/%s' % id)['perm'])
266+
220267
def get_perms(self, query=None):
221268
r = self.http.get('/perms', query)
222269
return [Permission.parse(p) for p in r['permissions']]
223270

271+
def update_perm(self, id, **kwargs):
272+
data = {
273+
'match': kwargs.get('match'), # role
274+
'scopes': kwargs.get('scopes')
275+
}
276+
return self.http.put('/perm/{}'.format(id), data)
277+
224278
def delete_perm(self, id):
225279
return self.http.delete('/perm/%s' % id)
226280

281+
def get_scopes(self):
282+
r = self.http.get('/scopes')
283+
return [Scope(s) for s in r['scopes']]
284+
227285
# Users
228286
def signup(self, name, email, password, status, attributes=None, text=''):
229287
data = {
@@ -250,6 +308,19 @@ def create_user(self, name, email, password, status, roles=None, attributes=None
250308
r = self.http.post('/user', data)
251309
return User.parse(r['user'])
252310

311+
def get_user(self):
312+
return Permission.parse(self.http.get('/user/%s' % id)['user'])
313+
314+
def get_user_groups(self, id):
315+
r = self.http.get('/user/{}/groups'.format(id))
316+
return [Group.parse(g) for g in r['groups']]
317+
318+
def get_me(self):
319+
return User.parse(self.http.get('/user/me')['user'])
320+
321+
def get_me_attributes(self):
322+
return self.http.get('/user/me/attributes')['attributes']
323+
253324
def get_users(self, query=None):
254325
r = self.http.get('/users', query)
255326
return [User.parse(u) for u in r['users']]
@@ -293,6 +364,7 @@ def update_me_attributes(self, attributes):
293364
def delete_user(self, id):
294365
return self.http.delete('/user/%s' % id)
295366

367+
# Auth
296368
def login(self, username, password):
297369
data = {
298370
'username': username,
@@ -318,6 +390,42 @@ def userinfo(self):
318390
def config(self):
319391
return self.http.get('/config')
320392

393+
# Groups
394+
def create_group(self, name, text):
395+
data = {
396+
'name': name,
397+
'text': text
398+
}
399+
r = self.http.post('/group', data)
400+
return Group.parse(r['group'])
401+
402+
def get_group(self):
403+
return Group.parse(self.http.get('/group/%s' % id)['group'])
404+
405+
def get_group_users(self, id):
406+
r = self.http.get('/group/{}/users'.format(id))
407+
return [User.parse(u) for u in r['users']]
408+
409+
def get_users_groups(self, query=None):
410+
r = self.http.get('/groups', query)
411+
return [Group.parse(g) for g in r['groups']]
412+
413+
def update_group(self, id, kwargs):
414+
data = {
415+
'name': kwargs.get('name'),
416+
'text': kwargs.get('text')
417+
}
418+
return self.http.put('/group/{}'.format(id), data)
419+
420+
def add_user_to_group(self, group_id, user_id):
421+
return self.http.put('/group/{}/user/{}'.format(group_id, user_id))
422+
423+
def remove_user_from_group(self, group_id, user_id):
424+
return self.http.delete('/group/{}/user/{}'.format(group_id, user_id))
425+
426+
def delete_group(self, id):
427+
return self.http.delete('/group/%s' % id)
428+
321429
# Management
322430
def mgmt_status(self):
323431
return self.http.get('/management/status')

alertaclient/models/enums.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from enum import Enum
2+
3+
4+
class Scope(str, Enum):
5+
6+
read = 'read'
7+
write = 'write'
8+
admin = 'admin'
9+
read_alerts = 'read:alerts'
10+
write_alerts = 'write:alerts'
11+
admin_alerts = 'admin:alerts'
12+
read_blackouts = 'read:blackouts'
13+
write_blackouts = 'write:blackouts'
14+
admin_blackouts = 'admin:blackouts'
15+
read_heartbeats = 'read:heartbeats'
16+
write_heartbeats = 'write:heartbeats'
17+
admin_heartbeats = 'admin:heartbeats'
18+
write_users = 'write:users'
19+
admin_users = 'admin:users'
20+
read_groups = 'read:groups'
21+
admin_groups = 'admin:groups'
22+
read_perms = 'read:perms'
23+
admin_perms = 'admin:perms'
24+
read_customers = 'read:customers'
25+
admin_customers = 'admin:customers'
26+
read_keys = 'read:keys'
27+
write_keys = 'write:keys'
28+
admin_keys = 'admin:keys'
29+
write_webhooks = 'write:webhooks'
30+
read_oembed = 'read:oembed'
31+
read_management = 'read:management'
32+
admin_management = 'admin:management'
33+
read_userinfo = 'read:userinfo'
34+
35+
@property
36+
def action(self):
37+
return self.split(':')[0]
38+
39+
@property
40+
def resource(self):
41+
try:
42+
return self.split(':')[1]
43+
except IndexError:
44+
return None
45+
46+
@staticmethod
47+
def from_str(action: str, resource: str=None):
48+
"""Return a scope based on the supplied action and resource.
49+
50+
:param action: the scope action eg. read, write or admin
51+
:param resource: the specific resource of the scope, if any eg. alerts,
52+
blackouts, heartbeats, users, perms, customers, keys, webhooks,
53+
oembed, management or userinfo or None
54+
:return: Scope
55+
"""
56+
if resource:
57+
return Scope('{}:{}'.format(action, resource))
58+
else:
59+
return Scope(action)

alertaclient/models/group.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
from typing import Any, Dict, List, Optional, Tuple, Union
3+
from uuid import uuid4
4+
5+
JSON = Dict[str, Any]
6+
7+
8+
# class GroupUser:
9+
#
10+
# def __init__(self, id: str, login: str, name: str, status: str) -> None:
11+
# self.id = id
12+
# self.login = login
13+
# self.name = name
14+
# self.status = status
15+
#
16+
17+
# class GroupUsers:
18+
#
19+
# def __init__(self, id: str, users: List[GroupUser]) -> None:
20+
# self.id = id
21+
# self.users = users
22+
23+
24+
class Group:
25+
"""
26+
Group model.
27+
"""
28+
29+
def __init__(self, name: str, text: str, **kwargs) -> None:
30+
if not name:
31+
raise ValueError('Missing mandatory value for name')
32+
33+
self.id = kwargs.get('id', str(uuid4()))
34+
self.name = name
35+
self.text = text or ''
36+
self.count = kwargs.get('count')
37+
38+
def __repr__(self) -> str:
39+
return 'Group(id={!r}, name={!r}, text={!r}, count={!r})'.format(
40+
self.id, self.name, self.text, self.count)
41+
42+
@classmethod
43+
def parse(cls, json: JSON) -> 'Group':
44+
return Group(
45+
name=json.get('name', None),
46+
text=json.get('text', None)
47+
)

tests/test_groups.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
import unittest
3+
4+
import requests_mock
5+
6+
from alertaclient.api import Client
7+
8+
9+
class GroupTestCase(unittest.TestCase):
10+
11+
def setUp(self):
12+
self.client = Client()
13+
14+
self.key = """
15+
{
16+
"group": {
17+
"count": 0,
18+
"href": "http://localhost:8080/group/8ed5d256-4205-4dfc-b25d-185bd019cb21",
19+
"id": "8ed5d256-4205-4dfc-b25d-185bd019cb21",
20+
"name": "myGroup",
21+
"text": "test group"
22+
},
23+
"id": "8ed5d256-4205-4dfc-b25d-185bd019cb21",
24+
"status": "ok"
25+
}
26+
"""
27+
28+
@requests_mock.mock()
29+
def test_key(self, m):
30+
m.post('http://localhost:8080/group', text=self.key)
31+
group = self.client.create_group(name='myGroup', text='test group')
32+
self.assertEqual(group.name, 'myGroup')
33+
self.assertEqual(group.text, 'test group')

tests/test_notes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import unittest
3+
4+
import requests_mock
5+
6+
from alertaclient.api import Client
7+
8+
9+
class NotesTestCase(unittest.TestCase):
10+
11+
def setUp(self):
12+
self.client = Client()
13+
14+
self.key = """
15+
{
16+
"status": "ok"
17+
}
18+
"""
19+
20+
@requests_mock.mock()
21+
def test_add_note(self, m):
22+
m.put('http://localhost:8080/alert/e7020428-5dad-4a41-9bfe-78e9d55cda06/note', text=self.key)
23+
r = self.client.add_note(id='e7020428-5dad-4a41-9bfe-78e9d55cda06', note='this is a test note')
24+
self.assertEqual(r['status'], 'ok')

0 commit comments

Comments
 (0)