From 884f1f6d92872a80ca86a8e75e07cc2754f73d74 Mon Sep 17 00:00:00 2001 From: anar Date: Nov 06 2017 08:57:44 +0000 Subject: UserIssues widget initial commit --- diff --git a/hubs/default_config.py b/hubs/default_config.py index b0e805d..d2cd3a4 100644 --- a/hubs/default_config.py +++ b/hubs/default_config.py @@ -73,4 +73,5 @@ WIDGETS = [ 'hubs.widgets.sticky:Sticky', 'hubs.widgets.subscriptions:Subscriptions', 'hubs.widgets.workflow.updates2stable:Updates2Stable', + 'hubs.widgets.user_issues:UserIssues', ] diff --git a/hubs/static/css/style.css b/hubs/static/css/style.css index 9257a3b..96a4b8d 100644 --- a/hubs/static/css/style.css +++ b/hubs/static/css/style.css @@ -318,12 +318,14 @@ font-size: 32pt; * Issue widgets */ .widget .widget-pagure_pr li, +.widget .widget-user_issues li, .widget .widget-githubissues li, .widget .widget-bugzilla li { border-bottom: 1px solid rgba(0, 0, 0, .125); } .widget .widget-pagure_pr li:last-child, +.widget .widget-user_issues li:last-child, .widget .widget-githubissues li:last-child, .widget .widget-bugzilla li:last-child { border: 0} diff --git a/hubs/tests/test_widget_validators.py b/hubs/tests/test_widget_validators.py index 77a2a54..e66ab45 100644 --- a/hubs/tests/test_widget_validators.py +++ b/hubs/tests/test_widget_validators.py @@ -60,3 +60,14 @@ class ValidatorsTest(APPTest): validators.PagureRepo("fedora-hubs"), "fedora-hubs") self.assertRaises(ValueError, validators.PagureRepo, "something-that-does-not-exist") + + def test_pagure_user(self): + self.assertEqual(validators.PagureUser("abompard"), "abompard") + self.assertRaises(ValueError, validators.PagureUser, + "someone-that-does-not-exist") + + def test_bugzilla_user(self): + self.assertEqual(validators.BugzillaUser("aurelien@bompard.org"), + "aurelien@bompard.org") + self.assertRaises(ValueError, validators.BugzillaUser, + "someone-that-does-not-exist") diff --git a/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_bugzilla_user b/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_bugzilla_user new file mode 100644 index 0000000..ee7d3c4 --- /dev/null +++ b/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_bugzilla_user @@ -0,0 +1,61 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: https://bugzilla.redhat.com/jsonrpc.cgi?method=User.get¶ms=[%7B%22names%22:[%22aurelien@bompard.org%22]%7D] + response: + body: + string: !!binary | + H4sIAAAAAAAAAy3NQQrDIBCF4auUWQeFCF24anuNUsqkDokwahh1E/HuFZLt+/h5DUgkCdhYmSfw + DixspezZar3U9fDMqITchkX9UtAwgVCuXMA2qJkkg323sSF/IwYa+bMKsad4e6Wwo7iRXIKXPJZT + VJIVzlNj7vNs+qf3Py9QgsCSAAAA + headers: + connection: [close] + content-encoding: [gzip] + content-length: ['135'] + content-type: [application/json; charset=UTF-8] + date: ['Sun, 05 Nov 2017 11:21:36 GMT'] + server: [Apache] + set-cookie: [Bugzilla_login_request_cookie=QrHmDU8BpR; domain=bugzilla.redhat.com; + path=/; HttpOnly] + vary: ['Accept-Encoding,User-Agent'] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: https://bugzilla.redhat.com/jsonrpc.cgi?method=User.get¶ms=[%7B%22names%22:[%22someone-that-does-not-exist%22]%7D] + response: + body: + string: !!binary | + H4sIAAAAAAAAAy2Ouw7CMAxFf8XywkJaMbB0REJiRvxAaEwbKYlR7EiUin/HPDbLOufeuyLVyhWH + FTOJ+IlwwMtMlSAKFIYmVKH4TAE2wpm4kNPZqwtM4gqro0cU3XRwjGoeLNwgR3G63M2x19cGrvDR + fnmz/2QrLKRQaTLfCgPcDPJwtutk5KFNz5iSBz+O3Ip2uMWRgw3c715bjMGWzqp3Gfr++mc7i7GW + buTcG15JWlIcSkvp9QaAQ1aH6wAAAA== + headers: + connection: [close] + content-encoding: [gzip] + content-length: ['193'] + content-type: [application/json; charset=UTF-8] + date: ['Sun, 05 Nov 2017 11:21:40 GMT'] + server: [Apache] + set-cookie: [Bugzilla_login_request_cookie=TFTzxQO4s0; domain=bugzilla.redhat.com; + path=/; HttpOnly] + vary: ['Accept-Encoding,User-Agent'] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +version: 1 diff --git a/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_pagure_user b/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_pagure_user new file mode 100644 index 0000000..169ce75 --- /dev/null +++ b/hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_pagure_user @@ -0,0 +1,272 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: https://pagure.io/api/0/user/abompard + response: + body: {string: !!python/unicode "{\n \"forks\": [\n {\n \"access_groups\"\ + : {\n \"admin\": [],\n \"commit\": [],\n \"ticket\":\ + \ []\n },\n \"access_users\": {\n \"admin\": [],\n \ + \ \"commit\": [],\n \"owner\": [\n \"abompard\"\n \ + \ ],\n \"ticket\": []\n },\n \"close_status\": [\n \ + \ \"Invalid\",\n \"Insufficient data\",\n \"Fixed\",\n \ + \ \"Duplicate\"\n ],\n \"custom_keys\": [],\n \"date_created\"\ + : \"1476178044\",\n \"date_modified\": \"1476178044\",\n \"description\"\ + : \"Fedora Hubs\",\n \"fullname\": \"forks/abompard/fedora-hubs\",\n\ + \ \"id\": 1232,\n \"milestones\": {},\n \"name\": \"fedora-hubs\"\ + ,\n \"namespace\": null,\n \"parent\": {\n \"access_groups\"\ + : {\n \"admin\": [],\n \"commit\": [],\n \"ticket\"\ + : []\n },\n \"access_users\": {\n \"admin\": [\n \ + \ \"duffy\",\n \"lmacken\",\n \"nyazdani\"\ + ,\n \"bee2502\",\n \"devyani7\",\n \"dhrish20\"\ + ,\n \"jflory7\",\n \"pingou\",\n \"ryanlerch\"\ + ,\n \"skrzepto\",\n \"sayanchowdhury\",\n \ + \ \"bkorren\",\n \"atelic\",\n \"wispfox\",\n \ + \ \"jcline\",\n \"abompard\"\n ],\n \"\ + commit\": [\n \"etsang\"\n ],\n \"owner\": [\n\ + \ \"decause\"\n ],\n \"ticket\": []\n \ + \ },\n \"close_status\": [\n \"Invalid\",\n \"Insufficient\ + \ data\",\n \"Fixed\",\n \"Duplicate\"\n ],\n \ + \ \"custom_keys\": [],\n \"date_created\": \"1433438868\",\n \ + \ \"date_modified\": \"1505214285\",\n \"description\": \"Fedora\ + \ Hubs\",\n \"fullname\": \"fedora-hubs\",\n \"id\": 50,\n \ + \ \"milestones\": {\n \"2017 Year End\": \"\",\n \"\ + Flock 2017\": \"\",\n \"None\": \"\",\n \"Post Flock 2017\"\ + : \"\",\n \"Production\": \"\"\n },\n \"name\": \"\ + fedora-hubs\",\n \"namespace\": null,\n \"parent\": null,\n\ + \ \"priorities\": {\n \"1\": \"High\",\n \"2\": \"\ + Normal\",\n \"3\": \"Low\"\n },\n \"settings\": {\n\ + \ \"Enforce_signed-off_commits_in_pull-request\": false,\n \ + \ \"Minimum_score_to_merge_pull-request\": -1,\n \"Only_assignee_can_merge_pull-request\"\ + : false,\n \"Web-hooks\": null,\n \"always_merge\": false,\n\ + \ \"fedmsg_notifications\": true,\n \"issue_tracker\": true,\n\ + \ \"issues_default_to_private\": false,\n \"project_documentation\"\ + : true,\n \"pull_request_access_only\": false,\n \"pull_requests\"\ + : true\n },\n \"tags\": [\n \"fedora-infra\",\n \ + \ \"hubs\"\n ],\n \"url_path\": \"fedora-hubs\",\n \ + \ \"user\": {\n \"fullname\": \"Remy DeCausemaker\",\n \ + \ \"name\": \"decause\"\n }\n },\n \"priorities\": {},\n\ + \ \"settings\": {\n \"Enforce_signed-off_commits_in_pull-request\"\ + : false,\n \"Minimum_score_to_merge_pull-request\": -1,\n \"\ + Only_assignee_can_merge_pull-request\": false,\n \"Web-hooks\": null,\n\ + \ \"always_merge\": false,\n \"fedmsg_notifications\": true,\n\ + \ \"issue_tracker\": false,\n \"issues_default_to_private\"\ + : false,\n \"project_documentation\": false,\n \"pull_request_access_only\"\ + : false,\n \"pull_requests\": false\n },\n \"tags\": [],\n\ + \ \"url_path\": \"fork/abompard/fedora-hubs\",\n \"user\": {\n \ + \ \"fullname\": \"Aurelien Bompard\",\n \"name\": \"abompard\"\ + \n }\n },\n {\n \"access_groups\": {\n \"admin\": [],\n\ + \ \"commit\": [],\n \"ticket\": []\n },\n \"access_users\"\ + : {\n \"admin\": [],\n \"commit\": [],\n \"owner\": [\n\ + \ \"abompard\"\n ],\n \"ticket\": []\n },\n \ + \ \"close_status\": [],\n \"custom_keys\": [],\n \"date_created\"\ + : \"1490611717\",\n \"date_modified\": \"1490611717\",\n \"description\"\ + : \"The Fedora Infrastructure Sphinx documentation project\",\n \"fullname\"\ + : \"forks/abompard/infra-docs\",\n \"id\": 2156,\n \"milestones\"\ + : {},\n \"name\": \"infra-docs\",\n \"namespace\": null,\n \ + \ \"parent\": {\n \"access_groups\": {\n \"admin\": [],\n\ + \ \"commit\": [],\n \"ticket\": []\n },\n \ + \ \"access_users\": {\n \"admin\": [\n \"abompard\",\n\ + \ \"bowlofeggs\",\n \"kushal\",\n \"maxamillion\"\ + ,\n \"kevin\",\n \"puiterwijk\",\n \"pingou\"\ + ,\n \"sayanchowdhury\",\n \"ryanlerch\",\n \ + \ \"pfrields\"\n ],\n \"commit\": [\n \"tflink\"\ + ,\n \"kparal\"\n ],\n \"owner\": [\n \ + \ \"jcline\"\n ],\n \"ticket\": []\n },\n \ + \ \"close_status\": [],\n \"custom_keys\": [],\n \"date_created\"\ + : \"1485549712\",\n \"date_modified\": \"1502897027\",\n \"\ + description\": \"The Fedora Infrastructure Sphinx documentation project\"\ + ,\n \"fullname\": \"infra-docs\",\n \"id\": 1658,\n \"\ + milestones\": {},\n \"name\": \"infra-docs\",\n \"namespace\"\ + : null,\n \"parent\": null,\n \"priorities\": {},\n \"\ + settings\": {\n \"Enforce_signed-off_commits_in_pull-request\": false,\n\ + \ \"Minimum_score_to_merge_pull-request\": -1,\n \"Only_assignee_can_merge_pull-request\"\ + : false,\n \"Web-hooks\": null,\n \"always_merge\": false,\n\ + \ \"fedmsg_notifications\": true,\n \"issue_tracker\": true,\n\ + \ \"issues_default_to_private\": false,\n \"project_documentation\"\ + : true,\n \"pull_request_access_only\": false,\n \"pull_requests\"\ + : true\n },\n \"tags\": [\n \"fedora-infra\",\n \ + \ \"docs\",\n \"infrastructure\",\n \"fedora\",\n\ + \ \"documentation\"\n ],\n \"url_path\": \"infra-docs\"\ + ,\n \"user\": {\n \"fullname\": \"Jeremy Cline\",\n \ + \ \"name\": \"jcline\"\n }\n },\n \"priorities\": {},\n\ + \ \"settings\": {\n \"Enforce_signed-off_commits_in_pull-request\"\ + : false,\n \"Minimum_score_to_merge_pull-request\": -1,\n \"\ + Only_assignee_can_merge_pull-request\": false,\n \"Web-hooks\": null,\n\ + \ \"always_merge\": false,\n \"fedmsg_notifications\": true,\n\ + \ \"issue_tracker\": false,\n \"issues_default_to_private\"\ + : false,\n \"project_documentation\": false,\n \"pull_request_access_only\"\ + : false,\n \"pull_requests\": false\n },\n \"tags\": [],\n\ + \ \"url_path\": \"fork/abompard/infra-docs\",\n \"user\": {\n \ + \ \"fullname\": \"Aurelien Bompard\",\n \"name\": \"abompard\"\ + \n }\n }\n ],\n \"repos\": [\n {\n \"access_groups\": {\n\ + \ \"admin\": [],\n \"commit\": [],\n \"ticket\": []\n\ + \ },\n \"access_users\": {\n \"admin\": [\n \"duffy\"\ + ,\n \"lmacken\",\n \"nyazdani\",\n \"bee2502\"\ + ,\n \"devyani7\",\n \"dhrish20\",\n \"jflory7\"\ + ,\n \"pingou\",\n \"ryanlerch\",\n \"skrzepto\"\ + ,\n \"sayanchowdhury\",\n \"bkorren\",\n \"atelic\"\ + ,\n \"wispfox\",\n \"jcline\",\n \"abompard\"\n\ + \ ],\n \"commit\": [\n \"etsang\"\n ],\n \ + \ \"owner\": [\n \"decause\"\n ],\n \"ticket\"\ + : []\n },\n \"close_status\": [\n \"Invalid\",\n \"\ + Insufficient data\",\n \"Fixed\",\n \"Duplicate\"\n ],\n\ + \ \"custom_keys\": [],\n \"date_created\": \"1433438868\",\n \ + \ \"date_modified\": \"1505214285\",\n \"description\": \"Fedora Hubs\"\ + ,\n \"fullname\": \"fedora-hubs\",\n \"id\": 50,\n \"milestones\"\ + : {\n \"2017 Year End\": \"\",\n \"Flock 2017\": \"\",\n \ + \ \"None\": \"\",\n \"Post Flock 2017\": \"\",\n \"Production\"\ + : \"\"\n },\n \"name\": \"fedora-hubs\",\n \"namespace\": null,\n\ + \ \"parent\": null,\n \"priorities\": {\n \"1\": \"High\"\ + ,\n \"2\": \"Normal\",\n \"3\": \"Low\"\n },\n \"\ + settings\": {\n \"Enforce_signed-off_commits_in_pull-request\": false,\n\ + \ \"Minimum_score_to_merge_pull-request\": -1,\n \"Only_assignee_can_merge_pull-request\"\ + : false,\n \"Web-hooks\": null,\n \"always_merge\": false,\n\ + \ \"fedmsg_notifications\": true,\n \"issue_tracker\": true,\n\ + \ \"issues_default_to_private\": false,\n \"project_documentation\"\ + : true,\n \"pull_request_access_only\": false,\n \"pull_requests\"\ + : true\n },\n \"tags\": [\n \"fedora-infra\",\n \"\ + hubs\"\n ],\n \"url_path\": \"fedora-hubs\",\n \"user\": {\n\ + \ \"fullname\": \"Remy DeCausemaker\",\n \"name\": \"decause\"\ + \n }\n },\n {\n \"access_groups\": {\n \"admin\": [],\n\ + \ \"commit\": [],\n \"ticket\": []\n },\n \"access_users\"\ + : {\n \"admin\": [],\n \"commit\": [],\n \"owner\": [\n\ + \ \"abompard\"\n ],\n \"ticket\": []\n },\n \ + \ \"close_status\": [],\n \"custom_keys\": [],\n \"date_created\"\ + : \"1501323827\",\n \"date_modified\": \"1501326044\",\n \"description\"\ + : \"A widget example for the Fedora Hubs workshop\",\n \"fullname\":\ + \ \"fedora-hubs-widget-workshop\",\n \"id\": 2691,\n \"milestones\"\ + : {},\n \"name\": \"fedora-hubs-widget-workshop\",\n \"namespace\"\ + : null,\n \"parent\": null,\n \"priorities\": {},\n \"settings\"\ + : {\n \"Enforce_signed-off_commits_in_pull-request\": false,\n \ + \ \"Minimum_score_to_merge_pull-request\": -1,\n \"Only_assignee_can_merge_pull-request\"\ + : false,\n \"Web-hooks\": null,\n \"always_merge\": false,\n\ + \ \"fedmsg_notifications\": true,\n \"issue_tracker\": true,\n\ + \ \"issues_default_to_private\": false,\n \"project_documentation\"\ + : true,\n \"pull_request_access_only\": false,\n \"pull_requests\"\ + : true\n },\n \"tags\": [],\n \"url_path\": \"fedora-hubs-widget-workshop\"\ + ,\n \"user\": {\n \"fullname\": \"Aurelien Bompard\",\n \ + \ \"name\": \"abompard\"\n }\n },\n {\n \"access_groups\"\ + : {\n \"admin\": [\n \"sysadmin-main\"\n ],\n \ + \ \"commit\": [],\n \"ticket\": []\n },\n \"access_users\"\ + : {\n \"admin\": [\n \"smooge\",\n \"puiterwijk\"\ + ,\n \"nb\",\n \"pingou\",\n \"ralph\",\n \ + \ \"abompard\"\n ],\n \"commit\": [],\n \"owner\"\ + : [\n \"kevin\"\n ],\n \"ticket\": [\n \"\ + bowlofeggs\",\n \"adrian\",\n \"mizdebsk\",\n \"\ + jcline\"\n ]\n },\n \"close_status\": [\n \"Duplicate\"\ + ,\n \"Fixed\",\n \"Insufficient data\",\n \"Invalid\"\ + ,\n \"Upstream\",\n \"Will Not/Can Not fix\"\n ],\n \ + \ \"custom_keys\": [],\n \"date_created\": \"1474746651\",\n \"\ + date_modified\": \"1508776105\",\n \"description\": \"Fedora Infrastructure\"\ + ,\n \"fullname\": \"fedora-infrastructure\",\n \"id\": 1176,\n \ + \ \"milestones\": {},\n \"name\": \"fedora-infrastructure\",\n \ + \ \"namespace\": null,\n \"parent\": null,\n \"priorities\":\ + \ {\n \"\": \"\",\n \"1\": \"High\",\n \"2\": \"Normal\"\ + ,\n \"3\": \"Low\",\n \"4\": \"Handwavy Future\"\n },\n\ + \ \"settings\": {\n \"Enforce_signed-off_commits_in_pull-request\"\ + : false,\n \"Minimum_score_to_merge_pull-request\": -1,\n \"\ + Only_assignee_can_merge_pull-request\": false,\n \"Web-hooks\": null,\n\ + \ \"always_merge\": false,\n \"fedmsg_notifications\": true,\n\ + \ \"issue_tracker\": true,\n \"issues_default_to_private\":\ + \ false,\n \"project_documentation\": false,\n \"pull_request_access_only\"\ + : false,\n \"pull_requests\": true\n },\n \"tags\": [\n \ + \ \"easyfix\",\n \"request-for-resources\",\n \"outage\"\ + ,\n \"lists\",\n \"authentication\"\n ],\n \"url_path\"\ + : \"fedora-infrastructure\",\n \"user\": {\n \"fullname\": \"\ + Kevin Fenzi\",\n \"name\": \"kevin\"\n }\n },\n {\n \ + \ \"access_groups\": {\n \"admin\": [],\n \"commit\": [],\n\ + \ \"ticket\": []\n },\n \"access_users\": {\n \"admin\"\ + : [\n \"abompard\",\n \"bowlofeggs\",\n \"kushal\"\ + ,\n \"maxamillion\",\n \"kevin\",\n \"puiterwijk\"\ + ,\n \"pingou\",\n \"sayanchowdhury\",\n \"ryanlerch\"\ + ,\n \"pfrields\"\n ],\n \"commit\": [\n \"\ + tflink\",\n \"kparal\"\n ],\n \"owner\": [\n \ + \ \"jcline\"\n ],\n \"ticket\": []\n },\n \"close_status\"\ + : [],\n \"custom_keys\": [],\n \"date_created\": \"1485549712\"\ + ,\n \"date_modified\": \"1502897027\",\n \"description\": \"The\ + \ Fedora Infrastructure Sphinx documentation project\",\n \"fullname\"\ + : \"infra-docs\",\n \"id\": 1658,\n \"milestones\": {},\n \"\ + name\": \"infra-docs\",\n \"namespace\": null,\n \"parent\": null,\n\ + \ \"priorities\": {},\n \"settings\": {\n \"Enforce_signed-off_commits_in_pull-request\"\ + : false,\n \"Minimum_score_to_merge_pull-request\": -1,\n \"\ + Only_assignee_can_merge_pull-request\": false,\n \"Web-hooks\": null,\n\ + \ \"always_merge\": false,\n \"fedmsg_notifications\": true,\n\ + \ \"issue_tracker\": true,\n \"issues_default_to_private\":\ + \ false,\n \"project_documentation\": true,\n \"pull_request_access_only\"\ + : false,\n \"pull_requests\": true\n },\n \"tags\": [\n \ + \ \"fedora-infra\",\n \"docs\",\n \"infrastructure\",\n\ + \ \"fedora\",\n \"documentation\"\n ],\n \"url_path\"\ + : \"infra-docs\",\n \"user\": {\n \"fullname\": \"Jeremy Cline\"\ + ,\n \"name\": \"jcline\"\n }\n },\n {\n \"access_groups\"\ + : {\n \"admin\": [],\n \"commit\": [],\n \"ticket\":\ + \ []\n },\n \"access_users\": {\n \"admin\": [],\n \ + \ \"commit\": [],\n \"owner\": [\n \"abompard\"\n \ + \ ],\n \"ticket\": []\n },\n \"close_status\": [\n \ + \ \"Invalid\",\n \"Insufficient data\",\n \"Fixed\",\n \ + \ \"Duplicate\"\n ],\n \"custom_keys\": [],\n \"date_created\"\ + : \"1467129373\",\n \"date_modified\": \"1467129373\",\n \"description\"\ + : \"A REST-based service to send karma to a Fedora user.\",\n \"fullname\"\ + : \"plus-plus-service\",\n \"id\": 807,\n \"milestones\": {},\n\ + \ \"name\": \"plus-plus-service\",\n \"namespace\": null,\n \ + \ \"parent\": null,\n \"priorities\": {},\n \"settings\": {\n \ + \ \"Enforce_signed-off_commits_in_pull-request\": false,\n \"\ + Minimum_score_to_merge_pull-request\": -1,\n \"Only_assignee_can_merge_pull-request\"\ + : false,\n \"Web-hooks\": null,\n \"always_merge\": false,\n\ + \ \"fedmsg_notifications\": true,\n \"issue_tracker\": true,\n\ + \ \"issues_default_to_private\": false,\n \"project_documentation\"\ + : false,\n \"pull_request_access_only\": false,\n \"pull_requests\"\ + : true\n },\n \"tags\": [],\n \"url_path\": \"plus-plus-service\"\ + ,\n \"user\": {\n \"fullname\": \"Aurelien Bompard\",\n \ + \ \"name\": \"abompard\"\n }\n }\n ],\n \"user\": {\n \"fullname\"\ + : \"Aurelien Bompard\",\n \"name\": \"abompard\"\n }\n}"} + headers: + connection: [Keep-Alive] + content-length: ['14727'] + content-type: [application/json] + date: ['Sun, 05 Nov 2017 11:23:45 GMT'] + keep-alive: ['timeout=5, max=100'] + referrer-policy: [same-origin] + server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 + Python/2.7.5] + set-cookie: ['pagure=eyJfcGVybWFuZW50Ijp0cnVlfQ.DOCGQg.zh-ell9n0Y9hxH-szbqIT8E9t_4; + Expires=Wed, 06-Dec-2017 11:23:46 GMT; Secure; HttpOnly; Path=/'] + strict-transport-security: [max-age=15768000; includeSubDomains; preload] + x-content-type-options: [nosniff] + x-frame-options: ['ALLOW FROM https://pagure.io/'] + x-xss-protection: [1; mode=block] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.18.4] + method: GET + uri: https://pagure.io/api/0/user/someone-that-does-not-exist + response: + body: {string: !!python/unicode "{\n \"error\": \"No such user found\",\n \"\ + error_code\": \"ENOUSER\"\n}"} + headers: + connection: [Keep-Alive] + content-length: ['62'] + content-type: [application/json] + date: ['Sun, 05 Nov 2017 11:23:48 GMT'] + keep-alive: ['timeout=5, max=100'] + referrer-policy: [same-origin] + server: [Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_wsgi/3.4 + Python/2.7.5] + set-cookie: ['pagure=eyJfcGVybWFuZW50Ijp0cnVlfQ.DOCGRA.owymPNg1OS1nm-kTzu84jjK9Vl8; + Expires=Wed, 06-Dec-2017 11:23:48 GMT; Secure; HttpOnly; Path=/'] + strict-transport-security: [max-age=15768000; includeSubDomains; preload] + x-content-type-options: [nosniff] + x-frame-options: ['ALLOW FROM https://pagure.io/'] + x-xss-protection: [1; mode=block] + status: {code: 404, message: NOT FOUND} +version: 1 diff --git a/hubs/tests/widgets/test_user_issues.py b/hubs/tests/widgets/test_user_issues.py new file mode 100644 index 0000000..ff7db31 --- /dev/null +++ b/hubs/tests/widgets/test_user_issues.py @@ -0,0 +1,115 @@ +from __future__ import unicode_literals + +from . import WidgetTest + + +class TestUserIssues(WidgetTest): + + plugin = "user_issues" + initial_widget_config = { + "bugzilla_user": "aurelien@bompard.org", + "github_user": "fedora-infra", + "pagure_user": "abompard", + } + + def populate(self): + super(TestUserIssues, self).populate() + self._add_widget_under_test() + + def _get_should_invalidate_result_bugzilla(self, msg): + func = self.widget.module.get_cached_functions()['GetBugzillaIssues'] + return func(self.widget).should_invalidate(msg) + + def test_should_invalidate_wrong_topic_bugzilla(self): + msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} + self.assertFalse(self._get_should_invalidate_result_bugzilla(msg)) + + def test_should_invalidate_good_match_bugzilla(self): + msg = { + 'topic': 'tests.bugzilla.bug.new', + 'msg': { + "bug": { + "creator": "aurelien@bompard.org", + }, + }, + } + self.assertTrue(self._get_should_invalidate_result_bugzilla(msg)) + + def test_should_invalidate_wrong_user_bugzilla(self): + msg = { + 'topic': 'tests.bugzilla.bug.new', + 'msg': { + "bug": { + "creator": "not-aurelien@bompard.org", + }, + }, + } + self.assertFalse(self._get_should_invalidate_result_bugzilla(msg)) + + def _get_should_invalidate_result_github(self, msg): + func = self.widget.module.get_cached_functions()['GetGithubIssues'] + return func(self.widget).should_invalidate(msg) + + def test_should_invalidate_wrong_topic_github(self): + msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} + self.assertFalse(self._get_should_invalidate_result_github(msg)) + + def test_should_invalidate_good_match_github(self): + msg = { + 'topic': 'tests.github.issue.opened', + 'msg': { + "issue": { + "user": { + "login": "fedora-infra", + }, + }, + }, + } + self.assertTrue(self._get_should_invalidate_result_github(msg)) + + def test_should_invalidate_wrong_user_github(self): + msg = { + 'topic': 'tests.github.issue.opened', + 'msg': { + "issue": { + "user": { + "login": "not-fedora-infra", + }, + }, + }, + } + self.assertFalse(self._get_should_invalidate_result_github(msg)) + + def _get_should_invalidate_result_pagure(self, msg): + func = self.widget.module.get_cached_functions()['GetPagureIssues'] + return func(self.widget).should_invalidate(msg) + + def test_should_invalidate_wrong_topic_pagure(self): + msg = {'topic': 'hubs.widget.update.WRONG.TOPIC'} + self.assertFalse(self._get_should_invalidate_result_pagure(msg)) + + def test_should_invalidate_good_match_pagure(self): + msg = { + 'topic': 'tests.pagure.issue.new', + 'msg': { + "issue": { + "user": { + "name": "abompard", + }, + }, + }, + } + self.assertTrue(self._get_should_invalidate_result_pagure(msg)) + + def test_should_invalidate_wrong_user_pagure(self): + msg = { + 'topic': 'tests.pagure.issue.new', + 'msg': { + "issue": { + "user": { + "name": "not-abompard", + }, + }, + }, + } + self.assertFalse(self._get_should_invalidate_result_pagure(msg)) diff --git a/hubs/widgets/user_issues/__init__.py b/hubs/widgets/user_issues/__init__.py new file mode 100644 index 0000000..e08aa95 --- /dev/null +++ b/hubs/widgets/user_issues/__init__.py @@ -0,0 +1,68 @@ +from __future__ import unicode_literals + +from hubs.widgets import validators +from hubs.widgets.base import Widget +from hubs.widgets.view import RootWidgetView + +from .functions import (GetPagureIssues, GetGithubIssues, GetBugzillaIssues, + combine) + +import logging + +log = logging.getLogger('hubs.widgets') + + +class UserIssues(Widget): + name = "user_issues" + label = "Tickets I Filed" + position = "right" + parameters = [ + dict( + name="bugzilla_user", + label="Bugzilla Email", + default="None", + validator=validators.BugzillaUser, + help="Bugzilla email to search issues for", + ), + dict( + name="github_user", + label="Github Username", + default="None", + validator=validators.GithubOrganization, + help="Github username to search issues for", + ), + dict( + name="pagure_user", + label="Pagure Username", + default="None", + validator=validators.PagureUser, + help="Pagure username to search issues for", + ), + ] + cached_functions_module = ".functions" + + def validate_parameters(self, values): + config = {} + if sum(username != "None" for source, username in values.items()) == 0: + raise ValueError("You should specify at least one of usernames") + + for param in self.get_parameters(): + if values[param.name] == "None": + config[param.name] = "None" + else: + config[param.name] = param.validator(values[param.name]) + return config + + +class BaseView(RootWidgetView): + + def get_context(self, instance, *args, **kwargs): + get_pagure_issues = GetPagureIssues(instance) + get_github_issues = GetGithubIssues(instance) + get_bugzilla_issues = GetBugzillaIssues(instance) + issues = combine(get_pagure_issues(), get_github_issues(), + get_bugzilla_issues()) + return dict( + display_number=min(10, len(issues)), + issues=issues, + ) diff --git a/hubs/widgets/user_issues/functions.py b/hubs/widgets/user_issues/functions.py new file mode 100644 index 0000000..620d99a --- /dev/null +++ b/hubs/widgets/user_issues/functions.py @@ -0,0 +1,135 @@ +from __future__ import unicode_literals, absolute_import +from operator import itemgetter + +import requests +import logging + +import arrow + +from hubs.widgets.caching import CachedFunction + + +log = logging.getLogger('hubs.widgets') + + +def combine(pagure, github, bugzilla): + ''' Data for user_issues widget + Combine pagure, github and bugzilla issues in one list + (sorted by last updated date)''' + # TODO: Possibly make this more versatile, allow different patterns for + # combining/filtering + + return sorted(github + bugzilla + pagure, + key=itemgetter("updated"), reverse=True) + + +class GetPagureIssues(CachedFunction): + ''' Data for user_issues widget + Queries Pagure api for issues ''' + + def execute(self): + # TODO: Wait until api allows to search issues created + # by user in all repos. + return [] + + def should_invalidate(self, message): + if ".pagure.issue." not in message["topic"]: + return False + try: + pagure_user = message["msg"]["issue"]["user"]["name"] + except KeyError: + return False + return pagure_user == self.instance.config["pagure_user"] + + +class GetGithubIssues(CachedFunction): + ''' Data for user_issues widget + Queries Github api for issues ''' + + github_date_format = "%Y-%m-%dT%H:%M:%SZ" + + def execute(self): + username = self.instance.config["github_user"] + if username == "None": + return [] + issues = [] + tmpl = ("https://api.github.com" + "/search/issues?q=author:{}&sort=updated") + url = tmpl.format(username) + result = requests.get(url).json() + if "items" not in result: + return [] + result = result["items"] + for issue in result: + if ("pull-request" not in issue and issue["state"] != "closed"): + issue_details = {} + issue_details["id"] = issue["id"] + issue_details["name"] = issue["title"] + issue_details["status"] = issue["state"] + issue_details["creator"] = issue["user"]["login"] + if "assignee" in issue: + issue_details["assignee"] = issue["assignee"] + issue_details["url"] = issue["html_url"] + issue_details["updated"] = \ + arrow.get(issue.get("updated_at")).timestamp + log.info(issue_details["updated"], issue["updated_at"]) + url = issue["repository_url"].split('/') + issue_details["repo"] = url[len(url)-1] + issue_details["from"] = "github.com" + issues.append(issue_details) + return issues + + def should_invalidate(self, message): + if ".github.issue." not in message["topic"]: + return False + try: + username = message["msg"]["issue"]["user"]["login"] + except KeyError: + return False + return username == self.instance.config["github_user"] + + +class GetBugzillaIssues(CachedFunction): + ''' Data for user_issues widget + Queries Bugzilla jsonrpc api for issues ''' + + def execute(self): + username = self.instance.config["bugzilla_user"] + if username == "None": + return [] + issues = [] + tmpl = ("https://bugzilla.redhat.com/jsonrpc.cgi?" + "method=Bug.search¶ms=[{{\"creator\":\"{}\"}}]") + url = tmpl.format(username) + result = requests.get(url).json() + if result["error"] is not None: + return [] + result = result["result"]["bugs"] + for bug in result: + if bug["status"] == "CLOSED": + continue + issue_details = {} + issue_details["id"] = bug["id"] + issue_details["name"] = bug["summary"] + issue_details["status"] = bug["status"] + issue_details["creator"] = bug["creator"] + if "assigned_to" in bug: + issue_details["assignee"] = bug["assigned_to"] + issue_details["updated"] = \ + arrow.get(bug["last_change_time"]).timestamp + issue_details["url"] = \ + ("https://bugzilla.redhat.com/" + "show_bug.cgi?id={id}").format(id=issue_details["id"]) + issue_details["repo"] = bug["product"] + issue_details["from"] = "bugzilla.redhat.com" + issues.append(issue_details) + return issues + + def should_invalidate(self, message): + if ".bugzilla.bug." not in message["topic"]: + return False + try: + username = message["msg"]["bug"]["creator"] + except KeyError: + return False + return username == self.instance.config["bugzilla_user"] diff --git a/hubs/widgets/user_issues/templates/root.html b/hubs/widgets/user_issues/templates/root.html new file mode 100644 index 0000000..180471a --- /dev/null +++ b/hubs/widgets/user_issues/templates/root.html @@ -0,0 +1,52 @@ + diff --git a/hubs/widgets/validators.py b/hubs/widgets/validators.py index ef19ad0..0401be4 100644 --- a/hubs/widgets/validators.py +++ b/hubs/widgets/validators.py @@ -116,3 +116,21 @@ def CommaSeparatedList(value): if not isinstance(value, list): raise ValueError("Expected a list") return value + + +def PagureUser(value): + url = "https://pagure.io/api/0/user/{}".format(value) + response = requests.get(url).json() + if "error" in response: + raise ValueError("Invalid Pagure username") + return value + + +def BugzillaUser(value): + tmpl = ("https://bugzilla.redhat.com/jsonrpc.cgi?" + "method=User.get¶ms=[{{\"names\":[\"{}\"]}}]") + url = tmpl.format(value) + response = requests.get(url).json() + if response["error"] is not None: + raise ValueError("Invalid Bugzilla email") + return value