diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cb406d3..6ce1672 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks.git - rev: v1.4.0 + rev: v2.5.0 hooks: - id: check-added-large-files - id: check-ast @@ -23,16 +23,16 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v1.5 + rev: v1.5.1 hooks: - id: autopep8 - repo: https://github.com/asottile/pyupgrade - rev: v1.26.2 + rev: v2.1.1 hooks: - id: pyupgrade args: ['--py3-plus'] - repo: https://github.com/asottile/seed-isort-config - rev: v1.9.4 + rev: v2.1.1 hooks: - id: seed-isort-config - repo: https://github.com/pre-commit/mirrors-isort diff --git a/alertaclient/api.py b/alertaclient/api.py index 7b32324..0cdd41c 100644 --- a/alertaclient/api.py +++ b/alertaclient/api.py @@ -20,6 +20,7 @@ from alertaclient.models.heartbeat import Heartbeat from alertaclient.models.history import RichHistory from alertaclient.models.key import ApiKey +from alertaclient.models.note import Note from alertaclient.models.permission import Permission from alertaclient.models.user import User from alertaclient.utils import CustomJsonEncoder, DateTime @@ -97,12 +98,6 @@ def update_attributes(self, id, attributes): } return self.http.put('/alert/%s/attributes' % id, data) - def add_note(self, id, note): - data = { - 'note': note - } - return self.http.put('/alert/%s/note' % id, data) - def delete_alert(self, id): return self.http.delete('/alert/%s' % id) @@ -149,6 +144,25 @@ def get_tags(self, query=None): r = self.http.get('/alerts/tags', query) return r['tags'] + def alert_note(self, id, note): + data = { + 'note': note + } + return self.http.put('/alert/%s/note' % id, data) + + def get_alert_notes(self, id, page=1, page_size=None): + r = self.http.get('/alert/{}/notes'.format(id), page=page, page_size=page_size) + return [Note.parse(b) for b in r['notes']] + + def update_alert_note(self, id, note_id, text): + data = { + 'text': text, + } + self.http.put('/alert/{}/note/{}'.format(id, note_id), data) + + def delete_alert_note(self, id, note_id): + return self.http.delete('/alert/{}/note/{}'.format(id, note_id)) + # Blackouts def create_blackout(self, environment, service=None, resource=None, event=None, group=None, tags=None, customer=None, start=None, duration=None, text=None): data = { @@ -313,7 +327,7 @@ def create_user(self, name, email, password, status, roles=None, attributes=None return User.parse(r['user']) def get_user(self): - return Permission.parse(self.http.get('/user/%s' % id)['user']) + return User.parse(self.http.get('/user/%s' % id)['user']) def get_user_groups(self, id): r = self.http.get('/user/{}/groups'.format(id)) @@ -414,7 +428,7 @@ def get_users_groups(self, query=None): r = self.http.get('/groups', query) return [Group.parse(g) for g in r['groups']] - def update_group(self, id, kwargs): + def update_group(self, id, **kwargs): data = { 'name': kwargs.get('name'), 'text': kwargs.get('text') diff --git a/alertaclient/commands/cmd_note.py b/alertaclient/commands/cmd_note.py index eda6d24..5bc82e8 100644 --- a/alertaclient/commands/cmd_note.py +++ b/alertaclient/commands/cmd_note.py @@ -3,25 +3,30 @@ from alertaclient.utils import build_query -@click.command('note', short_help='Add note to alerts') -@click.option('--ids', '-i', metavar='UUID', multiple=True, help='List of alert IDs (can use short 8-char id)') +@click.command('note', short_help='Add note') +@click.option('--ids', '-i', metavar='UUID', multiple=True, help='List of note IDs') +@click.option('--alert-ids', '-i', metavar='UUID', multiple=True, help='List of alert IDs (can use short 8-char id)') @click.option('--query', '-q', 'query', metavar='QUERY', help='severity:"warning" AND resource:web') @click.option('--filter', '-f', 'filters', metavar='FILTER', multiple=True, help='KEY=VALUE eg. serverity=warning resource=web') -@click.option('--text', required=True, help='Note or message') +@click.option('--text', help='Note or message') +@click.option('--delete', '-D', metavar='ID', nargs=2, help='Delete note parent ID and note ID') @click.pass_obj -def cli(obj, ids, query, filters, text): - """Add note to alerts.""" +def cli(obj, ids, alert_ids, query, filters, text, delete): + """Add or delete note to alerts.""" client = obj['client'] - if ids: - total = len(ids) + if delete: + client.delete_alert_note(*delete) else: - if query: - query = [('q', query)] + if alert_ids: + total = len(alert_ids) else: - query = build_query(filters) - total, _, _ = client.get_count(query) - ids = [a.id for a in client.get_alerts(query)] + if query: + query = [('q', query)] + else: + query = build_query(filters) + total, _, _ = client.get_count(query) + alert_ids = [a.id for a in client.get_alerts(query)] - with click.progressbar(ids, label='Add note to {} alerts'.format(total)) as bar: - for id in bar: - client.add_note(id, note=text) + with click.progressbar(alert_ids, label='Add note to {} alerts'.format(total)) as bar: + for id in bar: + client.alert_note(id, note=text) diff --git a/alertaclient/commands/cmd_notes.py b/alertaclient/commands/cmd_notes.py new file mode 100644 index 0000000..7435f67 --- /dev/null +++ b/alertaclient/commands/cmd_notes.py @@ -0,0 +1,25 @@ +import json + +import click +from tabulate import tabulate + + +@click.command('notes', short_help='List notes') +@click.option('--alert-id', '-i', metavar='UUID', help='alert IDs (can use short 8-char id)') +@click.pass_obj +def cli(obj, alert_id): + """List notes.""" + client = obj['client'] + if alert_id: + if obj['output'] == 'json': + r = client.http.get('/alert/{}/notes'.format(alert_id)) + click.echo(json.dumps(r['notes'], sort_keys=True, indent=4, ensure_ascii=False)) + else: + timezone = obj['timezone'] + headers = { + 'id': 'NOTE ID', 'text': 'NOTE', 'user': 'USER', 'type': 'TYPE', 'attributes': 'ATTRIBUTES', + 'createTime': 'CREATED', 'updateTime': 'UPDATED', 'related': 'RELATED ID', 'customer': 'CUSTOMER' + } + click.echo(tabulate([n.tabular(timezone) for n in client.get_alert_notes(alert_id)], headers=headers, tablefmt=obj['output'])) + else: + raise click.UsageError('Need "--alert-id" to list notes.') diff --git a/alertaclient/models/note.py b/alertaclient/models/note.py new file mode 100644 index 0000000..8718949 --- /dev/null +++ b/alertaclient/models/note.py @@ -0,0 +1,51 @@ +from datetime import datetime + +from alertaclient.utils import DateTime + + +class Note: + + def __init__(self, text, user, note_type, **kwargs): + + self.id = kwargs.get('id', None) + self.text = text + self.user = user + self.note_type = note_type + self.attributes = kwargs.get('attributes', None) or dict() + self.create_time = kwargs['create_time'] if 'create_time' in kwargs else datetime.utcnow() + self.update_time = kwargs.get('update_time') + self.alert = kwargs.get('alert') + self.customer = kwargs.get('customer') + + @classmethod + def parse(cls, json): + return Note( + id=json.get('id', None), + text=json.get('text', None), + user=json.get('user', None), + attributes=json.get('attributes', dict()), + note_type=json.get('type', None), + create_time=DateTime.parse(json['createTime']) if 'createTime' in json else None, + update_time=DateTime.parse(json['updateTime']) if 'updateTime' in json else None, + alert=json.get('related', {}).get('alert'), + customer=json.get('customer', None) + ) + + def __repr__(self): + return 'Note(id={!r}, text={!r}, user={!r}, type={!r}, customer={!r})'.format( + self.id, self.text, self.user, self.note_type, self.customer + ) + + def tabular(self, timezone=None): + note = { + 'id': self.id, + 'text': self.text, + 'user': self.user, + # 'attributes': self.attributes, + 'type': self.note_type, + 'createTime': DateTime.localtime(self.create_time, timezone), + 'updateTime': DateTime.localtime(self.update_time, timezone), + 'related': self.alert, + 'customer': self.customer + } + return note diff --git a/tests/test_notes.py b/tests/test_notes.py index 7dc90bd..f2d11d6 100644 --- a/tests/test_notes.py +++ b/tests/test_notes.py @@ -10,7 +10,7 @@ class NotesTestCase(unittest.TestCase): def setUp(self): self.client = Client() - self.key = """ + self.note = """ { "status": "ok" } @@ -18,6 +18,6 @@ def setUp(self): @requests_mock.mock() def test_add_note(self, m): - m.put('http://localhost:8080/alert/e7020428-5dad-4a41-9bfe-78e9d55cda06/note', text=self.key) - r = self.client.add_note(id='e7020428-5dad-4a41-9bfe-78e9d55cda06', note='this is a test note') + m.put('http://localhost:8080/alert/e7020428-5dad-4a41-9bfe-78e9d55cda06/note', text=self.note) + r = self.client.alert_note(id='e7020428-5dad-4a41-9bfe-78e9d55cda06', note='this is a test note') self.assertEqual(r['status'], 'ok')