From 5633c1fc79b1411c6feea06b1d6eaf16c43962cd Mon Sep 17 00:00:00 2001 From: yackob03 Date: Thu, 19 Dec 2013 17:06:04 -0500 Subject: [PATCH] Merge the plans and mark many as deprecated. Fix a bunch of pylint errors. --- data/plans.py | 51 +++++++++++------ endpoints/api.py | 128 +++++++++++++++++++++++++----------------- endpoints/webhooks.py | 21 +++---- 3 files changed, 119 insertions(+), 81 deletions(-) diff --git a/data/plans.py b/data/plans.py index aa17ed4b7..2b8b6af2b 100644 --- a/data/plans.py +++ b/data/plans.py @@ -1,20 +1,13 @@ -import json -import itertools - -USER_PLANS = [ - { - 'title': 'Open Source', - 'price': 0, - 'privateRepos': 0, - 'stripeId': 'free', - 'audience': 'Share with the world', - }, +PLANS = [ + # Deprecated Plans { 'title': 'Micro', 'price': 700, 'privateRepos': 5, 'stripeId': 'micro', 'audience': 'For smaller teams', + 'bus_features': False, + 'deprecated': True, }, { 'title': 'Basic', @@ -22,6 +15,8 @@ USER_PLANS = [ 'privateRepos': 10, 'stripeId': 'small', 'audience': 'For your basic team', + 'bus_features': False, + 'deprecated': True, }, { 'title': 'Medium', @@ -29,6 +24,8 @@ USER_PLANS = [ 'privateRepos': 20, 'stripeId': 'medium', 'audience': 'For medium teams', + 'bus_features': False, + 'deprecated': True, }, { 'title': 'Large', @@ -36,16 +33,28 @@ USER_PLANS = [ 'privateRepos': 50, 'stripeId': 'large', 'audience': 'For larger teams', + 'bus_features': False, + 'deprecated': True, }, -] -BUSINESS_PLANS = [ + # Active plans { 'title': 'Open Source', 'price': 0, 'privateRepos': 0, - 'stripeId': 'bus-free', + 'stripeId': 'free', 'audience': 'Committment to FOSS', + 'bus_features': False, + 'deprecated': False, + }, + { + 'title': 'Personal', + 'price': 1200, + 'privateRepos': 5, + 'stripeId': 'personal', + 'audience': 'Individuals', + 'bus_features': False, + 'deprecated': False, }, { 'title': 'Skiff', @@ -53,6 +62,8 @@ BUSINESS_PLANS = [ 'privateRepos': 10, 'stripeId': 'bus-micro', 'audience': 'For startups', + 'bus_features': True, + 'deprecated': False, }, { 'title': 'Yacht', @@ -60,6 +71,8 @@ BUSINESS_PLANS = [ 'privateRepos': 20, 'stripeId': 'bus-small', 'audience': 'For small businesses', + 'bus_features': True, + 'deprecated': False, }, { 'title': 'Freighter', @@ -67,6 +80,8 @@ BUSINESS_PLANS = [ 'privateRepos': 50, 'stripeId': 'bus-medium', 'audience': 'For normal businesses', + 'bus_features': True, + 'deprecated': False, }, { 'title': 'Tanker', @@ -74,14 +89,16 @@ BUSINESS_PLANS = [ 'privateRepos': 125, 'stripeId': 'bus-large', 'audience': 'For large businesses', + 'bus_features': True, + 'deprecated': False, }, ] -def get_plan(id): +def get_plan(plan_id): """ Returns the plan with the given ID or None if none. """ - for plan in itertools.chain(USER_PLANS, BUSINESS_PLANS): - if plan['stripeId'] == id: + for plan in PLANS: + if plan['stripeId'] == plan_id: return plan return None diff --git a/endpoints/api.py b/endpoints/api.py index 95912b570..19947c62f 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -12,7 +12,7 @@ from collections import defaultdict from data import model from data.queue import dockerfile_build_queue -from data.plans import USER_PLANS, BUSINESS_PLANS, get_plan +from data.plans import PLANS, get_plan from app import app from util.email import send_confirmation_email, send_recovery_email from util.names import parse_repository_name, format_robot_username @@ -30,6 +30,7 @@ from endpoints.web import common_login from util.cache import cache_control from datetime import datetime, timedelta + store = app.config['STORAGE'] user_files = app.config['USERFILES'] logger = logging.getLogger(__name__) @@ -37,8 +38,9 @@ logger = logging.getLogger(__name__) def log_action(kind, user_or_orgname, metadata={}, repo=None): performer = current_user.db_user() - model.log_action(kind, user_or_orgname, performer=performer, ip=request.remote_addr, - metadata=metadata, repository=repo) + model.log_action(kind, user_or_orgname, performer=performer, + ip=request.remote_addr, metadata=metadata, repository=repo) + def api_login_required(f): @wraps(f) @@ -64,7 +66,7 @@ def handle_dme(ex): @app.errorhandler(KeyError) -def handle_dme(ex): +def handle_dme_key_error(ex): return make_response(ex.message, 400) @@ -76,8 +78,7 @@ def welcome(): @app.route('/api/plans/') def plans_list(): return jsonify({ - 'user': USER_PLANS, - 'business': BUSINESS_PLANS, + 'plans': PLANS, }) @@ -164,7 +165,7 @@ def convert_user_to_organization(): # Subscribe the organization to the new plan. plan = convert_data['plan'] - subscribe(user, plan, None, BUSINESS_PLANS) + subscribe(user, plan, None, PLANS) # Convert the user to an organization. model.convert_user_to_organization(user, model.get_user(admin_username)) @@ -172,7 +173,6 @@ def convert_user_to_organization(): # And finally login with the admin credentials. return conduct_signin(admin_username, admin_password) - @app.route('/api/user/', methods=['PUT']) @@ -427,7 +427,7 @@ def change_organization_details(orgname): except model.InvalidOrganizationException: abort(404) - org_data = request.get_json(); + org_data = request.get_json() if 'invoice_email' in org_data: logger.debug('Changing invoice_email for organization: %s', org.username) model.change_invoice_email(org, org_data['invoice_email']) @@ -477,7 +477,7 @@ def get_organization_member(orgname, membername): abort(404) member_dict = None - member_teams = model.get_organization_members_with_teams(org, membername = membername) + member_teams = model.get_organization_members_with_teams(org, membername=membername) for member in member_teams: if not member_dict: member_dict = {'username': member.user.username, @@ -551,17 +551,20 @@ def update_organization_team(orgname, teamname): log_action('org_create_team', orgname, {'team': teamname}) if is_existing: - if 'description' in details and team.description != details['description']: + if ('description' in details and + team.description != details['description']): team.description = details['description'] team.save() - log_action('org_set_team_description', orgname, {'team': teamname, 'description': team.description}) + log_action('org_set_team_description', orgname, + {'team': teamname, 'description': team.description}) if 'role' in details: role = model.get_team_org_role(team).name if role != details['role']: team = model.set_team_org_permission(team, details['role'], current_user.db_user().username) - log_action('org_set_team_role', orgname, {'team': teamname, 'role': details['role']}) + log_action('org_set_team_role', orgname, + {'team': teamname, 'role': details['role']}) resp = jsonify(team_view(orgname, team)) if not is_existing: @@ -629,7 +632,8 @@ def update_organization_team_member(orgname, teamname, membername): # Add the user to the team. model.add_user_to_team(user, team) - log_action('org_add_team_member', orgname, {'member': membername, 'team': teamname}) + log_action('org_add_team_member', orgname, + {'member': membername, 'team': teamname}) return jsonify(member_view(user)) abort(403) @@ -644,7 +648,8 @@ def delete_organization_team_member(orgname, teamname, membername): # Remote the user from the team. invoking_user = current_user.db_user().username model.remove_user_from_team(orgname, teamname, membername, invoking_user) - log_action('org_remove_team_member', orgname, {'member': membername, 'team': teamname}) + log_action('org_remove_team_member', orgname, + {'member': membername, 'team': teamname}) return make_response('Deleted', 204) abort(403) @@ -673,7 +678,9 @@ def create_repo_api(): repo.description = req['description'] repo.save() - log_action('create_repo', namespace_name, {'repo': repository_name, 'namespace': namespace_name}, repo=repo) + log_action('create_repo', namespace_name, + {'repo': repository_name, 'namespace': namespace_name}, + repo=repo) return jsonify({ 'namespace': namespace_name, 'name': repository_name @@ -758,7 +765,8 @@ def update_repo_api(namespace, repository): repo.description = values['description'] repo.save() - log_action('set_repo_description', namespace, {'repo': repository, 'description': values['description']}, + log_action('set_repo_description', namespace, + {'repo': repository, 'description': values['description']}, repo=repo) return jsonify({ 'success': True @@ -778,7 +786,8 @@ def change_repo_visibility_api(namespace, repository): if repo: values = request.get_json() model.set_repository_visibility(repo, values['visibility']) - log_action('change_repo_visibility', namespace, {'repo': repository, 'visibility': values['visibility']}, + log_action('change_repo_visibility', namespace, + {'repo': repository, 'visibility': values['visibility']}, repo=repo) return jsonify({ 'success': True @@ -795,7 +804,8 @@ def delete_repository(namespace, repository): if permission.can(): model.purge_repository(namespace, repository) registry.delete_repository_storage(namespace, repository) - log_action('delete_repo', namespace, {'repo': repository, 'namespace': namespace}) + log_action('delete_repo', namespace, + {'repo': repository, 'namespace': namespace}) return make_response('Deleted', 204) abort(403) @@ -912,7 +922,8 @@ def request_repo_build(namespace, repository): dockerfile_build_queue.put(json.dumps({'build_id': build_request.id})) log_action('build_dockerfile', namespace, - {'repo': repository, 'namespace': namespace, 'fileid': dockerfile_id}, repo=repo) + {'repo': repository, 'namespace': namespace, + 'fileid': dockerfile_id}, repo=repo) resp = jsonify({ 'started': True @@ -943,7 +954,8 @@ def create_webhook(namespace, repository): resp.headers['Location'] = url_for('get_webhook', repository=repo_string, public_id=webhook.public_id) log_action('add_repo_webhook', namespace, - {'repo': repository, 'webhook_id': webhook.public_id}, repo=repo) + {'repo': repository, 'webhook_id': webhook.public_id}, + repo=repo) return resp abort(403) # Permissions denied @@ -1143,7 +1155,8 @@ def list_repo_user_permissions(namespace, repository): current_func = role_view_func def wrapped_role_org_view(repo_perm): - return wrap_role_view_org(current_func(repo_perm), repo_perm.user, org_members) + return wrap_role_view_org(current_func(repo_perm), repo_perm.user, + org_members) role_view_func = wrapped_role_org_view @@ -1228,7 +1241,8 @@ def change_user_permissions(namespace, repository, username): return error_resp log_action('change_repo_permission', namespace, - {'username': username, 'repo': repository, 'role': new_permission['role']}, + {'username': username, 'repo': repository, + 'role': new_permission['role']}, repo=model.get_repository(namespace, repository)) resp = jsonify(perm_view) @@ -1255,7 +1269,8 @@ def change_team_permissions(namespace, repository, teamname): new_permission['role']) log_action('change_repo_permission', namespace, - {'team': teamname, 'repo': repository, 'role': new_permission['role']}, + {'team': teamname, 'repo': repository, + 'role': new_permission['role']}, repo=model.get_repository(namespace, repository)) resp = jsonify(role_view(perm)) @@ -1282,7 +1297,8 @@ def delete_user_permissions(namespace, repository, username): error_resp.status_code = 400 return error_resp - log_action('delete_repo_permission', namespace, {'username': username, 'repo': repository}, + log_action('delete_repo_permission', namespace, + {'username': username, 'repo': repository}, repo=model.get_repository(namespace, repository)) return make_response('Deleted', 204) @@ -1299,7 +1315,8 @@ def delete_team_permissions(namespace, repository, teamname): if permission.can(): model.delete_team_permission(teamname, namespace, repository) - log_action('delete_repo_permission', namespace, {'team': teamname, 'repo': repository}, + log_action('delete_repo_permission', namespace, + {'team': teamname, 'repo': repository}, repo=model.get_repository(namespace, repository)) return make_response('Deleted', 204) @@ -1353,7 +1370,8 @@ def create_token(namespace, repository): token = model.create_delegate_token(namespace, repository, token_params['friendlyName']) - log_action('add_repo_accesstoken', namespace, {'repo': repository, 'token': token_params['friendlyName']}, + log_action('add_repo_accesstoken', namespace, + {'repo': repository, 'token': token_params['friendlyName']}, repo = model.get_repository(namespace, repository)) resp = jsonify(token_view(token)) @@ -1378,7 +1396,8 @@ def change_token(namespace, repository, code): new_permission['role']) log_action('change_repo_permission', namespace, - {'repo': repository, 'token': token.friendly_name, 'code': code, 'role': new_permission['role']}, + {'repo': repository, 'token': token.friendly_name, 'code': code, + 'role': new_permission['role']}, repo = model.get_repository(namespace, repository)) resp = jsonify(token_view(token)) @@ -1397,7 +1416,8 @@ def delete_token(namespace, repository, code): token = model.delete_delegate_token(namespace, repository, code) log_action('delete_repo_accesstoken', namespace, - {'repo': repository, 'token': token.friendly_name, 'code': code}, + {'repo': repository, 'token': token.friendly_name, + 'code': code}, repo = model.get_repository(namespace, repository)) return make_response('Deleted', 204) @@ -1486,9 +1506,9 @@ def get_card(user): if default_card: card_info = { - 'owner': card.name, - 'type': card.type, - 'last4': card.last4 + 'owner': default_card.name, + 'type': default_card.type, + 'last4': default_card.last4 } return jsonify({'card': card_info}) @@ -1500,7 +1520,7 @@ def subscribe_api(): plan = request_data['plan'] token = request_data['token'] if 'token' in request_data else None user = current_user.db_user() - return subscribe(user, plan, token, USER_PLANS) + return subscribe(user, plan, token, PLANS) def carderror_response(e): @@ -1511,15 +1531,18 @@ def carderror_response(e): return resp -def subscribe(user, plan, token, accepted_plans): +def subscribe(user, plan, token, require_business_plan): plan_found = None - for plan_obj in accepted_plans: + for plan_obj in PLANS: if plan_obj['stripeId'] == plan: plan_found = plan_obj if not plan_found: abort(404) + if require_business_plan and not plan_found['bus_features']: + abort(404) + private_repos = model.get_private_repo_count(user.username) # This is the default response @@ -1619,7 +1642,7 @@ def subscribe_org_api(orgname): plan = request_data['plan'] token = request_data['token'] if 'token' in request_data else None organization = model.get_organization(orgname) - return subscribe(organization, plan, token, BUSINESS_PLANS) + return subscribe(organization, plan, token, PLANS) abort(403) @@ -1743,20 +1766,20 @@ def delete_org_robot(orgname, robot_shortname): def log_view(log): - view = { - 'kind': log.kind.name, - 'metadata': json.loads(log.metadata_json), - 'ip': log.ip, - 'datetime': log.datetime, + view = { + 'kind': log.kind.name, + 'metadata': json.loads(log.metadata_json), + 'ip': log.ip, + 'datetime': log.datetime, + } + + if log.performer: + view['performer'] = { + 'username': log.performer.username, + 'is_robot': log.performer.robot, } - if log.performer: - view['performer'] = { - 'username': log.performer.username, - 'is_robot': log.performer.robot, - } - - return view + return view @@ -1786,12 +1809,14 @@ def org_logs_api(orgname): start_time = request.args.get('starttime', None) end_time = request.args.get('endtime', None) - return get_logs(orgname, start_time, end_time, performer_name=performer_name) + return get_logs(orgname, start_time, end_time, + performer_name=performer_name) abort(403) -def get_logs(namespace, start_time, end_time, performer_name=None, repository=None): +def get_logs(namespace, start_time, end_time, performer_name=None, + repository=None): performer = None if performer_name: performer = model.get_user(performer_name) @@ -1815,7 +1840,8 @@ def get_logs(namespace, start_time, end_time, performer_name=None, repository=No if not end_time: end_time = datetime.today() - logs = model.list_logs(namespace, start_time, end_time, performer = performer, repository=repository) + logs = model.list_logs(namespace, start_time, end_time, performer=performer, + repository=repository) return jsonify({ 'start_time': start_time, 'end_time': end_time, diff --git a/endpoints/webhooks.py b/endpoints/webhooks.py index 9675fe000..f93ef7a70 100644 --- a/endpoints/webhooks.py +++ b/endpoints/webhooks.py @@ -1,19 +1,14 @@ import logging -import requests import stripe -from flask import (abort, redirect, request, url_for, render_template, - make_response) -from flask.ext.login import login_user, UserMixin, login_required -from flask.ext.principal import identity_changed, Identity, AnonymousIdentity +from flask import request, make_response from data import model -from app import app, login_manager, mixpanel -from auth.permissions import QuayDeferredPermissionUser -from data.plans import USER_PLANS, BUSINESS_PLANS, get_plan +from app import app from util.invoice import renderInvoiceToHtml from util.email import send_invoice_email + logger = logging.getLogger(__name__) @@ -33,10 +28,10 @@ def stripe_webhook(): # Find the user associated with the customer ID. user = model.get_user_or_org_by_customer_id(customer_id) if user and user.invoice_email: - # Lookup the invoice. - invoice = stripe.Invoice.retrieve(invoice_id) - if invoice: - invoice_html = renderInvoiceToHtml(invoice, user) - send_invoice_email(user.email, invoice_html) + # Lookup the invoice. + invoice = stripe.Invoice.retrieve(invoice_id) + if invoice: + invoice_html = renderInvoiceToHtml(invoice, user) + send_invoice_email(user.email, invoice_html) return make_response('Okay')