Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions bin/load-mocks
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,21 @@ def main(num_events=1, extra_events=False):
order=commit_index,
)

# create an unreleased commit
Commit.objects.get_or_create(
organization_id=org.id,
repository_id=repo[0].id,
key=sha1(uuid4().hex).hexdigest(),
defaults={
'author': CommitAuthor.objects.get_or_create(
organization_id=org.id,
email=user.email,
defaults={'name': user.name}
)[0],
'message': 'feat: Do something to {}\n{}'.format(random.choice(loremipsum.words) + '.js', make_sentence()),
},
)[0]

Activity.objects.create(
type=Activity.RELEASE,
project=project,
Expand Down
34 changes: 0 additions & 34 deletions src/sentry/api/endpoints/organization_member_commits.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from __future__ import absolute_import

import six

from django.db import connections
from itertools import izip

from sentry.api.bases import OrganizationMemberEndpoint
from sentry.api.serializers import serialize
from sentry.models import Commit, Repository, UserEmail

query = """
select c1.*
from sentry_commit c1
join (
select max(c2.date_added) as date_added, c2.repository_id
from sentry_commit as c2
join (
select distinct commit_id from sentry_releasecommit
where organization_id = %%s
) as rc2
on c2.id = rc2.commit_id
group by c2.repository_id
) as cmax
on c1.repository_id = cmax.repository_id
where c1.date_added > cmax.date_added
and c1.author_id IN (
select id
from sentry_commitauthor
where organization_id = %%s
and upper(email) IN (%s)
)
order by c1.date_added desc
"""

quote_name = connections['default'].ops.quote_name


class OrganizationMemberUnreleasedCommitsEndpoint(OrganizationMemberEndpoint):
def get(self, request, organization, member):
email_list = list(UserEmail.objects.filter(
user=member.user_id,
is_verified=True,
).values_list('email', flat=True))
if not email_list:
return self.respond([])

params = [organization.id, organization.id]
for e in email_list:
params.append(e.upper())

queryset = Commit.objects.raw(query % (', '.join('%s' for _ in email_list),), params)

results = list(queryset)

if results:
repos = list(Repository.objects.filter(
id__in=set([r.repository_id for r in results]),
))
else:
repos = []

return self.respond({
'commits': [{
'id': c.key,
'message': c.message,
'dateCreated': c.date_added,
'repositoryID': six.text_type(c.repository_id),
} for c in results],
'repositories': {
six.text_type(r.id): d for r, d in izip(repos, serialize(repos, request.user))
}
})
8 changes: 4 additions & 4 deletions src/sentry/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
from .endpoints.organization_shortid import ShortIdLookupEndpoint
from .endpoints.organization_slugs import SlugsUpdateEndpoint
from .endpoints.organization_issues_new import OrganizationIssuesNewEndpoint
from .endpoints.organization_member_commits import OrganizationMemberCommitsEndpoint
from .endpoints.organization_member_details import OrganizationMemberDetailsEndpoint
from .endpoints.organization_member_index import OrganizationMemberIndexEndpoint
from .endpoints.organization_member_issues_assigned import OrganizationMemberIssuesAssignedEndpoint
from .endpoints.organization_member_issues_bookmarked import OrganizationMemberIssuesBookmarkedEndpoint
from .endpoints.organization_member_issues_viewed import OrganizationMemberIssuesViewedEndpoint
from .endpoints.organization_member_unreleased_commits import OrganizationMemberUnreleasedCommitsEndpoint
from .endpoints.organization_member_team_details import OrganizationMemberTeamDetailsEndpoint
from .endpoints.organization_onboarding_tasks import OrganizationOnboardingTaskEndpoint
from .endpoints.organization_index import OrganizationIndexEndpoint
Expand Down Expand Up @@ -401,9 +401,9 @@
name='sentry-api-0-organization-member-details'
),
url(
r'^organizations/(?P<organization_slug>[^\/]+)/members/(?P<member_id>[^\/]+)/commits/$',
OrganizationMemberCommitsEndpoint.as_view(),
name='sentry-api-0-organization-member-commits'
r'^organizations/(?P<organization_slug>[^\/]+)/members/(?P<member_id>[^\/]+)/unreleased-commits/$',
OrganizationMemberUnreleasedCommitsEndpoint.as_view(),
name='sentry-api-0-organization-member-unreleased-commits'
),
url(
r'^organizations/(?P<organization_slug>[^\/]+)/members/(?P<member_id>[^\/]+)/issues/assigned/$',
Expand Down
115 changes: 46 additions & 69 deletions src/sentry/static/sentry/app/views/organizationCommits.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,35 @@ import React from 'react';
import AsyncView from '../views/asyncView';
import OrganizationHomeContainer from '../components/organizations/homeContainer';

import CommitRow from '../components/commitRow';
import TimeSince from '../components/timeSince';
import CommitLink from '../components/commitLink';
import Pagination from '../components/pagination';
import {t} from '../locale';

export default class OrganizationCommits extends AsyncView {
getEndpoints() {
return [
['commitList', `/organizations/${this.props.params.orgId}/members/me/commits/`],
[
'unreleasedCommits',
`/organizations/${this.props.params.orgId}/members/me/unreleased-commits/`,
],
];
}

getTitle() {
return 'Commits';
}

renderMessage = message => {
if (!message) {
return t('No message provided');
}

let firstLine = message.split(/\n/)[0];

return firstLine;
};

emptyState() {
return (
<div className="box empty-stream m-y-0">
Expand All @@ -30,76 +44,37 @@ export default class OrganizationCommits extends AsyncView {
);
}

getCommitsByRepository(commitList) {
let commitsByRepository = commitList.reduce(function(cbr, commit) {
let {repository} = commit;
if (!cbr.hasOwnProperty(repository.name)) {
cbr[repository.name] = [];
}

cbr[repository.name].push(commit);
return cbr;
}, {});
return commitsByRepository;
}

renderCommitsForRepo(repo, commitList) {
let commitsByRepository = this.getCommitsByRepository(commitList);
let activeCommits = commitsByRepository[repo];
return (
<div className="panel panel-default">
<div className="panel-heading panel-heading-bold">{repo}</div>
<ul className="list-group list-group-lg commit-list">
{activeCommits.map(commit => {
return <CommitRow key={commit.id} commit={commit} />;
})}
</ul>
</div>
);
}

renderBody() {
let {commitList, commitListPageLinks} = this.state;
if (!commitList.length) return this.emptyState();

let unreleasedCommits = [],
releasedCommits = [];
let marker = false;
commitList.forEach(commit => {
if (marker) {
releasedCommits.push(commit);
} else if (commit.releases.length) {
marker = true;
releasedCommits.push(commit);
} else {
unreleasedCommits.push(commit);
}
});
let {unreleasedCommits, unreleasedCommitsPageLinks} = this.state;
let {commits, repositories} = unreleasedCommits;
if (!commits.length) return this.emptyState();

return (
<div>
{unreleasedCommits.length && (
<div className="panel panel-default">
<div className="panel-heading panel-heading-bold">Unreleased</div>
<ul className="list-group list-group-lg commit-list">
{unreleasedCommits.map(commit => {
return <CommitRow key={commit.id} commit={commit} />;
})}
</ul>
</div>
)}
{releasedCommits.length && (
<div className="panel panel-default">
<div className="panel-heading panel-heading-bold">Released</div>
<ul className="list-group list-group-lg commit-list">
{releasedCommits.map(commit => {
return <CommitRow key={commit.id} commit={commit} />;
})}
</ul>
</div>
)}
{commitListPageLinks && (
<Pagination pageLinks={commitListPageLinks} {...this.props} />
<div className="panel panel-default">
<ul className="list-group list-group-lg commit-list">
{commits.map(commit => {
let repo = repositories[commit.repositoryID];
return (
<li className="list-group-item" key={commit.id}>
<div className="row row-center-vertically">
<div className="col-xs-10">
<h5 className="truncate">{this.renderMessage(commit.message)}</h5>
<p>
{repo.name} &mdash; <TimeSince date={commit.dateCreated} />
</p>
</div>
<div className="col-xs-2 hidden-xs align-right">
<CommitLink commitId={commit.id} repository={repo} />
</div>
</div>
</li>
);
})}
</ul>
</div>
{!!unreleasedCommitsPageLinks && (
<Pagination pageLinks={unreleasedCommitsPageLinks} {...this.props} />
)}
</div>
);
Expand All @@ -108,7 +83,9 @@ export default class OrganizationCommits extends AsyncView {
render() {
return (
<OrganizationHomeContainer>
<h4>{t('My Commits')}</h4>
<h4>
{t('Unreleased Changes')} <small>Mine</small>
</h4>
{this.renderComponent()}
</OrganizationHomeContainer>
);
Expand Down
Loading