-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1431 from eciis/feature-toggles
Create feature toggles micro service
- Loading branch information
Showing
62 changed files
with
17,205 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Feature Toggle handler.""" | ||
|
||
import json | ||
from . import BaseHandler | ||
from utils import json_response, Utils | ||
from util import login_required | ||
from models import Feature, get_deciis | ||
from custom_exceptions import NotAuthorizedException | ||
|
||
|
||
__all__ = ['FeatureToggleHandler'] | ||
|
||
def to_json(feature_list, language="pt-br"): | ||
""" | ||
Method to generate list of feature models in json format object. | ||
Params: | ||
feature_list -- List of features objects | ||
""" | ||
|
||
features = [feature.make(language) for feature in feature_list] | ||
return json.dumps(features) | ||
|
||
class FeatureToggleHandler(BaseHandler): | ||
"""Feature toggle hanler.""" | ||
|
||
@json_response | ||
@login_required | ||
def get(self, user): | ||
""" | ||
Method to get all features or filter by name using query parameter. | ||
""" | ||
|
||
feature_name = self.request.get('name') | ||
language = self.request.get('lang') | ||
|
||
if feature_name: | ||
features = [Feature.get_feature(feature_name)] | ||
else: | ||
features = Feature.get_all_features() | ||
|
||
self.response.write(to_json(features, language)) | ||
|
||
@login_required | ||
@json_response | ||
def put(self, user): | ||
""" | ||
Method for modifying the properties of one or more features. | ||
""" | ||
|
||
""" | ||
The super user is the admin of | ||
'Departamento do Complexo Industrial e Inovação em Saúde". | ||
""" | ||
super_user = get_deciis().admin | ||
|
||
Utils._assert(not (super_user == user.key), "User not allowed to modify features!", NotAuthorizedException) | ||
|
||
feature_body = json.loads(self.request.body) | ||
feature = Feature.set_visibility(feature_body) | ||
self.response.write(json.dumps(feature.make())) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
"""Feature model.""" | ||
import json | ||
from google.appengine.ext import ndb | ||
|
||
__all__ = ['Feature'] | ||
|
||
class Feature(ndb.Model): | ||
""" | ||
Model of Feature. | ||
""" | ||
|
||
name = ndb.StringProperty() | ||
enable_mobile = ndb.StringProperty( | ||
choices=set(["SUPER_USER", "ADMIN", "ALL", "DISABLED"])) | ||
enable_desktop = ndb.StringProperty( | ||
choices=set(["SUPER_USER", "ADMIN", "ALL", "DISABLED"])) | ||
translation = ndb.JsonProperty(default="{}") | ||
|
||
@staticmethod | ||
@ndb.transactional(xg=True) | ||
def create(name, translation_dict, enable_mobile="ALL", enable_desktop="ALL"): | ||
""" | ||
Method to create new feature. | ||
Params: | ||
name -- Name of the new feature | ||
enable_mobile -- (Optional) User group to which the feature is enabled in the mobile version. If not received will be enabled for everyone. | ||
enable_desktop -- (Optional) User group to which the feature is enabled in the desktop version. If not received will be enabled for everyone. | ||
""" | ||
|
||
feature = Feature(id=name, name=name, enable_desktop=enable_desktop, enable_mobile=enable_mobile) | ||
feature.translation = json.dumps(translation_dict) | ||
feature.put() | ||
return feature | ||
|
||
@staticmethod | ||
def set_visibility(feature_dict): | ||
""" | ||
Method to enable or disable feature. | ||
Params: | ||
features_dict -- dictionary containing the properties of the feature model to be modified. | ||
""" | ||
|
||
feature = Feature.get_feature(feature_dict.get('name')) | ||
feature.enable_desktop = feature_dict['enable_desktop'] | ||
feature.enable_mobile = feature_dict['enable_mobile'] | ||
feature.put() | ||
return feature | ||
|
||
@staticmethod | ||
def get_all_features(): | ||
""" | ||
Method to get all stored features. | ||
""" | ||
|
||
features = Feature.query().fetch() | ||
return features | ||
|
||
@staticmethod | ||
def get_feature(feature_name): | ||
""" | ||
Method to get feature by name. | ||
Params: | ||
feature_name -- name of the requested feature | ||
""" | ||
|
||
feature = Feature.get_by_id(feature_name) | ||
|
||
if feature: | ||
return feature | ||
else: | ||
raise Exception("Feature not found!") | ||
|
||
def make(self, language="pt-br"): | ||
""" | ||
Method to make feature. | ||
""" | ||
make_obj = { | ||
'name': self.name, | ||
'enable_mobile': self.enable_mobile, | ||
'enable_desktop': self.enable_desktop, | ||
'translation': json.loads(self.translation).get(language) | ||
} | ||
|
||
return make_obj |
Empty file.
99 changes: 99 additions & 0 deletions
99
backend/test/feature_toggle_handler_tests/feature_toggle_handler_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Feature toggle handler test""" | ||
|
||
import json | ||
from ..test_base_handler import TestBaseHandler | ||
from handlers import FeatureToggleHandler | ||
from models import Feature | ||
from mock import patch | ||
from .. import mocks | ||
|
||
|
||
USER = {'email': '[email protected]'} | ||
|
||
class FeatureToggleHandlerTest(TestBaseHandler): | ||
""" | ||
Feature Toggle Handler Test. | ||
""" | ||
|
||
@classmethod | ||
def setUp(cls): | ||
"""Provide the base for the tests.""" | ||
super(FeatureToggleHandlerTest, cls).setUp() | ||
app = cls.webapp2.WSGIApplication( | ||
[("/api/feature-toggles.*", | ||
FeatureToggleHandler) | ||
], debug=True) | ||
cls.testapp = cls.webtest.TestApp(app) | ||
|
||
cls.feature = Feature.create('feature-test', {'pt-br': 'Feature Teste'}) | ||
cls.other_feature = Feature.create('feature-test-other', {'pt-br': 'Feature Teste'}) | ||
|
||
|
||
@patch('util.login_service.verify_token', return_value=USER) | ||
def test_get_all(self, verify_token): | ||
"""Test get all features.""" | ||
|
||
features = self.testapp.get('/api/feature-toggles?lang=pt-br').json | ||
features_make = [self.feature.make(), self.other_feature.make()] | ||
|
||
self.assertEquals(len(features), 2) | ||
self.assertItemsEqual(features, features_make) | ||
|
||
@patch('util.login_service.verify_token', return_value=USER) | ||
def test_get_by_query(self, verify_token): | ||
"""Test get feature with query parameter.""" | ||
feature = self.testapp.get('/api/feature-toggles?name=feature-test&lang=pt-br').json | ||
feature_make = [self.feature.make()] | ||
|
||
self.assertListEqual(feature, feature_make) | ||
|
||
feature = self.testapp.get('/api/feature-toggles?name=feature-test-other&lang=pt-br').json | ||
feature_make = [self.other_feature.make()] | ||
|
||
self.assertListEqual(feature, feature_make) | ||
|
||
with self.assertRaises(Exception) as raises_context: | ||
self.testapp.get('/api/feature-toggles?name=sfjkldh') | ||
|
||
exception_message = self.get_message_exception(str(raises_context.exception)) | ||
|
||
self.assertEquals(exception_message, "Error! Feature not found!") | ||
|
||
@patch('util.login_service.verify_token', return_value=USER) | ||
def test_put(self, verify_token): | ||
"""Test put features.""" | ||
|
||
user_admin = mocks.create_user('[email protected]') | ||
user = mocks.create_user() | ||
deciis = mocks.create_institution('DECIIS') | ||
deciis.trusted = True | ||
deciis.add_member(user_admin) | ||
deciis.set_admin(user_admin.key) | ||
user_admin.add_institution(deciis.key) | ||
user_admin.add_institution_admin(deciis.key) | ||
|
||
feature = self.feature.make() | ||
other_feature = self.other_feature.make() | ||
|
||
feature['enable_mobile'] = 'DISABLED' | ||
other_feature['enable_desktop'] = 'DISABLED' | ||
|
||
self.testapp.put_json('/api/feature-toggles', feature) | ||
self.testapp.put_json('/api/feature-toggles', other_feature) | ||
|
||
self.feature = self.feature.key.get() | ||
self.other_feature = self.other_feature.key.get() | ||
|
||
self.assertEquals(self.feature.enable_desktop, 'ALL') | ||
self.assertEquals(self.feature.enable_mobile, 'DISABLED') | ||
self.assertEquals(self.other_feature.enable_desktop, 'DISABLED') | ||
self.assertEquals(self.other_feature.enable_mobile, 'ALL') | ||
|
||
verify_token._mock_return_value = {'email': user.email[0]} | ||
|
||
with self.assertRaises(Exception) as raises_context: | ||
self.testapp.put_json('/api/feature-toggles', feature) | ||
|
||
exception_message = self.get_message_exception(str(raises_context.exception)) | ||
|
||
self.assertEquals(exception_message, 'Error! User not allowed to modify features!') |
Oops, something went wrong.