#324 Improve the widget configuration validators
Merged 7 years ago by abompard. Opened 7 years ago by abompard.
abompard/fedora-hubs widget-config  into  develop

file modified
+7
@@ -27,6 +27,13 @@ 

     :members:

     :show-inheritance:

  

+ Widget parameter validators

+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^

+ 

+ .. automodule:: hubs.widgets.validators

+    :members:

+    :show-inheritance:

+ 

  Widget view

  ^^^^^^^^^^^

  

@@ -25,7 +25,7 @@ 

              <fieldset class="form-group ">

                <strong>{{ param.label | capitalize }}</strong>

                <input id="{{ param.name }}" class="form-control" type="text"

-                 value="{{ param.default if param.default else '' }}"

+                 value="{{ param.validator.to_string(param.default) }}"

                  name="{{ param.name }}" />

                {% if param.help %}

                  <small class="text-muted">{{ param.help }}</small>
@@ -40,7 +40,7 @@ 

          <button type="button" class="btn btn-default" data-dismiss="modal">

            Close

          </button>

-         <button type="submit" class="btn btn-default" id="submit_btn">

+         <button type="submit" class="btn btn-primary" id="submit_btn">

            Add

          </button>

        </div>

file modified
+2 -2
@@ -12,7 +12,7 @@ 

          <fieldset class="form-group ">

            <strong>{{ param.label | capitalize }}</strong>

            <input id="{{ param.name }}" class="form-control" type="text"

-               value="{{ widget.config.get(param.name, param.default if param.default else '') }}"

+               value="{{ param.validator.to_string(widget.config.get(param.name, param.default)) }}"

                name="{{ param.name }}" />

            {% if param.help %}

            <small class="text-muted">{{ param.help }}</small>
@@ -27,7 +27,7 @@ 

            Close

          </button>

          {% if widget.module.get_parameters() %}

-         <button type="submit" class="btn btn-default">

+         <button type="submit" class="btn btn-primary">

            Save

          </button>

          {% endif %}

@@ -246,8 +246,10 @@ 

          user = tests.FakeAuthorization('ralph')

          with tests.auth_set(app, user):

              data = {'widget_name': 'about'}

-             result = self.app.post('/ralph/add', data=data,

-                                    follow_redirects=False)

+             result = self.app.post('/ralph/add', data=data)

+             self.assertEqual(result.status_code, 302)

+             self.assertEqual(urlparse(result.location).path, "/ralph/edit")

+             result = self.app.get('/ralph/edit')

              self.assertEqual(result.status_code, 200)

              expected_str = '<h6 class="dropdown-header">Account Information</h6>'

              self.assertIn(expected_str, result.get_data(as_text=True))
@@ -260,6 +262,9 @@ 

              data = {'widget_name': 'about', 'text': 'text of widget'}

              result = self.app.post('/ralph/add', data=data,

                                     follow_redirects=False)

+             self.assertEqual(result.status_code, 302)

+             self.assertEqual(urlparse(result.location).path, "/ralph/edit")

+             result = self.app.get('/ralph/edit')

              self.assertEqual(result.status_code, 200)

              expected_str = '<h6 class="dropdown-header">Account Information</h6>'

              self.assertIn(expected_str, result.get_data(as_text=True))

@@ -0,0 +1,69 @@ 

+ from __future__ import unicode_literals

+ 

+ import six

+ import unittest

+ 

+ from hubs.widgets import validators

+ 

+ #from mock import Mock

+ from hubs.tests import APPTest

+ from hubs.models import User

+ 

+ 

+ class ValidatorsTest(APPTest):

+ 

+     def test_required(self):

+         self.assertRaises(ValueError, validators.Required.from_string, "")

+ 

+     def test_text(self):

+         self.assertEqual(validators.Text.from_string("\xe9"), "\xe9")

+ 

+     def test_integer(self):

+         self.assertEqual(validators.Integer.from_string("1"), 1)

+         self.assertRaises(ValueError, validators.Integer.from_string, "text")

+ 

+     @unittest.skip("Not implemented yet")

+     def test_link(self):

+         value = '<a href="somewhere">dummy</a>'

+         self.assertEqual(validators.Link.from_string(value), value)

+         self.assertRaises(ValueError, validators.Link.from_string, "text")

+ 

+     def test_username(self):

+         self.assertEqual(validators.Username.from_string("ralph"), "ralph")

+         self.assertRaises(ValueError, validators.Username.from_string, "nobody")

+ 

+     @unittest.skip("Not implemented yet")

+     def test_github_organization(self):

+         self.assertEqual(

+             validators.GithubOrganization.from_string("fedora-infra"),

+             "fedora-infra")

+         self.assertRaises(

+             ValueError,

+             validators.GithubOrganization.from_string,

+             "something-that-does-not-exist")

+ 

+     @unittest.skip("Not implemented yet")

+     def test_github_repo(self):

+         self.assertEqual(

+             validators.GithubRepo.from_string("fedmsg"), "fedmsg")

+         self.assertRaises(ValueError, validators.GithubRepo.from_string,

+                           "something-that-does-not-exist")

+ 

+     def test_fmncontext(self):

+         self.assertEqual(validators.FMNContext.from_string("email"), "email")

+         self.assertRaises(

+             ValueError, validators.FMNContext.from_string, "dummy")

+ 

+     def test_pagure_repo(self):

+         self.assertEqual(

+             validators.PagureRepo.from_string("fedora-hubs"), "fedora-hubs")

+         self.assertRaises(ValueError, validators.PagureRepo.from_string,

+                           "something-that-does-not-exist")

+ 

+     def test_fedorahosted_project(self):

+         self.assertEqual(

+             validators.FedorahostedProject.from_string("about-fedora"),

+             "about-fedora")

+         self.assertRaises(

+             ValueError, validators.FedorahostedProject.from_string,

+             "something-that-does-not-exist")

@@ -0,0 +1,186 @@ 

+ interactions:

+ - request:

+     body: null

+     headers:

+       Accept: ['*/*']

+       Accept-Encoding: ['gzip, deflate']

+       Connection: [keep-alive]

+       User-Agent: [python-requests/2.13.0]

+     method: GET

+     uri: https://fedorahosted.org/about-fedora/

+   response:

+     body: {string: "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"\

+         http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\"\

+         >\n  \n  \n\n  \n\n\n  <head>\n    <title>\n      about-fedora\n    </title>\n\

+         \    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"\

+         \ />\n    <!--[if IE]><script type=\"text/javascript\">\n      if (/^#__msie303:/.test(window.location.hash))\n\

+         \        window.location.replace(window.location.hash.replace(/^#__msie303:/,\

+         \ '#'));\n    </script><![endif]-->\n        <link rel=\"search\" href=\"\

+         /about-fedora/search\" />\n        <link rel=\"help\" href=\"/about-fedora/wiki/TracGuide\"\

+         \ />\n        <link rel=\"alternate\" href=\"/about-fedora/wiki/WikiStart?format=txt\"\

+         \ type=\"text/x-trac-wiki\" title=\"Plain Text\" />\n        <link rel=\"\

+         start\" href=\"/about-fedora/wiki\" />\n        <link rel=\"stylesheet\" href=\"\

+         /about-fedora/chrome/common/css/trac.css\" type=\"text/css\" /><link rel=\"\

+         stylesheet\" href=\"/about-fedora/chrome/common/css/wiki.css\" type=\"text/css\"\

+         \ /><link rel=\"stylesheet\" href=\"/about-fedora/chrome/tracvatar/tracvatar.css\"\

+         \ type=\"text/css\" />\n        <link rel=\"shortcut icon\" href=\"/about-fedora/chrome/common/trac.ico\"\

+         \ type=\"image/x-icon\" />\n        <link rel=\"icon\" href=\"/about-fedora/chrome/common/trac.ico\"\

+         \ type=\"image/x-icon\" />\n      <link type=\"application/opensearchdescription+xml\"\

+         \ rel=\"search\" href=\"/about-fedora/search/opensearch\" title=\"Search about-fedora\"\

+         \ />\n    <script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/jquery.js\"\

+         ></script><script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/babel.js\"\

+         ></script><script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/messages/en_US.js\"\

+         ></script><script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/trac.js\"\

+         ></script><script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/search.js\"\

+         ></script><script type=\"text/javascript\" src=\"/about-fedora/chrome/common/js/folding.js\"\

+         ></script>\n    <!--[if lt IE 7]>\n    <script type=\"text/javascript\" src=\"\

+         /about-fedora/chrome/common/js/ie_pre7_hacks.js\"></script>\n    <![endif]-->\n\

+         \    <script type=\"text/javascript\">\n      jQuery(document).ready(function($)\

+         \ {\n        $(\"#content\").find(\"h1,h2,h3,h4,h5,h6\").addAnchor(_(\"Link\

+         \ to this section\"));\n        $(\"#content\").find(\".wikianchor\").each(function()\

+         \ {\n          $(this).addAnchor(babel.format(_(\"Link to #%(id)s\"), {id:\

+         \ $(this).attr('id')}));\n        });\n        $(\".foldable\").enableFolding(true,\

+         \ true);\n      });\n    </script>\n  </head>\n  <body>\n    <div id=\"banner\"\

+         >\n      <div id=\"header\">\n        <a id=\"logo\" href=\"https://fedorahosted.org/\"\

+         ><img src=\"https://fedorahosted.org/web/static/images/fedora-hosted-banner.png\"\

+         \ alt=\"Fedora Hosted\" /></a>\n      </div>\n      <form id=\"search\" action=\"\

+         /about-fedora/search\" method=\"get\">\n        <div>\n          <label for=\"\

+         proj-search\">Search:</label>\n          <input type=\"text\" id=\"proj-search\"\

+         \ name=\"q\" size=\"18\" value=\"\" />\n          <input type=\"submit\" value=\"\

+         Search\" />\n        </div>\n      </form>\n      <div id=\"metanav\" class=\"\

+         nav\">\n    <ul>\n      <li class=\"first\"><a href=\"/about-fedora/openidlogin\"\

+         >OpenID Login</a></li><li><a href=\"https://admin.fedoraproject.org/accounts/user/new\"\

+         >Fedora Account Sign Up</a></li><li><a href=\"/about-fedora/prefs\">Preferences</a></li><li><a\

+         \ href=\"/about-fedora/wiki/TracGuide\">Help/Guide</a></li><li><a href=\"\

+         /about-fedora/about\">About Trac</a></li><li class=\"last\"><a href=\"http://git.fedorahosted.org/cgit/about-fedora.git\"\

+         >CGit</a></li>\n    </ul>\n  </div>\n    </div>\n    <div id=\"mainnav\" class=\"\

+         nav\">\n    <ul>\n      <li class=\"first active\"><a href=\"/about-fedora/wiki\"\

+         >Wiki</a></li><li><a href=\"/about-fedora/timeline\">Timeline</a></li><li><a\

+         \ href=\"/about-fedora/roadmap\">Roadmap</a></li><li><a href=\"/about-fedora/browser\"\

+         >Browse Source</a></li><li><a href=\"/about-fedora/report\">View Tickets</a></li><li\

+         \ class=\"last\"><a href=\"/about-fedora/search\">Search</a></li>\n    </ul>\n\

+         \  </div>\n    <div id=\"main\">\n      <div id=\"pagepath\" class=\"noprint\"\

+         >\n  <a class=\"pathentry first\" title=\"View WikiStart\" href=\"/about-fedora/wiki\"\

+         >wiki:</a><a class=\"pathentry\" href=\"/about-fedora/wiki/WikiStart\" title=\"\

+         View WikiStart\">WikiStart</a>\n</div>\n      <div id=\"ctxtnav\" class=\"\

+         nav\">\n        <h2>Context Navigation</h2>\n          <ul>\n            \

+         \  <li class=\"first\"><a href=\"/about-fedora/wiki/WikiStart\">Start Page</a></li><li><a\

+         \ href=\"/about-fedora/wiki/TitleIndex\">Index</a></li><li class=\"last\"\

+         ><a href=\"/about-fedora/wiki/WikiStart?action=history\">History</a></li>\n\

+         \          </ul>\n        <hr />\n      </div>\n    <div id=\"content\" class=\"\

+         wiki\">\n      <div class=\"wikipage searchable\">\n        \n          \n\

+         \          <div class=\"trac-modifiedby\">\n            <span><a href=\"/about-fedora/wiki/WikiStart?action=diff&amp;version=5\"\

+         \ title=\"Version 5 by quaid: adding step to self-intro\">Last modified</a>\

+         \ <a class=\"timeline\" href=\"/about-fedora/timeline?from=2009-02-09T03%3A57%3A48Z&amp;precision=second\"\

+         \ title=\"2009-02-09T03:57:48Z in Timeline\">8 years</a> ago</span>\n    \

+         \        <span class=\"trac-print\">Last modified on 02/09/09 03:57:48</span>\n\

+         \          </div>\n          <div id=\"wikipage\"><h1 id=\"Whatisthissite\"\

+         >What is this site?</h1>\n<p>\nThis is the source project for About Fedora.\

+         \  It includes a <tt>git</tt> repo and a database for tracking how we develop\

+         \ the source toolchain.  \n</p>\n<p>\nIt's all part of the <a class=\"ext-link\"\

+         \ href=\"https://fedoraproject.org/wiki/Overview\"><span class=\"icon\">\u200B\

+         </span>Fedora Project</a>.  This site, in particular, is run by the <a class=\"\

+         ext-link\" href=\"https://fedoraproject.org/wiki/DocsProject\"><span class=\"\

+         icon\">\u200B</span>Fedora Documentation team</a>.\n</p>\n<p>\nIf you want\

+         \ to look at what we've created, use the \"Browse Source\" link above and\

+         \ to the right.\n</p>\n<h1 id=\"AboutFedorahassomethingmissingorwrong.\">About\

+         \ Fedora has something missing or wrong.</h1>\n<p>\nIf you need to tell us\

+         \ that something is wrong in About Fedora, visit <a class=\"ext-link\" href=\"\

+         https://bugzilla.redhat.com/bugzilla\"><span class=\"icon\">\u200B</span>Bugzilla</a>.\

+         \  File a bug against the product \"Fedora Documentation,\" component \"about-fedora\"\

+         .\n</p>\n<h1 id=\"Iwanttohelpwiththecontent.\">I want to help with the content.</h1>\n\

+         <p>\nIf you want to help update the content of About Fedora, first <a class=\"\

+         ext-link\" href=\"http://fedoraproject.org/wiki/DocsProject/Join#Things_you_need_to_do\"\

+         ><span class=\"icon\">\u200B</span>introduce yourself to the Docs Project</a>.\

+         \  When you've done that, <a class=\"ext-link\" href=\"https://admin.fedoraproject.org/accounts/group/view/gitabout-fedora\"\

+         ><span class=\"icon\">\u200B</span>join our project group</a>.  Then visit\

+         \ <a class=\"ext-link\" href=\"https://fedoraproject.org/wiki/Docs_Project_work_using_git\"\

+         ><span class=\"icon\">\u200B</span>this page on the Fedora Project wiki</a>\

+         \ to find out how to get started using <tt>git</tt>.  These instructions should\

+         \ work for most people with access to this repository:\n</p>\n<pre class=\"\

+         wiki\">cd ~/projects/\ngit clone ssh://&lt;username&gt;@git.fedorahosted.org/git/docs/about-fedora.git\n\

+         git clone git://git.fedorahosted.org/git/fedora-doc-utils docs-common\n</pre><p>\n\

+         If you don't have access to this repo, use <tt>git://</tt> instead of <tt>ssh://</tt>\

+         \ above.\n</p>\n<h1 id=\"Iwanttotranslatethecontenttomylanguage.\">I want\

+         \ to translate the content to my language.</h1>\n<p>\nCheck the <a class=\"\

+         ext-link\" href=\"http://translate.fedoraproject.org/module/docs-about-fedora\"\

+         ><span class=\"icon\">\u200B</span>statistics page</a> to get details about\

+         \ your language and join the <a class=\"ext-link\" href=\"https://fedoraproject.org/wiki/L10N\"\

+         ><span class=\"icon\">\u200B</span>Fedora Translation team</a> if your language\

+         \ is not available.\n</p>\n<h1 id=\"Imconfused.Whatisthissitefor\">I'm confused.\

+         \ What is this site for?</h1>\n<p>\nIf you have questions, come visit the\

+         \ Docs Project on IRC.  IRC is a way to communicate in real time with other\

+         \ Fedora Project members.  You can find more information <a class=\"ext-link\"\

+         \ href=\"https://fedoraproject.org/wiki/Communicate/IRC\"><span class=\"icon\"\

+         >\u200B</span>here</a>.\n</p>\n</div>\n        \n        \n      </div>\n\

+         \      \n\n    </div>\n    <div id=\"altlinks\">\n      <h3>Download in other\

+         \ formats:</h3>\n      <ul>\n        <li class=\"last first\">\n         \

+         \ <a rel=\"nofollow\" href=\"/about-fedora/wiki/WikiStart?format=txt\">Plain\

+         \ Text</a>\n        </li>\n      </ul>\n    </div>\n    </div>\n    <div id=\"\

+         footer\" lang=\"en\" xml:lang=\"en\"><hr />\n      <a id=\"tracpowered\" href=\"\

+         http://trac.edgewall.org/\"><img src=\"/about-fedora/chrome/common/trac_logo_mini.png\"\

+         \ height=\"30\" width=\"107\" alt=\"Trac Powered\" /></a>\n      <p class=\"\

+         left\">Powered by <a href=\"/about-fedora/about\"><strong>Trac 0.12.5</strong></a><br\

+         \ />\n        By <a href=\"http://www.edgewall.org/\">Edgewall Software</a>.<p\

+         \ class=\"left\">Libravatar support by <a href=\"https://bitbucket.org/zzzeek/tracvatar\"\

+         >Tracvatar 1.9</a></p></p>\n      <p class=\"right\">Visit the Trac open source\

+         \ project at<br /><a href=\"http://trac.edgewall.org/\">http://trac.edgewall.org/</a></p>\n\

+         \    </div>\n    <!-- Tack on our fedmenu stuff for this trac. -->\n    <script\

+         \ src=\"https://apps.fedoraproject.org/fedmenu/js/jquery-1.11.2.min.js\"></script>\n\

+         \    <script src=\"https://apps.fedoraproject.org/fedmenu/js/fedmenu.js\"\

+         ></script>\n    <script>\n      fedmenu({\n        'url': 'https://apps.fedoraproject.org/js/data.js',\n\

+         \        'mimeType': 'application/javascript',\n        'position': 'bottom-right',\n\

+         \      });\n    </script>\n  </body>\n</html>"}

+     headers:

+       cache-control: [must-revalidate]

+       connection: [Keep-Alive]

+       content-length: ['9437']

+       content-type: [text/html;charset=utf-8]

+       date: ['Tue, 14 Feb 2017 10:57:38 GMT']

+       expires: ['Fri, 01 Jan 1999 00:00:00 GMT']

+       keep-alive: ['timeout=5, max=500']

+       server: [Apache/2.2.15 (Red Hat)]

+       set-cookie: [trac_form_token=8344c426dbc2cc320d5e431d; Path=/about-fedora, 'trac_session=808a3fc5547b36ddaca5f057;

+           expires=Mon, 15-May-2017 10:57:39 GMT; Path=/about-fedora']

+       strict-transport-security: [max-age=15768000; includeSubDomains; preload]

+     status: {code: 200, message: Ok}

+ - request:

+     body: null

+     headers:

+       Accept: ['*/*']

+       Accept-Encoding: ['gzip, deflate']

+       Connection: [keep-alive]

+       User-Agent: [python-requests/2.13.0]

+     method: GET

+     uri: https://fedorahosted.org/something-that-does-not-exist/

+   response:

+     body: {string: !!python/unicode '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

+ 

+         <html><head>

+ 

+         <title>302 Found</title>

+ 

+         </head><body>

+ 

+         <h1>Found</h1>

+ 

+         <p>The document has moved <a href="https://fedorahosted.org/web/410">here</a>.</p>

+ 

+         <hr>

+ 

+         <address>Apache/2.2.15 (Red Hat) Server at fedorahosted.org Port 443</address>

+ 

+         </body></html>

+ 

+         '}

+     headers:

+       connection: [Keep-Alive]

+       content-length: ['300']

+       content-type: [text/html; charset=iso-8859-1]

+       date: ['Tue, 14 Feb 2017 10:57:40 GMT']

+       keep-alive: ['timeout=5, max=500']

+       location: ['https://fedorahosted.org/web/410']

+       server: [Apache/2.2.15 (Red Hat)]

+       strict-transport-security: [max-age=15768000; includeSubDomains; preload]

+     status: {code: 302, message: Found}

+ version: 1

@@ -0,0 +1,351 @@ 

+ interactions:

+ - request:

+     body: null

+     headers:

+       Accept: ['*/*']

+       Accept-Encoding: ['gzip, deflate']

+       Connection: [keep-alive]

+       User-Agent: [python-requests/2.13.0]

+     method: GET

+     uri: https://pagure.io/fedora-hubs

+   response:

+     body: {string: "<!DOCTYPE html>\n<html lang='en'>\n<head>\n    <meta http-equiv=\"\

+         Content-Type\" content=\"text/html; charset=UTF-8\"/>\n    <title>Overview\

+         \ - fedora-hubs - Pagure</title>\n    <link rel=\"shortcut icon\" type=\"\

+         image/vnd.microsoft.icon\"\n        href=\"/static/favicon.ico\"/>\n    <link\

+         \ href=\"https://apps.fedoraproject.org/global/fedora-bootstrap-1.0.1/fedora-bootstrap.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/pagure.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/open-iconic/css/open-iconic.min.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/fonts/fonts.css\"\

+         \n        rel=\"stylesheet\" type=\"text/css\" />\n    <link href=\"/static/hack_fonts/css/hack-extended.min.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <meta name=\"go-import\"\

+         \ content=\"pagure.io/fedora-hubs git https://pagure.io/fedora-hubs.git\"\

+         >\n  </head>\n  <body id=\"home\">\n  <!-- start masthead -->\n    <div class=\"\

+         masthead\">\n      <div class=\"container\">\n        <div class=\"row\">\n\

+         \          <div class=\"col-sm-3\">\n            <a href=\"/\">\n        \

+         \      <img height=40px src=\"/static/pagure-logo.png\"\n                alt=\"\

+         pagure Logo\" id=\"pagureLogo\"/>\n            </a>\n          </div>\n  \

+         \        <div class=\"col-sm-9\">\n            <div class=\"row\">\n\n  <nav\

+         \ class=\"navbar navbar-light p-t-0 p-b-0\">\n      <div class=\"container\"\

+         >\n        <ul class=\"nav navbar-nav nav-underline pull-xs-right\">\n   \

+         \           <li class=\"nav-item p-l-1\">\n                <a class=\"nav-link\

+         \ btn btn-primary\" href=\"/login/?next=https://pagure.io/fedora-hubs\">Log\

+         \ In</a>\n              </li>\n        </ul>\n      </div>\n    </nav>\n\n\

+         \            </div>\n          </div>\n        </div>\n      </div>\n    </div><!--\

+         \ close masthead-->\n\n    <div class=\"bodycontent p-b-3\">\n\n\n<div class=\"\

+         repo-header p-t-1\">\n  <div class=\"container\">\n    <header>\n      <h2\

+         \ class=\"repo-name m-b-0\">\n        <a href=\"/fedora-hubs\">\nfedora-hubs</a>\n\

+         \        </h2>\n        <div class=\"projectinfo m-t-1 m-b-1\">\nFedora Hubs\

+         \ &nbsp;| &nbsp;<a class=\"inline\" href=\"https://hubs-dev.fedorainfracloud.org/\"\

+         >https://hubs-dev.fedorainfracloud.org/</a>        </div>\n\n    </header>\n\

+         \    <ul class=\"nav nav-tabs nav-small\">\n      <li class=\"nav-item\">\n\

+         \        <a class=\"nav-link active\" href=\"/fedora-hubs\">\n           \

+         \ <span class=\"oi hidden-md-up\" data-glyph=\"home\"></span>\n          \

+         \  <span class=\"hidden-sm-down\">Overview</span>\n        </a>\n      </li>\n\

+         \        <li class=\"nav-item\" >\n          <a  class=\"nav-link\" href=\"\

+         /docs/fedora-hubs/\">\n              <span class=\"oi hidden-md-up\" data-glyph=\"\

+         book\"></span>\n              <span class=\"hidden-sm-down\">Docs</span>\n\

+         \          </a>\n        </li>\n\n        <li class=\"nav-item\">\n      \

+         \    <a class=\"nav-link\" href=\"/fedora-hubs/commits/develop\">\n      \

+         \        <span class=\"oi hidden-md-up\" data-glyph=\"spreadsheet\">\n   \

+         \           </span><span class=\"hidden-sm-down\">Commits</span>\n       \

+         \     </a>\n        </li>\n\n        <li class=\"nav-item\">\n          <a\

+         \ class=\"nav-link\" href=\"/fedora-hubs/tree/develop\">\n              <span\

+         \ class=\"oi hidden-md-up\" data-glyph=\"file\"></span>\n              <span\

+         \ class=\"hidden-sm-down\">Files</span>\n          </a>\n        </li>\n\n\

+         \        <li class=\"nav-item\">\n          <a class=\"nav-link\" href=\"\

+         /fedora-hubs/releases\">\n              <span class=\"oi hidden-md-up\" data-glyph=\"\

+         box\"></span>\n              <span class=\"hidden-sm-down\">Releases</span>\n\

+         \          </a>\n        </li>\n\n        <li class=\"nav-item\">\n      \

+         \      <a class=\"nav-link\" href=\"/fedora-hubs/issues\">\n             \

+         \   <span class=\"oi hidden-md-up\" data-glyph=\"warning\"></span>\n     \

+         \           <span class=\"hidden-sm-down\">Issues&nbsp;</span>\n         \

+         \       <span class=\"label label-default label-pill hidden-sm-down\">\n \

+         \                 111\n                </span>\n            </a>\n       \

+         \ </li>\n\n        <li class=\"nav-item\">\n          <a class=\"nav-link\"\

+         \ href=\"/fedora-hubs/pull-requests\">\n              <span class=\"oi hidden-md-up\"\

+         \ data-glyph=\"task\"></span>\n              <span class=\"hidden-sm-down\"\

+         >Pull Requests&nbsp;</span>\n              <span class=\"label label-default\

+         \ label-pill hidden-sm-down\">\n                3\n              </span>\n\

+         \          </a>\n        </li>\n\n        <li class=\"nav-item\">\n      \

+         \    <a class=\"nav-link\" href=\"/fedora-hubs/forks\">\n              <span\

+         \ class=\"oi hidden-md-up\" data-glyph=\"fork\"></span>\n              <span\

+         \ class=\"hidden-sm-down\">Forks&nbsp;</span>\n              <span class=\"\

+         hidden-sm-down label label-default label-pill\">\n                50\n   \

+         \           </span>\n            </a>\n        </li>\n\n\n    </ul>\n  </div>\n\

+         </div>\n\n<div class=\"container p-t-3\">\n  <div class=\"row\">\n       \

+         \ <div class=\"col-md-8\">\n            <section class=\"readme\">\n     \

+         \           <div class=\"document\">\n<h1>Fedora Hubs</h1>\n<p>Fedora Hubs\

+         \ will provide a communication and collaboration center for Fedora\ncontributors\

+         \ of all types. The idea is that contributors will be able to visit\nHubs\

+         \ to check on their involvements across Fedora, discover new places that they\n\

+         can contribute, and more.</p>\n<p>Hubs is currently under development, and\

+         \ you can see the progress on the\nDevelopment instance here: <a href=\"https://hubs-dev.fedorainfracloud.org/\"\

+         >https://hubs-dev.fedorainfracloud.org/</a></p>\n<div class=\"section\">\n\

+         <h1>Get Involved</h1>\n<p>Visit our <a href=\"https://lists.fedoraproject.org/archives/list/hubs-devel@lists.fedoraproject.org\"\

+         >mailing list</a>\nand join us in the <code><span class=\"pre\">#fedora-hubs</span></code>\

+         \ IRC channel on irc.freenode.net. Meetings are held\nweekly in <code><span\

+         \ class=\"pre\">#fedora-hubs</span></code> at 14:00UTC and the minutes for\

+         \ every meeting are\n<a href=\"https://meetbot.fedoraproject.org/sresults/?group_id=fedora-hubs&amp;type=team\"\

+         >archived</a>.\nIn the meetings we review our statuses from the preceding\

+         \ week and do ticket triage, too.</p>\n<p>For a more detailed overview of\

+         \ what Fedora Hubs is, see the\n<a href=\"https://docs.pagure.org/fedora-hubs/overview.html\"\

+         >documentation</a>.</p>\n<p>To set up a development environment and start\

+         \ contributing, check out\nthe <a href=\"https://docs.pagure.org/fedora-hubs/dev-guide.html\"\

+         >development guide</a>.</p>\n</div>\n</div>\n\n            </section>\n  \

+         \      </div>\n      <div class=\"col-md-4\">\n        <div class=\"card\"\

+         >\n          <div class=\"card-block\">\n            <h5><strong>Owners</strong></h5>\n\

+         \            <div class=\"m-b-2\">\n              <div>\n                <a\

+         \ href=\"/user/decause\">\n                  <img class=\"avatar circle\"\

+         \ src=\"https://seccdn.libravatar.org/avatar/2107fccec2d30c09bcc3f4141388b10aabf16b2a9664f16cdf7227295f28e89b?s=20&d=retro\"\

+         />\n                  Remy DeCausemaker (decause)  -  creator\n          \

+         \      </a>\n              </div>\n              <div>\n                <a\

+         \ href=\"/user/mrichard\">\n                  <img class=\"avatar circle\"\

+         \ src=\"https://seccdn.libravatar.org/avatar/d64b6dc2a878d19f600cb711e02d8547fc94589f8633452f84da1cb961d2b892?s=20&d=retro\"\

+         />\n                  Meghan Richardson (mrichard)\n                </a>\n\

+         \              </div>\n              <div>\n                <a href=\"/user/duffy\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/e4e83ac110d58771f0c0125651a1ab585e9f72715dc76bd4af54e0102e96f171?s=20&d=retro\"\

+         />\n                  M\xE1ir\xEDn Duffy (duffy)\n                </a>\n \

+         \             </div>\n              <div>\n                <a href=\"/user/lmacken\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/d433d26a9f50eb87d94b0473472202aa7cf7952700ae82fc5f9750adc0aad37e?s=20&d=retro\"\

+         />\n                  Luke Macken (lmacken)\n                </a>\n      \

+         \        </div>\n              <div>\n                <a href=\"/user/nyazdani\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/40c9ac361c6cf560171f6bfb99eef9c6849bf8156e2faa15b067e4c4d092246b?s=20&d=retro\"\

+         />\n                  Nathaniel Yazdani (nyazdani)\n                </a>\n\

+         \              </div>\n              <div>\n                <a href=\"/user/bee2502\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/0b4ce1166336c24dae92f4ece34d4b6a2c7218dd85088967e2f12c6e7b09d5ce?s=20&d=retro\"\

+         />\n                  Bhagyashree Padalkar (bee2502)\n                </a>\n\

+         \              </div>\n              <div>\n                <a href=\"/user/devyani7\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/cbfbb8a5d78cfc3a595d65dd26074e5b5799bb2b585311dcc0dd18230e2f5649?s=20&d=retro\"\

+         />\n                  Devyani Kota (devyani7)\n                </a>\n    \

+         \          </div>\n              <div>\n                <a href=\"/user/dhrish20\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/156c08ff565412423d5bdb7a1d46e9ea0a3f0cf8f9b51b4f6cd0c6da56a72da9?s=20&d=retro\"\

+         />\n                  Dhriti Shikhar (dhrish20)\n                </a>\n  \

+         \            </div>\n              <div>\n                <a href=\"/user/jflory7\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/bc8fd31501030af99c3f31ca8a1f4ee205a1d0035b487d09a497a194b8777c70?s=20&d=retro\"\

+         />\n                  Justin W. Flory (jflory7)\n                </a>\n  \

+         \            </div>\n              <div>\n                <a href=\"/user/pingou\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/b3ee7bb4de70b6522c2478df3b4cd6322b5ec5d62ac7ceb1128e3d4ff42f6928?s=20&d=retro\"\

+         />\n                  Pierre-YvesChibon (pingou)\n                </a>\n \

+         \             </div>\n              <div>\n                <a href=\"/user/ryanlerch\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/de5bf8d06663adb3bb1b8d49ccab259828fad7dddeb233b073d0c447d79b4c14?s=20&d=retro\"\

+         />\n                  Ryan Lerch (ryanlerch)\n                </a>\n     \

+         \         </div>\n              <div>\n                <a href=\"/user/skrzepto\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/55ffbcbe7ef5ef02b58c2082b79aa1dc23794da806e19333d4dbb994c4093b3d?s=20&d=retro\"\

+         />\n                  Szymon Mucha (skrzepto)\n                </a>\n    \

+         \          </div>\n              <div>\n                <a href=\"/user/sayanchowdhury\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/4771836b6b430740e853f097f7121782109560ec6099e72ffc923ed17f9f88bd?s=20&d=retro\"\

+         />\n                  Sayan Chowdhury (sayanchowdhury)\n                </a>\n\

+         \              </div>\n              <div>\n                <a href=\"/user/bkorren\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/632177fa944babf9d34ebdc5fb82a4b2ea3ca7c1da7ef9657ebd6f3f48e3c094?s=20&d=retro\"\

+         />\n                  bkorren (bkorren)\n                </a>\n          \

+         \    </div>\n              <div>\n                <a href=\"/user/atelic\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/72118562545c3cad698b51ca813fd0149dd19f63d7dceec161d447b834b03a45?s=20&d=retro\"\

+         />\n                  Eric Barbour (atelic)\n                </a>\n      \

+         \        </div>\n              <div>\n                <a href=\"/user/wispfox\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/b5f1b3792dc945fba3b616b489aa688b91577f186283427b4f08dd9dcbe53db7?s=20&d=retro\"\

+         />\n                  Suzanne Hillman (wispfox)\n                </a>\n  \

+         \            </div>\n              <div>\n                <a href=\"/user/jcline\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/1a108f46a960aa35efcabd0b779cd59b8c3aba2927213fe63373921e1aa3fac4?s=20&d=retro\"\

+         />\n                  Jeremy Cline (jcline)\n                </a>\n      \

+         \        </div>\n              <div>\n                <a href=\"/user/abompard\"\

+         >\n                  <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/224e9ad3084cb9f442f423b7910701377be7a018408a4f8b22a09d94c89af46f?s=20&d=retro\"\

+         />\n                  Aurelien Bompard (abompard)\n                </a>\n\

+         \              </div>\n            </div>\n            <h5><strong>Branches</strong></h5>\n\

+         \            <div class=\"m-b-2\">\n              <div id=\"branch-develop\"\

+         \ class=\"repoinfo-branchlistitem row\">\n                <div class=\"branch_name\

+         \ col-md-10\">\n                  <span class=\"oi text-muted\" data-glyph=\"\

+         random\"\n                    title=\"Default branch\"></span>\n         \

+         \         <strong title=\"Currently viewing branch develop\" data-toggle=\"\

+         tooltip\">develop</strong>\n                </div>\n                <div class=\"\

+         branch_del col-md-2\">\n                  <span class=\"oi text-muted\" data-glyph=\"\

+         star\"\n                    title=\"Default branch\"></span>\n           \

+         \     </div>\n              </div>\n\n              <div id=\"branch-feature__bugzilla-links\"\

+         \ class=\"repoinfo-branchlistitem row\">\n                <div class=\"branch_name\

+         \ col-md-8\">\n                  <span class=\"oi text-muted\" data-glyph=\"\

+         random\"></span>\n                  <a class=\"\" href=\"/fedora-hubs/branch/feature/bugzilla-links\"\

+         \n                    title=\"feature/bugzilla-links\" data-toggle=\"tooltip\"\

+         >feature/bugzilla-links\n                  </a>\n\n                </div>\n\

+         \                <div class=\"branch_del col-md-4\">\n                </div>\n\

+         \              </div>\n              <div id=\"branch-fix_fedmsgstats\" class=\"\

+         repoinfo-branchlistitem row\">\n                <div class=\"branch_name col-md-8\"\

+         >\n                  <span class=\"oi text-muted\" data-glyph=\"random\"></span>\n\

+         \                  <a class=\"\" href=\"/fedora-hubs/branch/fix_fedmsgstats\"\

+         \n                    title=\"fix_fedmsgstats\" data-toggle=\"tooltip\">fix_fedmsgstats\n\

+         \                  </a>\n\n                </div>\n                <div class=\"\

+         branch_del col-md-4\">\n                </div>\n              </div>\n   \

+         \           <div id=\"branch-jenkins\" class=\"repoinfo-branchlistitem row\"\

+         >\n                <div class=\"branch_name col-md-8\">\n                \

+         \  <span class=\"oi text-muted\" data-glyph=\"random\"></span>\n         \

+         \         <a class=\"\" href=\"/fedora-hubs/branch/jenkins\"\n           \

+         \         title=\"jenkins\" data-toggle=\"tooltip\">jenkins\n            \

+         \      </a>\n\n                </div>\n                <div class=\"branch_del\

+         \ col-md-4\">\n                </div>\n              </div>\n            \

+         \  <div id=\"branch-master\" class=\"repoinfo-branchlistitem row\">\n    \

+         \            <div class=\"branch_name col-md-8\">\n                  <span\

+         \ class=\"oi text-muted\" data-glyph=\"random\"></span>\n                \

+         \  <a class=\"\" href=\"/fedora-hubs/branch/master\"\n                   \

+         \ title=\"master\" data-toggle=\"tooltip\">master\n                  </a>\n\

+         \n                </div>\n                <div class=\"branch_del col-md-4\"\

+         >\n                </div>\n              </div>\n              <div id=\"\

+         branch-unittest\" class=\"repoinfo-branchlistitem row\">\n               \

+         \ <div class=\"branch_name col-md-8\">\n                  <span class=\"oi\

+         \ text-muted\" data-glyph=\"random\"></span>\n                  <a class=\"\

+         \" href=\"/fedora-hubs/branch/unittest\"\n                    title=\"unittest\"\

+         \ data-toggle=\"tooltip\">unittest\n                  </a>\n\n           \

+         \     </div>\n                <div class=\"branch_del col-md-4\">\n      \

+         \          </div>\n              </div>\n            </div>\n            <h5><strong>Source\

+         \ GIT URLs</strong>                  <span class=\"pull-xs-right\"><a data-toggle=\"\

+         collapse\" href=\"#moregiturls\"\n                  aria-expanded=\"false\"\

+         \ aria-controls=\"moregiturls\"\n                  id=\"more_gits\">more</a></span></h5>\n\

+         \            <div>\n              <div class=\"form-group\">\n           \

+         \     <div class=\"input-group input-group-sm\">\n                  <div class=\"\

+         input-group-addon\">SSH</div>\n                  <input class=\"form-control\"\

+         \ type=\"text\" value=\"ssh://git@pagure.io/fedora-hubs.git\" readonly>\n\

+         \                </div>\n              </div>\n              <div class=\"\

+         form-group\">\n                <div class=\"input-group input-group-sm\">\n\

+         \                  <div class=\"input-group-addon\">GIT</div>\n          \

+         \        <input class=\"form-control\" type=\"text\" value=\"https://pagure.io/fedora-hubs.git\"\

+         \ readonly>\n                </div>\n              </div>\n              <div\

+         \ class=\"collapse\" id=\"moregiturls\">\n                  <h5><strong>Docs\

+         \ GIT URLs</strong></h5>\n                  <div class=\"form-group\">\n \

+         \                   <div class=\"input-group input-group-sm\">\n         \

+         \             <div class=\"input-group-addon\">GIT</div>\n               \

+         \       <input class=\"form-control\" type=\"text\" value=\"https://pagure.io/docs/fedora-hubs.git\"\

+         \ readonly>\n                    </div>\n                  </div>\n      \

+         \        </div>\n            </div>\n          </div>\n          <div class=\"\

+         repo_date\" title=\"2015-06-04 17:27:48.074785\">\n              created 2\

+         \ years ago\n          </div>\n        </div>\n\n          <div class=\"card\"\

+         >\n            <div class=\"card-header\">\n              <strong>Recent Commits\

+         \ in\n                <span class=\"pr-toplabel\">\n                  <span\

+         \ class=\"oi\" data-glyph=\"random\"></span>\n                  develop\n\

+         \                </span>\n              </strong>\n            </div>\n  \

+         \          <div class=\"card-block p-a-0\">\n              <div class=\"list-group\

+         \ list-group-flush\">\n                  <a href=\"/fedora-hubs/c/71893f00e2afa9a2b8c3c2e2b417effabfa8a97e\"\

+         \ class=\"list-group-item\">\n                    <div class=\"commitdate\"\

+         \ title=\"Feb 10 2017 15:10:50\">\n                      <small>\n       \

+         \                 <strong>\n                          <img class=\"avatar\

+         \ circle\" src=\"https://seccdn.libravatar.org/avatar/224e9ad3084cb9f442f423b7910701377be7a018408a4f8b22a09d94c89af46f?s=20&d=retro\"\

+         />\n                          Aur\xE9lien Bompard\n                      \

+         \  </strong>\n                        commited 3 days ago\n              \

+         \        </small>\n                    </div>\n                    <div><small>\n\

+         \                      Sort the widget registry by name\n                \

+         \    </small></div>\n                  </a>\n                  <a href=\"\

+         /fedora-hubs/c/d6eb8815e9c4e7d6e60a924113b13095c5ccbe7e\" class=\"list-group-item\"\

+         >\n                    <div class=\"commitdate\" title=\"Feb 10 2017 13:56:13\"\

+         >\n                      <small>\n                        <strong>\n     \

+         \                     <img class=\"avatar circle\" src=\"https://seccdn.libravatar.org/avatar/224e9ad3084cb9f442f423b7910701377be7a018408a4f8b22a09d94c89af46f?s=20&d=retro\"\

+         />\n                          Aur\xE9lien Bompard\n                      \

+         \  </strong>\n                        commited 3 days ago\n              \

+         \        </small>\n                    </div>\n                    <div><small>\n\

+         \                      Fix the help text in the widgets config template\n\

+         \                    </small></div>\n                  </a>\n            \

+         \      <a href=\"/fedora-hubs/c/5e3de90c7d948bb82e4651e9d5b73a63472fa9e9\"\

+         \ class=\"list-group-item\">\n                    <div class=\"commitdate\"\

+         \ title=\"Feb 10 2017 10:27:24\">\n                      <small>\n       \

+         \                 <strong>\n                          <img class=\"avatar\

+         \ circle\" src=\"https://seccdn.libravatar.org/avatar/224e9ad3084cb9f442f423b7910701377be7a018408a4f8b22a09d94c89af46f?s=20&d=retro\"\

+         />\n                          Aur\xE9lien Bompard\n                      \

+         \  </strong>\n                        commited 4 days ago\n              \

+         \        </small>\n                    </div>\n                    <div><small>\n\

+         \                      Make CachedFunction.should_invalidate() abstract\n\

+         \                    </small></div>\n                  </a>\n            \

+         \  </div>\n              </div>\n            </div>\n          </div>\n  \

+         \    </div>\n    </div>\n\n\n</div>\n    </div>\n\n    <div class=\"footer\

+         \ p-t-1 p-b-1\">\n        <div class=\"container\">\n            <p class=\"\

+         text-muted credit\">\n         Copyright &copy; 2014-2017 Red Hat\n      \

+         \    <a href=\"https://pagure.io/pagure\">pagure</a> &mdash;\n          2.12.1\

+         \ &mdash; <a href=\"https://docs.pagure.org/pagure/usage.html\">Documentation</a>\n\

+         \            </p>\n            <p><a href=\"/ssh_info\">SSH Hostkey/Fingerprint</a></p>\n\

+         \        </div>\n    </div>\n\n    <script type=\"text/javascript\"\n    \

+         \    src=\"/static/jquery.min.js\">\n    </script>\n    <script type=\"text/javascript\"\

+         \n        src=\"/static/jquery-ui.min.js\">\n    </script>\n    <script type=\"\

+         text/javascript\"\n        src=\"https://apps.fedoraproject.org/global/fedora-bootstrap-1.0.1/fedora-bootstrap.js\"\

+         >\n    </script>\n    <script type=\"text/javascript\">\n$('[data-toggle=\"\

+         tooltip\"]').tooltip({placement : 'bottom'});\n    </script>\n\n<script type=\"\

+         text/javascript\">\n$(document).ready(function() {\n  $(\".watch-menu a\"\

+         ).click(function(){\n    var selectedValue = $(this).attr('id');\n    var\

+         \ action = $(\"#watch_project\").attr('action');\n      if (selectedValue\

+         \ != \"unwatch_button\") {\n        action = action.replace('/settings/0',\

+         \ '/settings/1');\n        $('#watch_project').attr('action', action);\n \

+         \     }\n      $('#watch_project').submit();\n\n  });\n});\n</script>\n\n\

+         <script type=\"text/javascript\">\n$(document).ready(function() {\n    $(\"\

+         .extra_gits\").hide();\n});\n\n$(function() {\n  $( \"#more_gits\" ).click(function()\

+         \ {\n      if ($( \"#more_gits\" ).html() == 'more') {\n        $( \"#more_gits\"\

+         \ ).html('less');\n      } else {\n        $( \"#more_gits\" ).html('more');\n\

+         \      }\n  });\n\n});\n</script>\n\n\n</body>\n</html>"}

+     headers:

+       connection: [Keep-Alive]

+       content-length: ['21185']

+       content-type: [text/html; charset=utf-8]

+       date: ['Tue, 14 Feb 2017 10:48:02 GMT']

+       keep-alive: ['timeout=5, max=100']

+       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=eyJfcGVybWFuZW50Ijp0cnVlLCJjc3JmIjp7IiBiIjoiTTJSallqTmlaak16T1ROaE4yWTNOekZpWkdNeFptRTVZek13Wm1FM01ETTBaV1E0WlRFMU1BPT0ifX0.C4Rx4g.oLbGmDy2SdBOneLnLMwKWvCQ5WY;

+           Expires=Fri, 17-Mar-2017 10:48:02 GMT; Secure; HttpOnly; Path=/']

+       strict-transport-security: [max-age=15768000; includeSubDomains; preload]

+     status: {code: 200, message: OK}

+ - request:

+     body: null

+     headers:

+       Accept: ['*/*']

+       Accept-Encoding: ['gzip, deflate']

+       Connection: [keep-alive]

+       User-Agent: [python-requests/2.13.0]

+     method: GET

+     uri: https://pagure.io/something-that-does-not-exist

+   response:

+     body: {string: !!python/unicode "<!DOCTYPE html>\n<html lang='en'>\n<head>\n \

+         \   <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"\

+         />\n    <title>Page not found :'( - Pagure</title>\n    <link rel=\"shortcut\

+         \ icon\" type=\"image/vnd.microsoft.icon\"\n        href=\"/static/favicon.ico\"\

+         />\n    <link href=\"https://apps.fedoraproject.org/global/fedora-bootstrap-1.0.1/fedora-bootstrap.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/pagure.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/open-iconic/css/open-iconic.min.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n    <link href=\"/static/fonts/fonts.css\"\

+         \n        rel=\"stylesheet\" type=\"text/css\" />\n    <link href=\"/static/hack_fonts/css/hack-extended.min.css\"\

+         \n        type=\"text/css\" rel=\"stylesheet\" />\n  </head>\n  <body id=\"\

+         error\">\n  <!-- start masthead -->\n    <div class=\"masthead\">\n      <div\

+         \ class=\"container\">\n        <div class=\"row\">\n          <div class=\"\

+         col-sm-3\">\n            <a href=\"/\">\n              <img height=40px src=\"\

+         /static/pagure-logo.png\"\n                alt=\"pagure Logo\" id=\"pagureLogo\"\

+         />\n            </a>\n          </div>\n          <div class=\"col-sm-9\"\

+         >\n            <div class=\"row\">\n\n  <nav class=\"navbar navbar-light p-t-0\

+         \ p-b-0\">\n      <div class=\"container\">\n        <ul class=\"nav navbar-nav\

+         \ nav-underline pull-xs-right\">\n              <li class=\"nav-item p-l-1\"\

+         >\n                <a class=\"nav-link btn btn-primary\" href=\"/login/?next=https://pagure.io/something-that-does-not-exist\"\

+         >Log In</a>\n              </li>\n        </ul>\n      </div>\n    </nav>\n\

+         \n            </div>\n          </div>\n        </div>\n      </div>\n   \

+         \ </div><!-- close masthead-->\n\n    <div class=\"bodycontent p-b-3\">\n\n\

+         \n<div class=\"container p-t-3\">\n  <div class=\"row\">\n    <div class=\"\

+         col-md-8\">\n      <h2>Page not found (404)</h2>\n      <p>With the message:</p>\n\

+         \      <div class=\"card-block\">\n      <p>Project not found</p>\n      </div>\n\

+         \      <p>You have either entered a bad URL or the page has moved, removed,\

+         \ or otherwise rendered unavailable.<br/>\n      Please use the main navigation\

+         \ menu to get (re)started.</p>\n    </div>\n  </div>\n</div>\n    </div>\n\

+         \n    <div class=\"footer p-t-1 p-b-1\">\n        <div class=\"container\"\

+         >\n            <p class=\"text-muted credit\">\n         Copyright &copy;\

+         \ 2014-2017 Red Hat\n          <a href=\"https://pagure.io/pagure\">pagure</a>\

+         \ &mdash;\n          2.12.1 &mdash; <a href=\"https://docs.pagure.org/pagure/usage.html\"\

+         >Documentation</a>\n            </p>\n            <p><a href=\"/ssh_info\"\

+         >SSH Hostkey/Fingerprint</a></p>\n        </div>\n    </div>\n\n    <script\

+         \ type=\"text/javascript\"\n        src=\"/static/jquery.min.js\">\n    </script>\n\

+         \    <script type=\"text/javascript\"\n        src=\"/static/jquery-ui.min.js\"\

+         >\n    </script>\n    <script type=\"text/javascript\"\n        src=\"https://apps.fedoraproject.org/global/fedora-bootstrap-1.0.1/fedora-bootstrap.js\"\

+         >\n    </script>\n    <script type=\"text/javascript\">\n$('[data-toggle=\"\

+         tooltip\"]').tooltip({placement : 'bottom'});\n    </script>\n\n\n</body>\n\

+         </html>"}

+     headers:

+       connection: [Keep-Alive]

+       content-length: ['3009']

+       content-type: [text/html; charset=utf-8]

+       date: ['Tue, 14 Feb 2017 10:48:03 GMT']

+       keep-alive: ['timeout=5, max=100']

+       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.C4Rx4w.okkFK2vKErtBTg2N2mPN4XPMTVc;

+           Expires=Fri, 17-Mar-2017 10:48:03 GMT; Secure; HttpOnly; Path=/']

+       strict-transport-security: [max-age=15768000; includeSubDomains; preload]

+     status: {code: 404, message: NOT FOUND}

+ version: 1

file removed
-61
@@ -1,61 +0,0 @@ 

- from __future__ import unicode_literals

- 

- import kitchen.text.converters

- 

- import hubs.models

- import requests

- 

- 

- def required(session, value):

-     if bool(value):

-         return value

- 

- 

- def text(session, value):

-     return kitchen.text.converters.to_unicode(value)

- 

- 

- def integer(session, value):

-     return int(value)

- 

- 

- def link(session, value):

-     # TODO -- verify that this is actually a link

-     return value

- 

- 

- def username(session, value):

-     if hubs.models.User.by_username(value) is not None:

-         return value

-     raise ValueError('Invalid username')

- 

- 

- def github_organization(session, value):

-     # TODO -- implement this.

-     return value

- 

- def github_repo(session, value):

-     # TODO -- implement this.

-     return value

- 

- 

- def fmn_context(session, value):

-     # TODO get this from the fedmsg config.

-     if value in [

-         'irc', 'email', 'android', 'desktop', 'hubs',]:

-         return value

-     raise ValueError('Invalid FMN context')

- 

- 

- def pagure_repo(session, value):

-     response = requests.get("https://pagure.io/%s" % value, timeout=5)

-     if response.status_code == 200:

-         return value

-     raise ValueError('Invalid pagure repo')

- 

- 

- def fedorahosted_project(session, value):

-     response = requests.get("https://fedorahosted.org/%s" % value, timeout=5)

-     if response.status_code == 200:

-         return value

-     raise ValueError('Invalid fedorahosted project')

file modified
+2 -4
@@ -212,7 +212,7 @@ 

              error = True

              break

          try:

-             param.validator(flask.g.db, val)

+             val = param.validator.from_string(val)

              config[param.name] = val

          except Exception as err:

              flask.flash('Invalid data provided, error: %s' % err, 'error')
@@ -231,6 +231,4 @@ 

                  'Could not save the configuration to the database '

                  'if the error persists, please warn an admin',

                  'error')

- 

-     return flask.render_template(

-         'hubs.html', hub=hub, edit=True)

+     return flask.redirect(flask.url_for('hub_edit', name=hub.name))

file modified
+1 -1
@@ -48,7 +48,7 @@ 

              error = True

              break

          try:

-             val = param.validator(flask.g.db, val)

+             val = param.validator.from_string(val)

              config[param.name] = val

          except Exception as err:

              flask.flash('Invalid data provided, error: %s' % err, 'error')

@@ -1,9 +1,8 @@ 

  from __future__ import unicode_literals

  

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

- from hubs import validators

- 

  

  class About(Widget):

  
@@ -13,7 +12,7 @@ 

          name="text",

          label="Text",

          default="I am a Fedora user, and this is my about",

-         validator=validators.text,

+         validator=validators.Text,

          help="Text about a user.",

          )]

  

@@ -3,7 +3,7 @@ 

  import operator

  import requests

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -16,7 +16,7 @@ 

          name="username",

          label="Username",

          default=None,

-         validator=validators.username,

+         validator=validators.Username,

          help="A FAS username.",

          )]

  

file modified
+8 -2
@@ -23,13 +23,19 @@ 

      :py:class:`WidgetParameter` objects returned by the widget's

      :py:meth:`~Widget.get_parameters` method.

  

+     The value of the parameter is stored in the database as the value returned

+     by the validator's :py:meth:`from_string` method.  It can thus be a string,

+     an integer, a list, a dict, or any JSON-serializable value.

+ 

      Attributes:

          name (str): The name of the parameter.

          label (str): A humanized name of the parameter, which will be shown in

              the UI.

          default: The default value if this parameter is not set.

-         validator (function): A validation function that will be called when

-             a user sets the parameter value.

+         validator (hubs.widgets.validators.Validator): A validator subclass

+             that will be used to convert the parameter value to and from

+             string, raising an exception if it is invalid. This attribute

+             points to the validator subclass, not an instance of the class.

          help (str): A help text that will be shown in the UI.

      """

  

@@ -3,7 +3,7 @@ 

  import requests

  import pkgwat.api

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -19,7 +19,7 @@ 

          name="username",

          label="Username",

          default=None,

-         validator=validators.username,

+         validator=validators.Username,

          help="A FAS username.",

          )]

  

@@ -1,6 +1,6 @@ 

  from __future__ import unicode_literals

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

  
@@ -12,7 +12,7 @@ 

          name="text",

          label="Text",

          default="Lorem ipsum dolor...",

-         validator=validators.text,

+         validator=validators.Text,

          help="Some dummy text to display.",

          )]

  

@@ -5,8 +5,8 @@ 

  import fedmsg.meta

  import requests

  

- from hubs import validators

  from hubs.utils import commas

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -21,7 +21,7 @@ 

          name="username",

          label="Username",

          default=None,

-         validator=validators.username,

+         validator=validators.Username,

          help="A FAS username.",

          )]

  

@@ -1,7 +1,7 @@ 

  from __future__ import unicode_literals

  

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

  import logging
@@ -17,13 +17,13 @@ 

              "name": "username",

              "label": "Username",

              "default": None,

-             "validator": validators.username,

+             "validator": validators.Username,

              "help": "A FAS username.",

          }, {

              "name": "message_limit",

              "label": "Message limit",

              "default": 20,

-             "validator": validators.integer,

+             "validator": validators.Integer,

              "help": "Max number of feed messages to display.",

          }]

  

@@ -2,7 +2,7 @@ 

  

  from six.moves.xmlrpc_client import ServerProxy

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -16,13 +16,13 @@ 

              name="project",

              label="Project",

              default=None,

-             validator=validators.fedorahosted_project,

+             validator=validators.FedorahostedProject,

              help="Name of the trac instance on fedorahosted.org.",

          ), dict(

              name="n_tickets",

              label="Number of tickets",

              default=4,

-             validator=validators.integer,

+             validator=validators.Integer,

              help="The number of tickets to display.",

          )]

  
@@ -48,7 +48,7 @@ 

      Queries Fedorahosted via xmlrpc for tickets. '''

  

      def execute(self):

-         n_tickets = int(self.instance.config["n_tickets"])

+         n_tickets = self.instance.config["n_tickets"]

          url = 'https://fedorahosted.org/%s/rpc' \

              % self.instance.config["project"]

          filters = 'status=accepted&status=assigned&status=new&status=reopened'\

@@ -3,7 +3,7 @@ 

  import logging

  import hubs.utils

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -22,13 +22,13 @@ 

              name="organization",

              label="Organization",

              default=None,

-             validator=validators.github_organization,

+             validator=validators.GithubOrganization,

              help="Github Organization or username",

          ), dict(

              name="display_number",

              label="Number of tickets",

              default=6,

-             validator=validators.integer,

+             validator=validators.Integer,

              help="How many pull requests to display at max.",

          )]

  
@@ -42,10 +42,9 @@ 

      def get_context(self, instance, *args, **kwargs):

          get_prs = GetPRs(instance)

          org = instance.config["organization"]

-         display_number = int(instance.config["display_number"])

          context = dict(

              organization=org,

-             display_number=display_number,

+             display_number=instance.config["display_number"],

              title="Github: Pull Requests",

          )

          context.update(get_prs())
@@ -56,7 +55,7 @@ 

  

      def execute(self):

          org = self.instance.config["organization"]

-         display_number = int(self.instance.config["display_number"])

+         display_number = self.instance.config["display_number"]

          log.info("Getting GH prs for %r, (%r)" % (org, display_number))

          token = fedmsg_config.get('github.oauth_token')

          pulls = []

@@ -2,7 +2,7 @@ 

  

  import requests

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -16,19 +16,19 @@ 

              name="org",

              label="Username",

              default=None,

-             validator=validators.github_organization,

+             validator=validators.GithubOrganization,

              help="Github Organization or username",

          ), dict(

              name="repo",

              label="Repository",

              default=None,

-             validator=validators.github_repo,

+             validator=validators.GithubRepo,

              help="Github repository",

          ), dict(

              name="display_number",

              label="Number of tickets",

              default=10,

-             validator=validators.integer,

+             validator=validators.Integer,

              help="The number of tickets to display.",

          )]

  
@@ -44,7 +44,7 @@ 

          return dict(

              org=instance.config["org"],

              repo=instance.config["repo"],

-             display_number=int(instance.config["display_number"]),

+             display_number=instance.config["display_number"],

              title="Github: Newest Open Tickets",

              all_issues=get_issues(),

          )

@@ -1,7 +1,6 @@ 

  from __future__ import unicode_literals

  

- from hubs import validators

- from hubs.widgets import clean_input

+ from hubs.widgets import clean_input, validators

  from hubs.widgets.base import Widget, WidgetView

  

  
@@ -13,7 +12,7 @@ 

          name="urls",

          label="URLs",

          default=None,

-         validator=validators.text,

+         validator=validators.Text,

          help="A comma separated list of URLs to add to the library. "

               "External links must include the whole link "

               "(starting with http...)."

@@ -1,6 +1,6 @@ 

  from __future__ import unicode_literals

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

  
@@ -12,7 +12,7 @@ 

          name="username",

          label="Username",

          default=None,

-         validator=validators.username,

+         validator=validators.Username,

          help="A FAS username.",

          )]

  

@@ -5,8 +5,8 @@ 

  import datetime

  import requests

  

- from hubs import validators

  from hubs import utils

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -20,13 +20,13 @@ 

              name="calendar",

              label="Calendar",

              default=None,

-             validator=validators.required,

+             validator=validators.Required,

              help="A fedocal calendar.",

          ), dict(

              name="n_meetings",

              label="Number of meetings",

              default=4,

-             validator=validators.integer,

+             validator=validators.Integer,

              help="The number of meetings to display.",

          )]

  
@@ -56,7 +56,7 @@ 

  

      def execute(self):

          calendar = self.instance.config["calendar"]

-         n_meetings = int(self.instance.config.get("n_meetings", 4))

+         n_meetings = self.instance.config.get("n_meetings", 4)

          base = ('https://apps.fedoraproject.org/calendar/api/meetings/'

                  '?calendar=%s')

          url = base % calendar

@@ -1,6 +1,6 @@ 

  from __future__ import unicode_literals

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -18,7 +18,7 @@ 

              name="repo",

              label="Repository",

              default=None,

-             validator=validators.pagure_repo,

+             validator=validators.PagureRepo,

              help="Pagure repo name.",

          )]

  

@@ -2,7 +2,7 @@ 

  

  import requests

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -18,7 +18,7 @@ 

              name="repo",

              label="Repository",

              default=None,

-             validator=validators.pagure_repo,

+             validator=validators.PagureRepo,

              help="Pagure repo name",

          )]

  

@@ -2,8 +2,8 @@ 

  

  from collections import OrderedDict as ordereddict

  

- from hubs import validators

  from hubs.utils import username2avatar

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

  
@@ -19,25 +19,25 @@ 

              name="link",

              label="Link",

              default=None,

-             validator=validators.link,

+             validator=validators.Link,

              help="Link to the community rules and guidelines.",

          ), dict(

              name="schedule_text",

              label="Schedule text",

              default=None,

-             validator=validators.text,

+             validator=validators.Text,

              help="Some text about when meetings are.",

          ), dict(

              name="schedule_link",

              label="Schedule link",

              default=None,

-             validator=validators.link,

+             validator=validators.Link,

              help="Link to a schedule for IRC meetings, etc.",

          ), dict(

              name="minutes_link",

              label="Minutes link",

              default=None,

-             validator=validators.link,

+             validator=validators.Link,

              help="Link to meeting menutes from past meetings.",

          )]

  

@@ -1,6 +1,6 @@ 

  from __future__ import unicode_literals

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  

  
@@ -13,7 +13,7 @@ 

              name="text",

              label="Text",

              default="Lorem ipsum dolor...",

-             validator=validators.text,

+             validator=validators.Text,

              help="Some dummy text to display.",

          )]

  

@@ -3,7 +3,7 @@ 

  import flask

  import hubs.models

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -22,7 +22,7 @@ 

              name="username",

              label="Username",

              default=None,

-             validator=validators.username,

+             validator=validators.Username,

              help="A FAS username.",

          )]

  

@@ -0,0 +1,151 @@ 

+ from __future__ import unicode_literals

+ 

+ import flask

+ import hubs.models

+ import kitchen.text.converters

+ import requests

+ 

+ 

+ class Validator(object):

+     """Convert widget parameters to and from string, and validate their value.

+ 

+     Validators are used to convert

+     :py:class:`~hubs.widgets.base.WidgetParameter` values to and from string.

+     They will raise an exception if the value is invalid.

+ 

+     A validator is a subclass of the :py:class:`Validator` class and implements

+     two class methods: :py:meth:`from_string` and :py:meth:`to_string`.

+     """

+ 

+     @classmethod

+     def from_string(cls, value):

+         """Convert the value from a string to a JSON-serializable value.

+ 

+         The result of this function will be stored in the database for widget

+         parameters.

+ 

+         Raises:

+             ValueError: The value is invalid.

+         """

+         if value is None:

+             return ""

+         return value

+ 

+     @classmethod

+     def to_string(cls, value):

+         """Convert the value to a string.

+ 

+         The result of this function will be used in the widget configuration

+         form fields.

+         """

+         return value

+ 

+ 

+ class Required(Validator):

+     """Raises an error if the value is ``False``-like."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         if not bool(value):

+             raise ValueError("the parameter is required")

+         return value

+ 

+ 

+ class Text(Validator):

+     """Raises an error if the value can't be converted to unicode."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         return kitchen.text.converters.to_unicode(value)

+ 

+ 

+ class Integer(Validator):

+     """Raises an error if the value can't be converted to an integer."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         return int(value)

+ 

+ 

+ class Link(Validator):

+     """Raises an error if the value doesn't look like a link."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         # TODO -- verify that this is actually a link

+         return value

+ 

+ 

+ class Username(Validator):

+     """Raises an error if the value isn't an existing username.

+ 

+     There must be a corresponding :py:class:`~hubs.models.User` record.

+ 

+     This validator does not return the User instance because it is not

+     JSON-serializable, it returns the username unchanged.

+     """

+ 

+     @classmethod

+     def from_string(cls, value):

+         if hubs.models.User.by_username(value) is not None:

+             return value

+         raise ValueError('Invalid username')

+ 

+     @classmethod

+     def to_string(cls, value):

+         if value is None and flask.g.auth.logged_in:

+             return flask.g.auth.user.username

+         return value

+ 

+ 

+ class GithubOrganization(Validator):

+     """Fails if the Github organization name does not exist."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         # TODO -- implement this.

+         return value

+ 

+ 

+ class GithubRepo(Validator):

+     """Fails if the Github repository name does not exist."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         # TODO -- implement this.

+         return value

+ 

+ 

+ class FMNContext(Validator):

+     """Fails if the value is not a valid FMN context name."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         # TODO get this from the fedmsg config.

+         if value in [

+             'irc', 'email', 'android', 'desktop', 'hubs',]:

+             return value

+         raise ValueError('Invalid FMN context')

+ 

+ 

+ class PagureRepo(Validator):

+     """Fails if the Pagure repository name does not exist."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         response = requests.get("https://pagure.io/%s" % value, timeout=5)

+         if response.status_code == 200:

+             return value

+         raise ValueError('Invalid pagure repo')

+ 

+ 

+ class FedorahostedProject(Validator):

+     """Fails if the FedoraHosted project name does not exist."""

+ 

+     @classmethod

+     def from_string(cls, value):

+         response = requests.get("https://fedorahosted.org/%s/" % value,

+                                 timeout=5, allow_redirects=False)

+         if response.status_code == 200:

+             return value

+         raise ValueError('Invalid fedorahosted project')

@@ -2,8 +2,8 @@ 

  

  import requests

  

- from hubs import validators

  from hubs.utils import username2avatar

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -17,7 +17,7 @@ 

              name="username",

              label="Username",

              default=None,

-             validator=validators.username,

+             validator=validators.Username,

              help="A FAS username.",

          )]

  

@@ -2,7 +2,7 @@ 

  

  import requests

  

- from hubs import validators

+ from hubs.widgets import validators

  from hubs.widgets.base import Widget, WidgetView

  from hubs.widgets.caching import CachedFunction

  
@@ -21,7 +21,7 @@ 

              name="username",

              label="Username",

              default=None,

-             validator=validators.username,

+             validator=validators.Username,

              help="A FAS username.",

          )]

  

Use the validators's return value for the widget conf

The validators were returning a converted value (for example, to integer) but that value was ignored and the widgets had to do the conversion again themselves when they wanted to use that value.

This changeset make use of the validators to convert to and from the string that was passed to the config form. It also moves them to the widgets module because validators are actually only used by widgets, so it makes more sense to have them with the other widget-related modules.

There also two other smaller fixes. As usual, I tried to keep the commits topical.

Perhaps add an assertion that this result is a 200.

A 200 assertion would be good here too.

I would recommend tracking TODOs in the issue tracker rather than in code. In my experience, code TODOs often sit for a long time and become forgotten.

Same here - I recommend filing this as an issue rather than putting a TODO.

I recommend adding docblocks on all the classes and methods in this file, so that all the types are clearly indicated and the purposes of the classes/methods are documented.

No blockers, just some suggestions that you can apply at your option. I also recommend writing tests for this change, as it appears that the test suite isn't altered by these commits.

Thanks for your review ! I just converted the existing functions but you're right, it's the perfect time to add documentation :)

I'll also add tickets for the unimplemented validators, but I'll leave the TODO comments too, for those who just happen on the code and might want to fix it ;-)

Oh, and I've added unit tests too. Writing unit tests for validators is so easy that it would be a shame to miss it ;-)

3 new commits added

  • Add unittests for validators
  • Add two more assertions in the tests
  • Add and fix documentation
7 years ago

2 new commits added

  • Provide a sensible default for the Username validator
  • Cleanup blank spaces
7 years ago

Pull-Request has been merged by abompard

7 years ago
Metadata
Changes Summary 31
+7 -0
file changed
docs/api.rst
+2 -2
file changed
hubs/templates/add_widget.html
+2 -2
file changed
hubs/templates/edit.html
+7 -2
file changed
hubs/tests/test_fedora_hubs_flask_api.py
+69
file added
hubs/tests/test_widget_validators.py
+186
file added
hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_fedorahosted_project
+351
file added
hubs/tests/vcr-request-data/hubs.tests.test_widget_validators.ValidatorsTest.test_pagure_repo
-61
file removed
hubs/validators.py
+2 -4
file changed
hubs/views/hub.py
+1 -1
file changed
hubs/views/widget.py
+2 -3
file changed
hubs/widgets/about/__init__.py
+2 -2
file changed
hubs/widgets/badges/__init__.py
+8 -2
file changed
hubs/widgets/base.py
+2 -2
file changed
hubs/widgets/bugzilla/__init__.py
+2 -2
file changed
hubs/widgets/dummy/__init__.py
+2 -2
file changed
hubs/widgets/fedmsgstats/__init__.py
+3 -3
file changed
hubs/widgets/feed/__init__.py
+4 -4
file changed
hubs/widgets/fhosted/__init__.py
+5 -6
file changed
hubs/widgets/github_pr/__init__.py
+5 -5
file changed
hubs/widgets/githubissues/__init__.py
+2 -3
file changed
hubs/widgets/library/__init__.py
+2 -2
file changed
hubs/widgets/linechart/__init__.py
+4 -4
file changed
hubs/widgets/meetings/__init__.py
+2 -2
file changed
hubs/widgets/pagure_pr/__init__.py
+2 -2
file changed
hubs/widgets/pagureissues/__init__.py
+5 -5
file changed
hubs/widgets/rules/__init__.py
+2 -2
file changed
hubs/widgets/sticky/__init__.py
+2 -2
file changed
hubs/widgets/subscriptions/__init__.py
+151
file added
hubs/widgets/validators.py
+2 -2
file changed
hubs/widgets/workflow/pendingacls.py
+2 -2
file changed
hubs/widgets/workflow/updates2stable.py