diff --git a/endpoints/api.py b/endpoints/api.py index edd1a9b87..5c86e8710 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -28,7 +28,7 @@ from auth.permissions import (ReadRepositoryPermission, ViewTeamPermission, UserPermission) from endpoints.common import (common_login, get_route_data, truthy_param, - start_build, add_notification) + start_build, check_repository_usage) from endpoints.trigger import (BuildTrigger, TriggerActivationException, TriggerDeactivationException, EmptyRepositoryException) @@ -2197,6 +2197,7 @@ def subscribe(user, plan, token, require_business_plan): cus = stripe.Customer.create(email=user.email, plan=plan, card=card) user.stripe_id = cus.id user.save() + check_repository_usage(user, plan_found) log_action('account_change_plan', user.username, {'plan': plan}) except stripe.CardError as e: return carderror_response(e) @@ -2213,6 +2214,7 @@ def subscribe(user, plan, token, require_business_plan): # We only have to cancel the subscription if they actually have one cus.cancel_subscription() cus.save() + check_repository_usage(user, plan_found) log_action('account_change_plan', user.username, {'plan': plan}) else: @@ -2228,6 +2230,7 @@ def subscribe(user, plan, token, require_business_plan): return carderror_response(e) response_json = subscription_view(cus.subscription, private_repos) + check_repository_usage(user, plan_found) log_action('account_change_plan', user.username, {'plan': plan}) resp = jsonify(response_json) diff --git a/endpoints/common.py b/endpoints/common.py index 0f0e68df7..064d29461 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -126,11 +126,14 @@ def render_page_template(name, **kwargs): return resp -def add_notification(kind, metadata=None, user=None): - if not user and current_user: - user = current_user.db_user() +def check_repository_usage(user_or_org, plan_found): + private_repos = model.get_private_repo_count(user_or_org.username) + repos_allowed = plan_found['privateRepos'] - return model.create_notification(kind, user, metadata or {}) + if private_repos > repos_allowed: + model.create_notification('over_private_usage', user_or_org, {'namespace': user_or_org.username}) + else: + model.delete_notifications_by_kind(user_or_org, 'over_private_usage') def start_build(repository, dockerfile_id, tags, build_name, subdir, manual, diff --git a/static/directives/header-bar.html b/static/directives/header-bar.html index 688eb8411..6d31cf951 100644 --- a/static/directives/header-bar.html +++ b/static/directives/header-bar.html @@ -43,8 +43,7 @@ ng-show="notificationService.notifications.length" ng-class="notificationService.notificationClasses" bs-tooltip="" - title="{{ notificationService.notificationSummaries }}" - data-html="true" + title="User Notifications" data-placement="left" data-container="body"> {{ notificationService.notifications.length }} diff --git a/static/directives/notification-view.html b/static/directives/notification-view.html index 5adb81261..6327a5df8 100644 --- a/static/directives/notification-view.html +++ b/static/directives/notification-view.html @@ -1,7 +1,7 @@
-
{{ getMessage(notification) }}
+
{{ notification.organization }} diff --git a/static/js/app.js b/static/js/app.js index 62734d089..07b0a53ff 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -479,8 +479,8 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu return userService; }]); - $provide.factory('NotificationService', ['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', - function($rootScope, $interval, UserService, ApiService, StringBuilderService) { + $provide.factory('NotificationService', ['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', 'PlanService', 'UserService', + function($rootScope, $interval, UserService, ApiService, StringBuilderService, PlanService, UserService) { var notificationService = { 'user': null, 'notifications': [], @@ -493,20 +493,35 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu var notificationKinds = { 'test_notification': { 'level': 'primary', - 'summary': 'This is a test notification', 'message': 'This notification is a long message for testing', 'page': '/about/' }, 'password_required': { 'level': 'error', - 'summary': 'A password is needed for your account', 'message': 'In order to begin pushing and pulling repositories to Quay.io, a password must be set for your account', 'page': '/user?tab=password' + }, + 'over_private_usage': { + 'level': 'error', + 'message': 'Namespace {namespace} is over its allowed private repository count. ' + + '

Please upgrade your plan to avoid disruptions in service.', + 'page': function(metadata) { + var organization = UserService.getOrganization(metadata['namespace']); + if (organization) { + return '/organization/' + metadata['namespace'] + '/admin'; + } else { + return '/user'; + } + } } }; notificationService.getPage = function(notification) { - return notificationKinds[notification['kind']]['page']; + var page = notificationKinds[notification['kind']]['page']; + if (typeof page != 'string') { + page = page(notification['metadata']); + } + return page; }; notificationService.getMessage = function(notification) { @@ -514,20 +529,6 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu return StringBuilderService.buildString(kindInfo['message'], notification['metadata']); }; - notificationService.getSummary = function(notification) { - var kindInfo = notificationKinds[notification['kind']]; - return StringBuilderService.buildString(kindInfo['summary'], notification['metadata']); - }; - - notificationService.getSummaries = function(notifications) { - var summaries = []; - for (var i = 0; i < notifications.length; ++i) { - var notification = notifications[i]; - summaries.push(notificationService.getSummary(notification)); - } - return summaries.join('
'); - }; - notificationService.getClass = function(notification) { return 'notification-' + notificationKinds[notification['kind']]['level']; }; @@ -550,7 +551,6 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu ApiService.listUserNotifications().then(function(resp) { notificationService.notifications = resp['notifications']; notificationService.notificationClasses = notificationService.getClasses(notificationService.notifications); - notificationService.notificationSummaries = notificationService.getSummaries(notificationService.notifications); }); }; @@ -559,6 +559,12 @@ quayApp = angular.module('quay', ['ngRoute', 'chieffancypants.loadingBar', 'angu pollTimerHandle = $interval(notificationService.update, 5 * 60 * 1000 /* five minutes */); }; + // Watch for plan changes and update. + PlanService.registerListener(this, function(plan) { + notificationService.reset(); + notificationService.update(); + }); + // Watch for user changes and update. $rootScope.$watch(function() { return UserService.currentUser(); }, function(currentUser) { notificationService.reset(); diff --git a/test/data/test.db b/test/data/test.db index e4f9f044f..ff059481c 100644 Binary files a/test/data/test.db and b/test/data/test.db differ