#454 Halp widget: only allow following existing hubs
Merged 4 years ago by abompard. Opened 4 years ago by abompard.
abompard/fedora-hubs fix/halp-select-hubs  into  develop

@@ -80,19 +80,19 @@ 

        data: {q: value},

        dataType: 'json',

        cache: false,

-       success: function(result) {

+       success: (result) => {

          var suggestions = result.data;

          if (this.props.filterResults) {

            suggestions = suggestions.filter(this.props.filterResults);

          }

          this.setState({suggestions: suggestions});

-       }.bind(this),

-       error: function(xhr, status, err) {

+       },

+       error: (xhr, status, err) => {

          console.error(this.props.url, status, err.toString());

-       }.bind(this),

-       complete: function(xhr, status) {

+       },

+       complete: (xhr, status) => {

          this.setState({isLoading: false});

-       }.bind(this)

+       }

      });

    }

  

@@ -68,7 +68,7 @@ 

  });

  

  

- export default class Config extends React.Component {

+ class Config extends React.Component {

  

    constructor(props) {

      super(props);
@@ -130,20 +130,6 @@ 

          );

      });

  

-     /*

-     const IntlCompletionInput = injectIntl((props) => (

-         <CompletionInput

-           inputProps={{

-             name: "hub-add",

-             placeholder: props.intl.formatMessage(messages.placeholder),

-           }}

-           url={props.widget.urls.allHubs}

-           onChange={this.handleHubSelected}

-           />

-     ));

-               //<IntlCompletionInput {...this.props} />

-     */

- 

      return (

        <div className="row widget-halp-config">

          <div className="col-sm-4 hubs-list">
@@ -162,12 +148,12 @@ 

            <FormattedMessage {...messages.main_title} tagName="h4" />

            <FormattedMessage {...messages.main_subtitle} tagName="p" />

            <form onSubmit={this.handleAddClicked}>

-             <fieldset className="row">

+             <div className="form-row">

                <div className="col-sm-9">

                  <CompletionInput

                    inputProps={{

                      name: "hub-add",

-                     placeholder: "Enter team name here...",

+                     placeholder: this.props.intl.formatMessage(messages.placeholder),

                      value: this.state.selectedHub || "",

                    }}

                    url={this.props.widget.urls.allHubs}
@@ -179,28 +165,33 @@ 

                    <FormattedMessage {...messages.add} />

                  </button>

                </div>

-             </fieldset>

+             </div>

            </form>

-           <fieldset className="row">

-             <label className="col-sm-6 form-control-label">

+           <div className="mt-3 form-inline">

+             <label className="form-control-label mr-2">

                <FormattedMessage {...messages.per_page} />

              </label>

-             <div className="col-sm-6">

                <input

                  name="per_page"

                  type="number"

                  value={this.props.values.per_page || "5"}

                  onChange={this.handlePerPageChanged}

-                 className="form-control form-control-sm"

+                 className="form-control form-control-sm col-2"

                  />

+           </div>

+           <div className="text-muted">

+             <FormattedMessage {...messages.per_page_help} tagName="small" />

+           </div>

+           { this.props.widget.configError &&

+             <div className="alert alert-warning my-2">

+               { this.props.widget.configError }

              </div>

-             <div className="col-sm-12 text-muted">

-               <FormattedMessage {...messages.per_page_help} tagName="small" />

-             </div>

-           </fieldset>

+           }

          </div>

        </div>

      );

    }

- 

  }

+ 

+ 

+ export default injectIntl(Config);

@@ -4,6 +4,7 @@ 

  

  from hubs.app import app

  from hubs.models import Hub

+ from hubs.utils.views import configure_widget_instance, WidgetConfigError

  from hubs.tests import widget_instance

  from hubs.widgets import registry

  from . import WidgetTest
@@ -147,7 +148,7 @@ 

              'name': 'halp',

              'params': [

                  {'default': [],

-                  'help': 'A comma-separated list of hubs to monitor.',

+                  'help': 'A list of hubs to monitor.',

                   'label': 'Hubs',

                   'name': 'hubs',

                   'renderTag': 'input',
@@ -344,6 +345,16 @@ 

                  },

              })

  

+     def test_follow_non_existing_hub(self):

+         # See #443

+         self.assertRaises(

+             WidgetConfigError,

+             configure_widget_instance,

+             self.widget, dict(hubs=["non-existing"], per_page=4)

+             )

+         self.assertDictEqual(

+             self.widget.config, dict(hubs=["fedora-devel"], per_page=3))

+ 

  

  class HalpFunctionsTestCase(WidgetTest):

      plugin = 'halp'  # The name in hubs.widgets.registry

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

  from hubs.widgets.base import Widget

  from hubs.widgets import validators

  from .views import hubs_suggest_view

+ from .utils import listofhubs_validator

  

  

  class Halp(Widget):
@@ -26,8 +27,8 @@ 

              name="hubs",

              label="Hubs",

              default=[],

-             validator=validators.CommaSeparatedList,

-             help="A comma-separated list of hubs to monitor.",

+             validator=listofhubs_validator,

+             help="A list of hubs to monitor.",

          ), dict(

              name="per_page",

              label="Requests per page",

@@ -20,3 +20,22 @@ 

              HubConfig.chat_channel == msg["channel"]

          ).values(Hub.name)

          ]

+ 

+ 

+ def listofhubs_validator(value):

+     """Fails if the value isn't a list.

+ 

+     If the value is a string, list, it will be interpreted as a comma-separated

+     list and converted to a Python list. If there is no comma in the original

+     value, it will produce a list with a single element. Whitespaces will be

+     stripped from the elements, so spaces are allowed around the commas.

+     """

+     if not value:

+         return []

+     if not isinstance(value, list):

+         raise ValueError("Expected a list")

+     hub_names = [h.name for h in Hub.query.all()]

+     for hub in value:

+         if hub not in hub_names:

+             raise ValueError("hub \"{}\" does not exist".format(hub))

+     return value