diff --git a/ardumont/sentry/README.md b/ardumont/sentry/README.md index cc3d9a3..405e330 100644 --- a/ardumont/sentry/README.md +++ b/ardumont/sentry/README.md @@ -1,47 +1,63 @@ sentry ======= +Basic cli to analyze sentry data in [1]. + +# Requisites + +You need a sentry account in [1] +You need to generate an api auth token in [2] + +[1] https://sentry.softwareheritage.org + +[2] https://sentry.softwareheritage.org/settings/account/api/auth-tokens/ + + +# Virtualenv + Enter the virtualenv (this is using pipenv): ``` pipenv shell ``` -Limited use case so far: +# Use cases -List existing projects in our sentry instance: +## List projects +``` python -m sentry --token $TOKEN project \ | jq . { "swh-objstorage": { "id": "4", "name": "swh-objstorage" }, "swh-storage": { "id": "3", "name": "swh.storage" }, ... } ``` -List issues: +## List issues + ``` $ python -m sentry --token $TOKEN \ issue --project-slug swh-storage \ - | jq ".[0].metadata" -{ - "function": "_content_add_metadata", - "type": "HashCollision", - "value": "('sha1', b'\\xc6\\xfd\\t\\xe3t\\xb5/\\x9c\\xf7\\x96\\xb7D\\xf1\\xe7\\xcc\\x0b)\\x94JS', [{'sha1_git': b'\\xbc\\x90If\\x02\\x01\\xcc\\x94B;\\xc3\\xc5,\\xbf,\\x97\\xc16\\x920', 'blake2s256': b'\\x8fe5]\\x97\\xad\\xe2\\x05:B\\x8bJ\\x8f?i\\xa2|S>\\x7f.\\xad\\x18\\xe9\\x04\\xba\\x17\\xeb\\xa6\\x97\\x87\\x03', 'sha256': b'`\\x194\\xa7\\x08-\\x1c_\\xa8c\\x99\\xdb3~\\xda6\\xf5\\x13g~:#\\xd3\\xfeP\\xcfT!\\xa05\\xdar', 'sha1': b'\\xc6\\xfd\\t\\xe3t\\xb5/\\x9c\\xf7\\x96\\xb7D\\xf1\\xe7\\xcc\\x0b)\\x94JS'}])", - "filename": "swh/storage/storage.py" -} + | jq . +[{ + ... + "1438": { + "short-id": "SWH-STORAGE-AP", + "status": "unresolved", + "metadata": { + "function": "_content_add_metadata", + "type": "HashCollision", + "value": "('sha1', b'\\xc6\\xfd\\t\\xe3t\\xb5/\\x9c\\xf7\\x96\\xb7D\\xf1\\xe7\\xcc\\x0b)\\x94JS', [{'sha1_git': b'\\xbc\\x90If\\x02\\x01\\xcc\\x94B;\\xc3\\xc5,\\xbf,\\x97\\xc16\\x920', 'blake2s256': b'\\x8fe5]\\x97\\xad\\xe2\\x05:B\\x8bJ\\x8f?i\\xa2|S>\\x7f.\\xad\\x18\\xe9\\x04\\xba\\x17\\xeb\\xa6\\x97\\x87\\x03', 'sha256': b'`\\x194\\xa7\\x08-\\x1c_\\xa8c\\x99\\xdb3~\\xda6\\xf5\\x13g~:#\\xd3\\xfeP\\xcfT!\\xa05\\xdar', 'sha1': b'\\xc6\\xfd\\t\\xe3t\\xb5/\\x9c\\xf7\\x96\\xb7D\\xf1\\xe7\\xcc\\x0b)\\x94JS'}])", + "filename": "swh/storage/storage.py" + } + }, + ... +] ``` - -Note: -You need a sentry account in [1] -You need to generate an api auth token in [2] - -[1] https://sentry.softwareheritage.org - -[2] https://sentry.softwareheritage.org/settings/account/api/auth-tokens/ diff --git a/ardumont/sentry/sentry.py b/ardumont/sentry/sentry.py index 1d51444..845b5d2 100644 --- a/ardumont/sentry/sentry.py +++ b/ardumont/sentry/sentry.py @@ -1,117 +1,129 @@ #!/usr/bin/env python3 import json import logging import click import requests from typing import Any, Dict, Optional, Iterable logger = logging.getLogger(__name__) SENTRY_URL = 'https://sentry.softwareheritage.org' -ORGANIZATION_SLUG = 'swh' +ORGA_SLUG = 'swh' def url_api_project(base_url: str) -> str: return f'{base_url}/api/0/projects/' def url_api_token(base_url: str) -> str: return f'{base_url}/settings/account/api/auth-tokens/' -def url_project_issue(base_url: str, project_slug: str) -> str: - return f'{base_url}/api/0/projects/{ORGANIZATION_SLUG}/{project_slug}/issues/' +def url_project_issues(base_url: str, project_slug: str, short_id: Optional[str] = None) -> str: + return f'{base_url}/api/0/projects/{ORGA_SLUG}/{project_slug}/issues/' + + +def url_issue(base_url: str, issue_id: int) -> str: + return f'{base_url}/api/0/issues/{issue_id}/' @click.group() @click.option('-a', '--api-url', default=SENTRY_URL, help='sentry api to use') @click.option('-t', '--token', help='Api authentication token') @click.pass_context def main(ctx, api_url: str, token: str): """Allow sentry data manipulation with the click """ api_token = url_api_token(api_url) if not token: raise ValueError( f'Missing api token, connect and generate one in {api_token}' ) ctx.ensure_object(dict) ctx.obj['token'] = token ctx.obj['url'] = { 'base': api_url, 'project': url_api_project(api_url), 'api-token': api_token, } def project_name_to_id(projects: Iterable[Dict[str, Any]]) -> Dict[str, Any]: """Compute the project mapping from name to id. """ mapping = {} for project in projects: mapping[project['slug']] = { 'id': project['id'], 'name': project['name'], } return mapping def query(url, token: Optional[str] = None) -> Optional[Dict[str, Any]]: """Query the sentry api url with authentication token. """ resp = requests.get(url, headers={ 'Authorization': f'Bearer {token}', 'content-type': 'application/json' }) if resp.ok: logger.debug('resp: %(resp)s', {'resp': resp}) data = resp.json() return data @main.command('project') @click.pass_context def list_projects(ctx: Dict) -> Dict[str, Any]: """List all projects's. This returns a mapping from their slug to their {id, name}. """ url = ctx.obj['url']['project'] token = ctx.obj['token'] data = query(url, token=token) if data: projects = project_name_to_id(data) click.echo(json.dumps(projects)) -@main.command('issue') +@main.command('issues') @click.option('--project-slug', '-p', required=1, help="Project's slug identifier") -# @click.option('--issue', '-i', help='Issue id') @click.pass_context -def issue(ctx, project_slug): - """List all projects's. This returns a mapping from their name to their id. +def issues(ctx, project_slug): + """List all projects's issues. This returns a mapping from their id to their + summary. """ base_url = ctx.obj['url']['base'] token = ctx.obj['token'] - url = url_project_issue(base_url, project_slug) + url = url_project_issues(base_url, project_slug) data = query(url, token=token) if data: - click.echo(json.dumps(data)) + mappings = {} + for issue in data: + mappings[issue['id']] = { + 'short-id': issue['shortId'], + 'status': issue['status'], + 'metadata': issue['metadata'], + } + + click.echo(json.dumps(mappings)) if __name__ == '__main__': # logging.basicConfig(level=logging.DEBUG) main()