def get(self, author_id): if author_id == 'me' and not get_current_user(): return error('Must be logged in to ask about yourself', http_code=401) authors = Author.find(author_id, get_current_user()) if not authors: return self.respond([]) args = self.get_parser.parse_args() # serialize everything when fetching so that we batch any needed data # fetching. we'll still rearrange things later # grab recent revisions by author (for any repository/project, which # means we can't use vcs commands) sources = self.serialize( list( Source.query.options(joinedload('revision')). join(Revision, Source.revision_sha == Revision.sha).filter( # Repository matching not required for correctness, but # enables an index hit that improves performance significantly. Revision.repository_id == Source.repository_id, Revision.author_id.in_([a.id for a in authors]), Source.patch_id.is_(None), ).order_by(Revision.date_committed.desc(), ).limit( args.num_revs))) if not sources: return self.respond(sources) # grab builds for those revisions commit_builds_list = self.serialize( list( Build.query.options( joinedload('project'), joinedload('author'), ).filter(Build.source_id.in_([s['id'] for s in sources]), ).order_by( Build.date_created.desc(), Build.date_started.desc()))) # move builds into sources builds_map = defaultdict(list) for build in commit_builds_list: builds_map[build['source']['id']].append(build) for source in sources: source['builds'] = builds_map[source['id']] return self.respond(sources, serialize=False)
def get(self, author_id): if author_id == 'me' and not get_current_user(): return error('Must be logged in to ask about yourself', http_code=401) authors = Author.find(author_id, get_current_user()) if not authors: return self.respond([]) try: author_email = authors[0].email request = PhabricatorClient() request.connect() user_info = request.call('user.query', {'emails': [author_email]}) if not user_info: return 'phabricator: %s not found' % author_email, 404 author_phid = user_info[0]["phid"] diff_info = request.call('differential.query', { 'authors': [author_phid], 'status': "status-open" }) diff_info.sort(key=lambda k: -1 * int(k['dateModified'])) except requests.exceptions.ConnectionError: return 'Unable to connect to Phabricator', 503 if not diff_info: # No diffs, no point in trying to find builds. return self.respond([]) rows = list( db.session.query(PhabricatorDiff, Build).join( Build, Build.source_id == PhabricatorDiff.source_id).filter( PhabricatorDiff.revision_id.in_( [d['id'] for d in diff_info]))) serialized_builds = zip(self.serialize([row.Build for row in rows]), [row.PhabricatorDiff for row in rows]) builds_map = defaultdict(list) for build, phabricator_diff in serialized_builds: builds_map[str(phabricator_diff.revision_id)].append(build) for d in diff_info: d['builds'] = builds_map[str(d['id'])] return self.respond(diff_info)
def get(self): """ Returns basic information used by every page: - is the user authenticated? - user messages """ # authentication user = get_current_user() auth = { 'authenticated': False, } if user: auth = { 'authenticated': True, 'user': user, } # admin message messages = list(AdminMessage.query.options(joinedload('user'), )) admin_message = None if messages: if len(messages) > 1: # In the future we may have more than one message logging.warning('Multiple messages found') else: admin_message = messages[0] return self.respond({'auth': auth, 'admin_message': admin_message})
def post(self): user = get_current_user() if user is None: return error('Not logged in.', http_code=401) args = self.post_parser.parse_args() slug = str(args.slug or args.name.replace(' ', '-').lower()) if not user_has_project_permission(user, slug): return error('User does not have permission to create a project with slug {}.'.format(slug), http_code=403) match = Project.query.filter( Project.slug == slug, ).first() if match: return error('Project with slug {} already exists.'.format(slug), http_code=400) repository = Repository.get(args.repository) if repository is None: return error('Repository with url {} does not exist.'.format(args.repository), http_code=400) project = Project( name=args.name, slug=slug, repository=repository, ) db.session.add(project) db.session.commit() return self.respond(project)
def post(self, node_id): args = self.get_parser.parse_args() if not args.toggle: return self.get(node_id) node = Node.query.get(node_id) if node is None: return error('Node not found.', ['node_id'], 404) if not node.label: return error('Node does not contain a label.', ['node_id'], 404) user = get_current_user() if user is None: return error('User is not logged in.', ['user'], 401) master = self.get_master(node_id) if not master: return error('Node master not found.', ['node_id'], 404) toggle_url = '%s/toggleOffline' % (self.get_jenkins_url( master, node.label)) timestamp = datetime.utcnow() data = { 'offlineMessage': '[changes] Disabled by %s at %s' % (user.email, timestamp) } response = requests.Session().post(toggle_url, data=data, timeout=10) if response.status_code != 200: logging.warning('Unable to toggle offline status (%s)' % (toggle_url)) return self.respond_status(node, master)
def post(self): user = get_current_user() if user is None: return error('Not logged in.', http_code=401) args = self.post_parser.parse_args() slug = str(args.slug or args.name.replace(' ', '-').lower()) if not user_has_project_permission(user, slug): return error( 'User does not have permission to create a project with slug {}.' .format(slug), http_code=403) match = Project.query.filter(Project.slug == slug, ).first() if match: return error('Project with slug {} already exists.'.format(slug), http_code=400) repository = Repository.get(args.repository) if repository is None: return error('Repository with url {} does not exist.'.format( args.repository), http_code=400) project = Project( name=args.name, slug=slug, repository=repository, ) db.session.add(project) db.session.commit() return self.respond(project)
def get(self, author_id): authors = Author.find(author_id, get_current_user()) if not authors and author_id == 'me': return '''Either there is no current user or you are not in the author table''', 401 elif not authors: return 'author not found', 404 try: author_email = authors[0].email request = PhabricatorRequest() request.connect() user_info = request.call('user.query', {'emails': [author_email]}) if not user_info: return 'phabricator: %s not found' % author_email, 404 author_phid = user_info[0]["phid"] diff_info = request.call('differential.query', { 'authors': [author_phid], 'status': "status-open" }) diff_info.sort(key=lambda k: -1 * int(k['dateModified'])) return diff_info except requests.exceptions.ConnectionError: return 'Unable to connect to Phabricator', 503
def post(self, node_id): args = self.get_parser.parse_args() if not args.toggle: return self.get(node_id) node = Node.query.get(node_id) if node is None: return error('Node not found.', ['node_id'], 404) if not node.label: return error('Node does not contain a label.', ['node_id'], 404) user = get_current_user() if user is None: return error('User is not logged in.', ['user'], 401) master = self.get_master(node_id) if not master: return error('Node master not found.', ['node_id'], 404) toggle_url = '%s/toggleOffline' % (self.get_jenkins_url(master, node.label)) timestamp = datetime.utcnow() data = { 'offlineMessage': '[changes] Disabled by %s at %s' % (user.email, timestamp) } response = requests.Session().post(toggle_url, data=data) if response.status_code != 200: logging.warning('Unable to toggle offline status (%s)' % (toggle_url)) return self.respond_status(node, master)
def get(self, author_id): if author_id == 'me' and not get_current_user(): return error('Must be logged in to ask about yourself', http_code=401) authors = Author.find(author_id, get_current_user()) if not authors: return self.respond([]) queryset = Build.query.options( joinedload('project'), joinedload('author'), joinedload('source').joinedload('revision'), ).filter( Build.author_id.in_([a.id for a in authors]) ).order_by(Build.date_created.desc(), Build.date_started.desc()) return self.paginate(queryset)
def get(self, path=''): # get user options, e.g. colorblind current_user = get_current_user() user_options = {} if current_user: user_options = dict( db.session.query( ItemOption.name, ItemOption.value, ).filter(ItemOption.item_id == current_user.id, )) statsreporter.stats().incr('homepage_view') if current_app.config['SENTRY_DSN'] and False: parsed = urlparse.urlparse(current_app.config['SENTRY_DSN']) dsn = '%s://%s@%s/%s' % ( parsed.scheme.rsplit('+', 1)[-1], parsed.username, parsed.hostname + (':%s' % (parsed.port, ) if parsed.port else ''), parsed.path, ) else: dsn = None # variables to ship down to the webapp use_another_host = current_app.config['WEBAPP_USE_ANOTHER_HOST'] # if we have custom js, embed it in the html (making sure we # only do one file read in prod). fetch_custom_js = (current_app.config['WEBAPP_CUSTOM_JS'] and (current_app.debug or not IndexView.custom_js)) if fetch_custom_js: IndexView.custom_js = open( current_app.config['WEBAPP_CUSTOM_JS']).read() disable_custom = request.args and "disable_custom" in request.args return render_template( 'webapp.html', **{ 'SENTRY_PUBLIC_DSN': dsn, 'RELEASE_INFO': changes.get_revision_info(), 'WEBAPP_USE_ANOTHER_HOST': use_another_host, 'WEBAPP_CUSTOM_JS': (IndexView.custom_js if not disable_custom else None), 'USE_PACKAGED_JS': not current_app.debug, 'HAS_CUSTOM_CSS': (current_app.config['WEBAPP_CUSTOM_CSS'] and not disable_custom), 'IS_DEBUG': current_app.debug, 'PHABRICATOR_LINK_HOST': current_app.config['PHABRICATOR_LINK_HOST'], 'COLORBLIND': (user_options.get('user.colorblind') and user_options.get('user.colorblind') != '0'), })
def _get_author(self, author_id): if author_id == 'me': user = get_current_user() if user is None: return return Author.query.filter_by(email=user.email).first() return Author.query.get(author_id)
def capture_user(*args, **kwargs): from changes.api.auth import get_current_user user = get_current_user() if user is not None: sentry.client.user_context({ 'id': user.id, 'email': user.email, })
def get(self, author_id): if author_id == 'me' and not get_current_user(): return error('Must be logged in to ask about yourself', http_code=401) authors = Author.find(author_id, get_current_user()) if not authors: return self.respond([]) queryset = Build.query.options( joinedload('project'), joinedload('author'), joinedload('source').joinedload('revision'), ).filter(Build.author_id.in_([a.id for a in authors ])).order_by(Build.date_created.desc(), Build.date_started.desc()) return self.paginate(queryset)
def get(self, path=''): # get user options, e.g. colorblind current_user = get_current_user() user_options = {} if current_user: user_options = dict(db.session.query( ItemOption.name, ItemOption.value, ).filter( ItemOption.item_id == current_user.id, )) statsreporter.stats().incr('homepage_view') if current_app.config['SENTRY_DSN'] and False: parsed = urlparse.urlparse(current_app.config['SENTRY_DSN']) dsn = '%s://%s@%s/%s' % ( parsed.scheme.rsplit('+', 1)[-1], parsed.username, parsed.hostname + (':%s' % (parsed.port,) if parsed.port else ''), parsed.path, ) else: dsn = None # variables to ship down to the webapp use_another_host = current_app.config['WEBAPP_USE_ANOTHER_HOST'] # if we have custom js, embed it in the html (making sure we # only do one file read in prod). fetch_custom_js = (current_app.config['WEBAPP_CUSTOM_JS'] and (current_app.debug or not IndexView.custom_js)) if fetch_custom_js: IndexView.custom_js = open(current_app.config['WEBAPP_CUSTOM_JS']).read() disable_custom = request.args and "disable_custom" in request.args # use new react code if self.use_v2: return render_template('webapp.html', **{ 'SENTRY_PUBLIC_DSN': dsn, 'RELEASE_INFO': changes.get_revision_info(), 'WEBAPP_USE_ANOTHER_HOST': use_another_host, 'WEBAPP_CUSTOM_JS': (IndexView.custom_js if not disable_custom else None), 'USE_PACKAGED_JS': not current_app.debug, 'HAS_CUSTOM_CSS': (current_app.config['WEBAPP_CUSTOM_CSS'] and not disable_custom), 'IS_DEBUG': current_app.debug, 'PHABRICATOR_HOST': current_app.config['PHABRICATOR_HOST'], 'COLORBLIND': (user_options.get('user.colorblind') and user_options.get('user.colorblind') != '0'), }) return render_template('index.html', **{ 'SENTRY_PUBLIC_DSN': dsn, 'VERSION': changes.get_version(), 'WEBAPP_USE_ANOTHER_HOST': use_another_host })
def get(self, path=""): # get user options, e.g. colorblind current_user = get_current_user() user_options = {} if current_user: user_options = dict( db.session.query(ItemOption.name, ItemOption.value).filter(ItemOption.item_id == current_user.id) ) statsreporter.stats().incr("homepage_view") if current_app.config["SENTRY_DSN"] and False: parsed = urlparse.urlparse(current_app.config["SENTRY_DSN"]) dsn = "%s://%s@%s/%s" % ( parsed.scheme.rsplit("+", 1)[-1], parsed.username, parsed.hostname + (":%s" % (parsed.port,) if parsed.port else ""), parsed.path, ) else: dsn = None # variables to ship down to the webapp use_another_host = current_app.config["WEBAPP_USE_ANOTHER_HOST"] # if we have custom js, embed it in the html (making sure we # only do one file read in prod). fetch_custom_js = current_app.config["WEBAPP_CUSTOM_JS"] and (current_app.debug or not IndexView.custom_js) if fetch_custom_js: IndexView.custom_js = open(current_app.config["WEBAPP_CUSTOM_JS"]).read() disable_custom = request.args and "disable_custom" in request.args # use new react code if self.use_v2: return render_template( "webapp.html", **{ "SENTRY_PUBLIC_DSN": dsn, "RELEASE_INFO": changes.get_revision_info(), "WEBAPP_USE_ANOTHER_HOST": use_another_host, "WEBAPP_CUSTOM_JS": (IndexView.custom_js if not disable_custom else None), "USE_PACKAGED_JS": not current_app.debug, "HAS_CUSTOM_CSS": (current_app.config["WEBAPP_CUSTOM_CSS"] and not disable_custom), "IS_DEBUG": current_app.debug, "PHABRICATOR_LINK_HOST": current_app.config["PHABRICATOR_LINK_HOST"], "COLORBLIND": (user_options.get("user.colorblind") and user_options.get("user.colorblind") != "0"), } ) return render_template( "index.html", **{"SENTRY_PUBLIC_DSN": dsn, "VERSION": changes.get_version(), "WEBAPP_USE_ANOTHER_HOST": use_another_host} )
def get(self, author_id): authors = Author.find(author_id, get_current_user()) if not authors and author_id == 'me': return '''Either there is no current user or you are not in the author table''', 401 elif not authors: return 'author not found', 404 try: author_email = authors[0].email request = PhabricatorRequest() request.connect() user_info = request.call('user.query', {'emails': [author_email]}) if not user_info: return 'phabricator: %s not found' % author_email, 404 author_phid = user_info[0]["phid"] diff_info = request.call('differential.query', { 'authors': [author_phid], 'status': "status-open" }) diff_info.sort(key=lambda k: -1 * int(k['dateModified'])) except requests.exceptions.ConnectionError: return 'Unable to connect to Phabricator', 503 if not diff_info: # No diffs, no point in trying to find builds. return self.respond([]) rows = list(db.session.query( PhabricatorDiff, Build ).join( Build, Build.source_id == PhabricatorDiff.source_id ).filter( PhabricatorDiff.revision_id.in_([d['id'] for d in diff_info]) )) serialized_builds = zip( self.serialize([row.Build for row in rows]), [row.PhabricatorDiff for row in rows] ) builds_map = defaultdict(list) for build, phabricator_diff in serialized_builds: builds_map[str(phabricator_diff.revision_id)].append(build) for d in diff_info: d['builds'] = builds_map[str(d['id'])] return self.respond(diff_info)
def _get_author(self, author_id): if author_id == 'me': user = get_current_user() if user is None: return None return Author.query.filter_by(email=user.email).first() try: author_id = UUID(author_id) except ValueError: return None return Author.query.get(author_id)
def post(self, node_id): args = self.get_parser.parse_args() if not args.toggle: return self.get(node_id) node = Node.query.get(node_id) if node is None: return error('Node not found.', ['node_id'], 404) if not node.label: return error('Node does not contain a label.', ['node_id'], 404) user = get_current_user() if user is None: return error('User is not logged in.', ['user'], 401) jenkins_master = self.get_jenkins_master(node_id) if not jenkins_master: # We are most likely dealing with a Mesos slave here node_hostname = node.label.strip() mesos_master = mesos_lib.get_mesos_master() if not mesos_lib.is_active_slave(mesos_master, node_hostname): return error('Node is currently not active on Mesos master', 400) try: mesos_lib.toggle_node_maintenance_status( mesos_master, node_hostname) except Exception as err: return error('Unable to toggle offline status of node %s: %s' % (node_hostname, err), http_code=500) return self.respond_mesos_status(node, mesos_master) toggle_url = '%s/toggleOffline' % (self.get_jenkins_url( jenkins_master, node.label)) timestamp = datetime.utcnow() data = { 'offlineMessage': '[changes] Disabled by %s at %s' % (user.email, timestamp) } response = requests.Session().post(toggle_url, data=data, timeout=10) if response.status_code != 200: logging.warning('Unable to toggle offline status (%s)' % (toggle_url)) return self.respond_jenkins_status(node, jenkins_master)
def get(self, author_id): authors = Author.find(author_id, get_current_user()) if not authors and author_id == 'me': return '', 401 elif not authors: return '', 404 queryset = Build.query.options( joinedload('project'), joinedload('author'), joinedload('source').joinedload('revision'), ).filter( Build.author_id.in_([a.id for a in authors]) ).order_by(Build.date_created.desc(), Build.date_started.desc()) return self.paginate(queryset)
def post(self, build_id): build = Build.query.get(build_id) if build is None: return '', 404 user = get_current_user() if user is None: # don't do anything if they aren't logged in return self.respond({}) try_create(BuildSeen, where={ 'build_id': build.id, 'user_id': user.id, }) return self.respond({})
def get(self, author_id): if author_id == 'me' and not get_current_user(): return '', 401 author = self._get_author(author_id) if not author: return self.respond([]) queryset = Build.query.options( joinedload('project'), joinedload('author'), joinedload('source').joinedload('revision'), ).filter(Build.author_id == author.id, ).order_by( Build.date_created.desc(), Build.date_started.desc()) return self.paginate(queryset)
def get(self): """ Return information on the currently authenticated user. """ user = get_current_user() if user is None: context = { 'authenticated': False, } else: context = { 'authenticated': True, 'user': user, } return self.respond(context)
def get(self, author_id): if author_id == 'me' and not get_current_user(): return '', 401 author = self._get_author(author_id) if not author: return self.respond([]) queryset = Build.query.options( joinedload('project'), joinedload('author'), joinedload('source').joinedload('revision'), ).filter( Build.author_id == author.id, ).order_by(Build.date_created.desc(), Build.date_started.desc()) return self.paginate(queryset)
def get(self, author_id): authors = Author.find(author_id, get_current_user()) if not authors and author_id == 'me': return '', 401 elif not authors: return '', 404 args = self.get_parser.parse_args() # serialize everything when fetching so that we batch any needed data # fetching. we'll still rearrange things later # grab recent revisions by author (for any repository/project, which # means we can't use vcs commands) sources = self.serialize(list(Source.query.join( Revision, Source.revision_sha == Revision.sha ).filter( Revision.author_id.in_([a.id for a in authors]), Source.patch_id.is_(None), ).order_by( Revision.date_committed.desc(), ).limit(args.num_revs))) # grab builds for those revisions commit_builds_list = self.serialize(list(Build.query.options( joinedload('project'), joinedload('author'), ).filter( Build.source_id.in_([s['id'] for s in sources]), ).order_by( Build.date_created.desc(), Build.date_started.desc() ))) # move builds into sources builds_map = defaultdict(list) revision_list = OrderedDict() for build in commit_builds_list: builds_map[build['source']['id']].append(build) for source in sources: source['builds'] = builds_map[source['id']] return self.respond(sources, serialize=False)
def post(self): user = get_current_user() if user is None: return '', 404 args = self.post_parser.parse_args() for name, value in args.iteritems(): if value is None: continue create_or_update(ItemOption, where={ 'item_id': user.id, 'name': name, }, values={ 'value': value, }) return '', 200
def post(self): user = get_current_user() if user is None: return error("User not found", http_code=404) args = self.post_parser.parse_args() for name, value in args.iteritems(): if value is None: continue create_or_update(ItemOption, where={ 'item_id': user.id, 'name': name, }, values={ 'value': value, }) return self.respond({})
def post(self, node_id): args = self.get_parser.parse_args() if not args.toggle: return self.get(node_id) node = Node.query.get(node_id) if node is None: return error('Node not found.', ['node_id'], 404) if not node.label: return error('Node does not contain a label.', ['node_id'], 404) user = get_current_user() if user is None: return error('User is not logged in.', ['user'], 401) jenkins_master = self.get_jenkins_master(node_id) if not jenkins_master: # We are most likely dealing with a Mesos slave here node_hostname = node.label.strip() mesos_master = mesos_lib.get_mesos_master() if not mesos_lib.is_active_slave(mesos_master, node_hostname): return error('Node is currently not active on Mesos master', 400) try: mesos_lib.toggle_node_maintenance_status(mesos_master, node_hostname) except Exception as err: return error('Unable to toggle offline status of node %s: %s' % (node_hostname, err), http_code=500) return self.respond_mesos_status(node, mesos_master) toggle_url = '%s/toggleOffline' % (self.get_jenkins_url(jenkins_master, node.label)) timestamp = datetime.utcnow() data = { 'offlineMessage': '[changes] Disabled by %s at %s' % (user.email, timestamp) } response = requests.Session().post(toggle_url, data=data, timeout=10) if response.status_code != 200: logging.warning('Unable to toggle offline status (%s)' % (toggle_url)) return self.respond_jenkins_status(node, jenkins_master)
def post(self): """ HTTP POST to create or update AdminMessages. This API enforces that we only ever have at most one message. This will likely change in the future. To clear the current message, post an empty message. Messages cannot be deleted once created. Returns: str: JSON representation of the AdminMessage that was edited. """ args = self.post_parser.parse_args() # Enforce we only ever have a single message message, _ = create_or_update(AdminMessage, where={}, values={ 'message': args.message, 'user_id': get_current_user().id, 'date_created': datetime.utcnow() }) # Response isn't required, but we give one to make testing easier return self.respond(message)
def post(self, build_id): user = get_current_user() if user is None: return '', 401 build = Build.query.get(build_id) if build is None: return '', 404 args = self.parser.parse_args() # TODO(dcramer): ensure this comment wasnt just created comment = Comment( build=build, user=user, text=args.text.strip(), ) db.session.add(comment) # TODO(dcramer): this should send out a notification about a new comment return self.respond(comment)
def get_v2_redirect(self): """ Redirect opted-in users to v2. This won't affect people using the original angular app (since it doesn't need full-page loads to navigate), but covers links from phabricator comments, emails, etc. """ # ignore any explicit optouts if request.args and "optout" in request.args: return None current_user = get_current_user() current_username = (current_user.email.split('@')[0] if current_user else None) if current_username not in current_app.config['NEW_UI_OPTIN_USERS']: return None path = request.path.strip('/') path_pieces = path.split('/') if (path == '' and not request.args): return redirect('/v2/?optin=1') elif (len(path_pieces) == 4 and path_pieces[0] == 'projects' and path_pieces[2] == 'builds'): return redirect('/v2/find_build/%s/' % (path_pieces[3],))
def _get_authors(self, author_id): if author_id == 'me': user = get_current_user() if user is None: return [] username, domain = user.email.split('@', 1) email_query = '{}+%@{}'.format(username, domain) return list(Author.query.filter( or_( Author.email.like(email_query), Author.email == user.email, ) )) try: author_id = UUID(author_id) except ValueError: return [] author = Author.query.get(author_id) if author is None: return [] return [author]
def validate_author(author_id): current_user = get_current_user() if author_id == 'me' and not current_user: raise ValueError('You are not signed in.') return Author.find(author_id, current_user)