Merge remote-tracking branch 'origin/master' into swaggerlikeus
Conflicts: data/database.py endpoints/api.py endpoints/common.py templates/base.html test/data/test.db test/specs.py
This commit is contained in:
commit
c93c62600d
59 changed files with 4636 additions and 216 deletions
|
@ -18,9 +18,23 @@ user_files = app.config['USERFILES']
|
|||
build_logs = app.config['BUILDLOGS']
|
||||
|
||||
|
||||
def get_trigger_config(trigger):
|
||||
try:
|
||||
return json.loads(trigger.config)
|
||||
except:
|
||||
return {}
|
||||
|
||||
|
||||
def get_job_config(build_obj):
|
||||
try:
|
||||
return json.loads(build_obj.job_config)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def trigger_view(trigger):
|
||||
if trigger and trigger.uuid:
|
||||
config_dict = json.loads(trigger.config)
|
||||
config_dict = get_trigger_config(trigger)
|
||||
build_trigger = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
return {
|
||||
'service': trigger.service.name,
|
||||
|
@ -42,7 +56,7 @@ def build_status_view(build_obj, can_write=False):
|
|||
'started': format_date(build_obj.started),
|
||||
'display_name': build_obj.display_name,
|
||||
'status': status,
|
||||
'job_config': json.loads(build_obj.job_config) if can_write else None,
|
||||
'job_config': get_job_config(build_obj) if can_write else None,
|
||||
'is_writer': can_write,
|
||||
'trigger': trigger_view(build_obj.trigger),
|
||||
'resource_key': build_obj.resource_key,
|
||||
|
@ -54,7 +68,7 @@ def build_status_view(build_obj, can_write=False):
|
|||
return resp
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/build/')
|
||||
@resource('/v1/repository/<repopath:repository>/build/')
|
||||
class RepositoryBuildList(RepositoryParamResource):
|
||||
""" Resource related to creating and listing repository builds. """
|
||||
schemas = {
|
||||
|
@ -127,7 +141,7 @@ class RepositoryBuildList(RepositoryParamResource):
|
|||
return resp, 201, headers
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/build/<build_uuid>/status')
|
||||
@resource('/v1/repository/<repopath:repository>/build/<build_uuid>/status')
|
||||
class RepositoryBuildStatus(RepositoryParamResource):
|
||||
""" Resource for dealing with repository build status. """
|
||||
@require_repo_read
|
||||
|
@ -142,7 +156,7 @@ class RepositoryBuildStatus(RepositoryParamResource):
|
|||
return build_status_view(build, can_write)
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/build/<build_uuid>/logs')
|
||||
@resource('/v1/repository/<repopath:repository>/build/<build_uuid>/logs')
|
||||
class RepositoryBuildLogs(RepositoryParamResource):
|
||||
""" Resource for loading repository build logs. """
|
||||
@require_repo_write
|
||||
|
|
|
@ -29,7 +29,7 @@ def image_view(image):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/image/')
|
||||
@resource('/v1/repository/<repopath:repository>/image/')
|
||||
class RepositoryImageList(RepositoryParamResource):
|
||||
""" Resource for listing repository images. """
|
||||
@require_repo_read
|
||||
|
@ -54,7 +54,7 @@ class RepositoryImageList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/image/<image_id>')
|
||||
@resource('/v1/repository/<repopath:repository>/image/<image_id>')
|
||||
class RepositoryImage(RepositoryParamResource):
|
||||
""" Resource for handling repository images. """
|
||||
@require_repo_read
|
||||
|
@ -68,7 +68,7 @@ class RepositoryImage(RepositoryParamResource):
|
|||
return image_view(image)
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/image/<image_id>/changes')
|
||||
@resource('/v1/repository/<repopath:repository>/image/<image_id>/changes')
|
||||
class RepositoryImageChanges(RepositoryParamResource):
|
||||
""" Resource for handling repository image change lists. """
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ def get_logs(namespace, start_time, end_time, performer_name=None,
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/logs')
|
||||
@resource('/v1/repository/<repopath:repository>/logs')
|
||||
@internal_only
|
||||
class RepositoryLogs(RepositoryParamResource):
|
||||
""" Resource for fetching logs for the specific repository. """
|
||||
|
|
|
@ -25,7 +25,7 @@ def wrap_role_view_org(role_json, user, org_members):
|
|||
return role_json
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/permissions/team/')
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/team/')
|
||||
class RepositoryTeamPermissionList(RepositoryParamResource):
|
||||
""" Resource for repository team permissions. """
|
||||
@require_repo_admin
|
||||
|
@ -40,7 +40,7 @@ class RepositoryTeamPermissionList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/permissions/user/')
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/user/')
|
||||
class RepositoryUserPermissionList(RepositoryParamResource):
|
||||
""" Resource for repository user permissions. """
|
||||
@require_repo_admin
|
||||
|
@ -79,7 +79,7 @@ class RepositoryUserPermissionList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/permissions/user/<username>')
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/user/<username>')
|
||||
class RepositoryUserPermission(RepositoryParamResource):
|
||||
""" Resource for managing individual user permissions. """
|
||||
schemas = {
|
||||
|
@ -174,7 +174,7 @@ class RepositoryUserPermission(RepositoryParamResource):
|
|||
return 'Deleted', 204
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/permissions/team/<teamname>')
|
||||
@resource('/v1/repository/<repopath:repository>/permissions/team/<teamname>')
|
||||
class RepositoryTeamPermission(RepositoryParamResource):
|
||||
""" Resource for managing individual team permissions. """
|
||||
schemas = {
|
||||
|
|
|
@ -152,7 +152,7 @@ def image_view(image):
|
|||
'size': extended_props.image_size,
|
||||
}
|
||||
|
||||
@resource('/v1/repository/<path:repository>')
|
||||
@resource('/v1/repository/<repopath:repository>')
|
||||
class Repository(RepositoryParamResource):
|
||||
"""Operations for managing a specific repository."""
|
||||
schemas = {
|
||||
|
@ -248,7 +248,7 @@ class Repository(RepositoryParamResource):
|
|||
return 'Deleted', 204
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/changevisibility')
|
||||
@resource('/v1/repository/<repopath:repository>/changevisibility')
|
||||
class RepositoryVisibility(RepositoryParamResource):
|
||||
""" Custom verb for changing the visibility of the repository. """
|
||||
schemas = {
|
||||
|
|
|
@ -18,7 +18,7 @@ def token_view(token_obj):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/tokens/')
|
||||
@resource('/v1/repository/<repopath:repository>/tokens/')
|
||||
class RepositoryTokenList(RepositoryParamResource):
|
||||
""" Resource for creating and listing repository tokens. """
|
||||
schemas = {
|
||||
|
@ -65,7 +65,7 @@ class RepositoryTokenList(RepositoryParamResource):
|
|||
return token_view(token), 201
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/tokens/<code>')
|
||||
@resource('/v1/repository/<repopath:repository>/tokens/<code>')
|
||||
class RepositoryToken(RepositoryParamResource):
|
||||
""" Resource for managing individual tokens. """
|
||||
schemas = {
|
||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
|||
import stripe
|
||||
|
||||
from endpoints.api import request_error, log_action, NotFound
|
||||
from endpoints.common import check_repository_usage
|
||||
from data import model
|
||||
from data.plans import PLANS
|
||||
|
||||
|
@ -58,6 +59,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)
|
||||
|
@ -74,6 +76,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:
|
||||
|
@ -89,6 +92,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})
|
||||
|
||||
return response_json, status_code
|
||||
|
|
|
@ -5,7 +5,7 @@ from data import model
|
|||
from auth.auth_context import get_authenticated_user
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/tag/<tag>')
|
||||
@resource('/v1/repository/<repopath:repository>/tag/<tag>')
|
||||
class RepositoryTag(RepositoryParamResource):
|
||||
""" Resource for managing repository tags. """
|
||||
|
||||
|
@ -24,7 +24,7 @@ class RepositoryTag(RepositoryParamResource):
|
|||
return 'Deleted', 204
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/tag/<tag>/images')
|
||||
@resource('/v1/repository/<repopath:repository>/tag/<tag>/images')
|
||||
class RepositoryTagImages(RepositoryParamResource):
|
||||
""" Resource for listing the images in a specific repository tag. """
|
||||
@require_repo_read
|
||||
|
|
|
@ -9,7 +9,8 @@ from app import app
|
|||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||
log_action, request_error, query_param, parse_args, internal_only,
|
||||
validate_json_request, api, Unauthorized, NotFound, InvalidRequest)
|
||||
from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus
|
||||
from endpoints.api.build import (build_status_view, trigger_view, RepositoryBuildStatus,
|
||||
get_trigger_config)
|
||||
from endpoints.common import start_build
|
||||
from endpoints.trigger import (BuildTrigger as BuildTriggerBase, TriggerDeactivationException,
|
||||
TriggerActivationException, EmptyRepositoryException)
|
||||
|
@ -25,7 +26,7 @@ def _prepare_webhook_url(scheme, username, password, hostname, path):
|
|||
return urlunparse((scheme, auth_hostname, path, '', '', ''))
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/')
|
||||
class BuildTriggerList(RepositoryParamResource):
|
||||
""" Resource for listing repository build triggers. """
|
||||
|
||||
|
@ -39,7 +40,7 @@ class BuildTriggerList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>')
|
||||
class BuildTrigger(RepositoryParamResource):
|
||||
""" Resource for managing specific build triggers. """
|
||||
|
||||
|
@ -64,7 +65,7 @@ class BuildTrigger(RepositoryParamResource):
|
|||
raise NotFound()
|
||||
|
||||
handler = BuildTriggerBase.get_trigger_for_service(trigger.service.name)
|
||||
config_dict = json.loads(trigger.config)
|
||||
config_dict = get_trigger_config(trigger)
|
||||
if handler.is_active(config_dict):
|
||||
try:
|
||||
handler.deactivate(trigger.auth_token, config_dict)
|
||||
|
@ -81,7 +82,7 @@ class BuildTrigger(RepositoryParamResource):
|
|||
return 'No Content', 204
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/subdir')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/subdir')
|
||||
@internal_only
|
||||
class BuildTriggerSubdirs(RepositoryParamResource):
|
||||
""" Custom verb for fetching the subdirs which are buildable for a trigger. """
|
||||
|
@ -123,7 +124,7 @@ class BuildTriggerSubdirs(RepositoryParamResource):
|
|||
raise Unauthorized()
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/activate')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/activate')
|
||||
@internal_only
|
||||
class BuildTriggerActivate(RepositoryParamResource):
|
||||
""" Custom verb for activating a build trigger once all required information has been collected.
|
||||
|
@ -147,7 +148,7 @@ class BuildTriggerActivate(RepositoryParamResource):
|
|||
raise NotFound()
|
||||
|
||||
handler = BuildTriggerBase.get_trigger_for_service(trigger.service.name)
|
||||
existing_config_dict = json.loads(trigger.config)
|
||||
existing_config_dict = get_trigger_config(trigger)
|
||||
if handler.is_active(existing_config_dict):
|
||||
raise InvalidRequest('Trigger config is not sufficient for activation.')
|
||||
|
||||
|
@ -191,7 +192,7 @@ class BuildTriggerActivate(RepositoryParamResource):
|
|||
raise Unauthorized()
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/start')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/start')
|
||||
class ActivateBuildTrigger(RepositoryParamResource):
|
||||
""" Custom verb to manually activate a build trigger. """
|
||||
|
||||
|
@ -205,11 +206,11 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
raise NotFound()
|
||||
|
||||
handler = BuildTriggerBase.get_trigger_for_service(trigger.service.name)
|
||||
existing_config_dict = json.loads(trigger.config)
|
||||
if not handler.is_active(existing_config_dict):
|
||||
config_dict = get_trigger_config(trigger)
|
||||
if not handler.is_active(config_dict):
|
||||
raise InvalidRequest('Trigger is not active.')
|
||||
|
||||
specs = handler.manual_start(trigger.auth_token, json.loads(trigger.config))
|
||||
specs = handler.manual_start(trigger.auth_token, config_dict)
|
||||
dockerfile_id, tags, name, subdir = specs
|
||||
|
||||
repo = model.get_repository(namespace, repository)
|
||||
|
@ -225,7 +226,7 @@ class ActivateBuildTrigger(RepositoryParamResource):
|
|||
return resp, 201, headers
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/builds')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/builds')
|
||||
class TriggerBuildList(RepositoryParamResource):
|
||||
""" Resource to represent builds that were activated from the specified trigger. """
|
||||
@require_repo_admin
|
||||
|
@ -242,7 +243,7 @@ class TriggerBuildList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/sources')
|
||||
@resource('/v1/repository/<repopath:repository>/trigger/<trigger_uuid>/sources')
|
||||
@internal_only
|
||||
class BuildTriggerSources(RepositoryParamResource):
|
||||
""" Custom verb to fetch the list of build sources for the trigger config. """
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import stripe
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
from flask.ext.login import logout_user
|
||||
|
@ -8,7 +9,7 @@ from flask.ext.principal import identity_changed, AnonymousIdentity
|
|||
from app import app
|
||||
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
|
||||
log_action, internal_only, NotFound, Unauthorized, require_user_admin,
|
||||
require_user_read, InvalidToken, require_scope)
|
||||
require_user_read, InvalidToken, require_scope, format_date)
|
||||
from endpoints.api.subscribe import subscribe
|
||||
from endpoints.common import common_login
|
||||
from data import model
|
||||
|
@ -60,6 +61,15 @@ def user_view(user):
|
|||
}
|
||||
|
||||
|
||||
def notification_view(notification):
|
||||
return {
|
||||
'organization': notification.target.username if notification.target.organization else None,
|
||||
'kind': notification.kind.name,
|
||||
'created': format_date(notification.created),
|
||||
'metadata': json.loads(notification.metadata_json),
|
||||
}
|
||||
|
||||
|
||||
@resource('/v1/user/')
|
||||
class User(ApiResource):
|
||||
""" Operations related to users. """
|
||||
|
@ -364,3 +374,15 @@ class Recovery(ApiResource):
|
|||
code = model.create_reset_password_email_code(email)
|
||||
send_recovery_email(email, code.code)
|
||||
return 'Created', 201
|
||||
|
||||
|
||||
@resource('/v1/user/notifications')
|
||||
@internal_only
|
||||
class UserNotificationList(ApiResource):
|
||||
@require_user_admin
|
||||
@nickname('listUserNotifications')
|
||||
def get(self):
|
||||
notifications = model.list_notifications(get_authenticated_user())
|
||||
return {
|
||||
'notifications': [notification_view(notification) for notification in notifications]
|
||||
}
|
|
@ -14,7 +14,7 @@ def webhook_view(webhook):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/webhook/')
|
||||
@resource('/v1/repository/<repopath:repository>/webhook/')
|
||||
class WebhookList(RepositoryParamResource):
|
||||
""" Resource for dealing with listing and creating webhooks. """
|
||||
schemas = {
|
||||
|
@ -52,7 +52,7 @@ class WebhookList(RepositoryParamResource):
|
|||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/webhook/<public_id>')
|
||||
@resource('/v1/repository/<repopath:repository>/webhook/<public_id>')
|
||||
class Webhook(RepositoryParamResource):
|
||||
""" Resource for dealing with specific webhooks. """
|
||||
@require_repo_admin
|
||||
|
|
Reference in a new issue