Skip to content

Commit

Permalink
add new_open field to bug job map. (#8279)
Browse files Browse the repository at this point in the history
* add new_open field to bug job map.

* Add support for bug_open field to bug_job_map when create or reopen bug.
  • Loading branch information
jmaher authored Nov 23, 2024
1 parent 5269aed commit ca3225d
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 10 deletions.
28 changes: 26 additions & 2 deletions tests/etl/test_bugzilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.urls import reverse

from treeherder.etl.bugzilla import BzApiBugProcess
from treeherder.model.models import Bugscache
from treeherder.model.models import Bugscache, BugJobMap


@pytest.mark.django_db(transaction=True)
Expand Down Expand Up @@ -50,7 +50,12 @@ def test_bz_reopen_bugs(
not_incomplete_bugs[0],
not_incomplete_bugs[2],
]:
submit_obj = {"job_id": test_jobs[idx].id, "bug_id": bug.id, "type": "manual"}
submit_obj = {
"job_id": test_jobs[idx].id,
"bug_id": bug.id,
"type": "manual",
"bug_open": False,
}

client.post(
reverse("bug-job-map-list", kwargs={"project": test_jobs[idx].repository.name}),
Expand All @@ -61,10 +66,29 @@ def test_bz_reopen_bugs(
if idx % 11 == 0:
idx = 0

# always closed
# as we only reopen a single instance of a bug, we choose the most recent instance
# since the reopen code queries and then `.order_by("-created")`
bug_job_map = BugJobMap.objects.filter(job_id=test_jobs[4].id, bug_id=incomplete_bugs[0].id)[0]
assert bug_job_map.bug_open is False

bug_job_map = BugJobMap.objects.filter(job_id=test_jobs[3].id, bug_id=incomplete_bugs[2].id)[0]
assert bug_job_map.bug_open is False

process = BzApiBugProcess()
process.minimum_failures_to_reopen = minimum_failures_to_reopen
process.run()

# reopens based on minimum_failures_to_reopen
bug_job_map = BugJobMap.objects.filter(job_id=test_jobs[4].id, bug_id=incomplete_bugs[0].id)[0]
assert bug_job_map.bug_open is True

bug_job_map = BugJobMap.objects.filter(job_id=test_jobs[3].id, bug_id=incomplete_bugs[2].id)[0]
if minimum_failures_to_reopen < 3:
assert bug_job_map.bug_open is True
else:
assert bug_job_map.bug_open is False

reopened_bugs = request.config.cache.get("reopened_bugs", None)

import json
Expand Down
13 changes: 10 additions & 3 deletions tests/webapp/api/test_bug_job_map_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@


@pytest.mark.parametrize(
"test_no_auth,test_duplicate_handling", [(True, False), (False, False), (False, True)]
"test_no_auth,test_duplicate_handling,bug_open",
[(True, False, False), (False, False, False), (False, True, True)],
)
def test_create_bug_job_map(
client, test_job, test_user, bugs, test_no_auth, test_duplicate_handling
client, test_job, test_user, bugs, test_no_auth, test_duplicate_handling, bug_open
):
"""
test creating a single note via endpoint
Expand All @@ -19,7 +20,12 @@ def test_create_bug_job_map(
if not test_no_auth:
client.force_authenticate(user=test_user)

submit_obj = {"job_id": test_job.id, "bug_id": bug.id, "type": "manual"}
submit_obj = {
"job_id": test_job.id,
"bug_id": bug.id,
"type": "manual",
"bug_open": bug_open,
}

# if testing duplicate handling, submit twice
if test_duplicate_handling:
Expand All @@ -43,6 +49,7 @@ def test_create_bug_job_map(
assert bug_job_map.job_id == submit_obj["job_id"]
assert bug_job_map.bug_id == submit_obj["bug_id"]
assert bug_job_map.user == test_user
assert bug_job_map.bug_open == bug_open


def test_bug_job_map_list(client, test_repository, eleven_jobs_stored, test_user, bugs):
Expand Down
7 changes: 6 additions & 1 deletion treeherder/etl/bugzilla.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ def reopen_intermittent_bugs(minimum_failures_to_reopen=1):

comment = {"body": "New failure instance: " + log_url}
url = settings.BUGFILER_API_URL + "/rest/bug/" + str(bug_id)
headers = {"x-bugzilla-api-key": settings.BUGFILER_API_KEY, "Accept": "application/json"}
headers = {
"x-bugzilla-api-key": settings.BUGFILER_API_KEY,
"Accept": "application/json",
}
data = {
"status": "REOPENED",
"comment": comment,
Expand All @@ -59,6 +62,8 @@ def reopen_intermittent_bugs(minimum_failures_to_reopen=1):

try:
reopen_request(url, method="PUT", headers=headers, json=data)
# NOTE: this will only toggle 1 bug_job_map entry, not all (if there are retriggers)
BugJobMap.objects.filter(job_id=job_id, bug_id=bug_id).update(bug_open=True)
except requests.exceptions.HTTPError as e:
try:
message = e.response.json()["message"]
Expand Down
21 changes: 21 additions & 0 deletions treeherder/model/migrations/0034_bugjobmap_bug_open.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.13 on 2024-10-21 13:53

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"model",
"0033_textlogerror_new_failure",
),
]

operations = [
migrations.AddField(
model_name="bugjobmap",
name="bug_open",
field=models.BooleanField(default=False),
),
]
4 changes: 3 additions & 1 deletion treeherder/model/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ class BugJobMap(models.Model):
bug_id = models.PositiveIntegerField(db_index=True)
created = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True) # null if autoclassified
bug_open = models.BooleanField(default=False)

failures = FailuresQuerySet.as_manager()
objects = models.Manager()
Expand All @@ -736,11 +737,12 @@ def who(self):
return "autoclassifier"

@classmethod
def create(cls, job_id, bug_id, user=None):
def create(cls, job_id, bug_id, user=None, bug_open=False):
bug_map = BugJobMap.objects.create(
job_id=job_id,
bug_id=bug_id,
user=user,
bug_open=bug_open,
)

if not user:
Expand Down
2 changes: 2 additions & 0 deletions treeherder/webapp/api/bug.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ def create(self, request, project):
"""Add a new relation between a job and a bug."""
job_id = int(request.data["job_id"])
bug_id = int(request.data["bug_id"])
bug_open = bool(request.data["bug_open"])

try:
BugJobMap.create(
job_id=job_id,
bug_id=bug_id,
user=request.user,
bug_open=bug_open,
)
message = "Bug job map saved"
except IntegrityError:
Expand Down
6 changes: 5 additions & 1 deletion ui/job-view/details/PinBoard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,14 @@ class PinBoard extends React.Component {
};

saveBugs = (job) => {
const { pinnedJobBugs, notify } = this.props;
const { pinnedJobBugs, newBug, notify } = this.props;

pinnedJobBugs.forEach((bugId) => {
const bjm = new BugJobMapModel({
bug_id: bugId,
job_id: job.id,
type: 'annotation',
bug_open: newBug.has(bugId),
});

bjm.create().catch((response) => {
Expand Down Expand Up @@ -703,6 +704,7 @@ PinBoard.propTypes = {
isPinBoardVisible: PropTypes.bool.isRequired,
pinnedJobs: PropTypes.shape({}).isRequired,
pinnedJobBugs: PropTypes.shape({}).isRequired,
newBug: PropTypes.string.isRequired,
addBug: PropTypes.func.isRequired,
removeBug: PropTypes.func.isRequired,
unPinJob: PropTypes.func.isRequired,
Expand Down Expand Up @@ -733,6 +735,7 @@ const mapStateToProps = ({
pinnedJobBugs,
failureClassificationId,
failureClassificationComment,
newBug,
},
}) => ({
revisionTips,
Expand All @@ -743,6 +746,7 @@ const mapStateToProps = ({
pinnedJobBugs,
failureClassificationId,
failureClassificationComment,
newBug,
});

export default connect(mapStateToProps, {
Expand Down
11 changes: 10 additions & 1 deletion ui/job-view/redux/stores/pinnedJobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,20 @@ export const pinJobs = (jobsToPin) => {
export const addBug = (bug, job) => {
return async (dispatch, getState) => {
const {
pinnedJobs: { pinnedJobBugs },
pinnedJobs: { pinnedJobBugs, newBug },
} = getState();

const bugId = bug.dupe_of ? bug.dupe_of : bug.id;
if (!pinnedJobBugs.has(bugId)) {
pinnedJobBugs.add(bugId);
}

if ('newBug' in bug) {
if (!newBug.has(bug.newBug)) {
newBug.add(bug.newBug);
}
}

dispatch({
type: SET_PINNED_JOB_BUGS,
pinnedJobBugs: new Set(pinnedJobBugs),
Expand Down Expand Up @@ -148,6 +155,7 @@ export const unPinAll = () => ({
payload: {
failureClassificationId: 4,
failureClassificationComment: '',
newBug: new Set(),
pinnedJobs: {},
pinnedJobBugs: new Set(),
},
Expand All @@ -171,6 +179,7 @@ const initialState = {
pinnedJobs: {},
pinnedJobBugs: new Set(),
failureClassificationComment: '',
newBug: new Set(),
failureClassificationId: 4,
isPinBoardVisible: false,
};
Expand Down
2 changes: 1 addition & 1 deletion ui/shared/tabs/failureSummary/FailureSummaryTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class FailureSummaryTab extends React.Component {
bugFilerCallback = (data) => {
const { addBug } = this.props;

addBug({ id: data.id });
addBug({ id: data.id, newBug: data.id });
window.dispatchEvent(new CustomEvent(thEvents.saveClassification));
// Open the newly filed bug in a new tab or window for further editing
window.open(data.url);
Expand Down

0 comments on commit ca3225d

Please sign in to comment.