зеркало из https://github.com/mozilla/treeherder.git
add bug_job_map endpoints and model methods
This commit is contained in:
Родитель
0853bdfda4
Коммит
78d46f1505
|
@ -391,7 +391,7 @@ def test_update_bugscache(refdata, sample_bugs):
|
|||
assert len(bug_list) == len(row_data)
|
||||
|
||||
|
||||
def test_get_suggested_bugs(refdata, sample_bugs):
|
||||
def test_get_bugscache(refdata, sample_bugs):
|
||||
"""
|
||||
Test that at least one result is retrieved
|
||||
for the right search terms
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
import random
|
||||
|
||||
|
||||
def test_create_bug_job_map(webapp, eleven_jobs_processed, jm):
|
||||
"""
|
||||
test creating a single note via endpoint
|
||||
"""
|
||||
job = jm.get_job_list(0, 1)[0]
|
||||
|
||||
bug_job_map_obj = {
|
||||
"job_id": job["id"],
|
||||
"bug_id": 1,
|
||||
"type": "manual"
|
||||
}
|
||||
|
||||
resp = webapp.post_json(
|
||||
reverse("bug-job-map-list", kwargs={"project": jm.project}),
|
||||
bug_job_map_obj)
|
||||
|
||||
assert (bug_job_map_obj,) == jm.get_bug_job_map_list(0, 1)
|
||||
|
||||
|
||||
def test_bug_job_map_list(webapp, jm, eleven_jobs_processed):
|
||||
"""
|
||||
test retrieving a list of bug_job_map
|
||||
"""
|
||||
jobs = jm.get_job_list(0, 10)
|
||||
bugs = [random.randint(0, 100) for i in range(0, len(jobs))]
|
||||
|
||||
expected = list()
|
||||
|
||||
for i, v in enumerate(jobs):
|
||||
jm.insert_bug_job_map(v["id"], bugs[i], "manual")
|
||||
expected.append({
|
||||
"job_id": v["id"],
|
||||
"bug_id": bugs[i],
|
||||
"type": "manual"})
|
||||
|
||||
resp = webapp.get(
|
||||
reverse("bug-job-map-list", kwargs={"project": jm.project}))
|
||||
|
||||
for i, v in enumerate(expected):
|
||||
assert v == resp.json[i]
|
||||
|
||||
def test_bug_job_map_detail(webapp, jm, eleven_jobs_processed):
|
||||
"""
|
||||
test retrieving a list of bug_job_map
|
||||
"""
|
||||
job_id = jm.get_job_list(0, 1)[0]["id"]
|
||||
bug_id = random.randint(0, 100)
|
||||
|
||||
expected = list()
|
||||
|
||||
jm.insert_bug_job_map(job_id, bug_id, "manual")
|
||||
|
||||
pk = "{0}-{1}".format(job_id, bug_id)
|
||||
|
||||
resp = webapp.get(
|
||||
reverse("bug-job-map-detail", kwargs={
|
||||
"project": jm.project,
|
||||
"pk": pk
|
||||
})
|
||||
)
|
||||
|
||||
assert resp.json == {"job_id": job_id, "bug_id": bug_id, "type": "manual"}
|
||||
|
||||
|
||||
def test_bug_job_map_delete(webapp, jm, eleven_jobs_processed):
|
||||
"""
|
||||
test retrieving a list of bug_job_map
|
||||
"""
|
||||
job_id = jm.get_job_list(0, 1)[0]["id"]
|
||||
bug_id = random.randint(0, 100)
|
||||
|
||||
jm.insert_bug_job_map(job_id, bug_id, "manual")
|
||||
|
||||
pk = "{0}-{1}".format(job_id, bug_id)
|
||||
|
||||
|
||||
|
||||
resp = webapp.delete_json(
|
||||
reverse("bug-job-map-detail", kwargs={
|
||||
"project": jm.project,
|
||||
"pk": pk
|
||||
})
|
||||
)
|
||||
|
||||
assert resp.json == {"message": "Bug job map deleted"}
|
|
@ -126,21 +126,3 @@ def test_job_detail_not_found(webapp, jm):
|
|||
expect_errors=True
|
||||
)
|
||||
assert resp.status_int == 404
|
||||
|
||||
|
||||
def test_retrieve_result_set(jm, webapp, eleven_jobs_processed):
|
||||
resp = webapp.get(
|
||||
reverse("resultset-list",
|
||||
kwargs={"project": jm.project})
|
||||
)
|
||||
assert resp.status_int == 200
|
||||
assert isinstance(resp.json, list)
|
||||
|
||||
|
||||
def test_retrieve_result_set_detail(jm, webapp, eleven_jobs_processed):
|
||||
job = jm.get_job_list(0, 1)[0]
|
||||
resp = webapp.get(
|
||||
reverse("resultset-detail",
|
||||
kwargs={"project": jm.project, "pk": job["id"]})
|
||||
)
|
||||
assert resp.status_int == 200
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import json
|
||||
import pytest
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
xfail = pytest.mark.xfail
|
||||
|
||||
|
||||
def test_note_list(webapp, sample_notes, jm):
|
||||
"""
|
||||
|
@ -142,5 +138,3 @@ def test_create_note(webapp, eleven_jobs_processed, jm):
|
|||
u'active_status': u'active',
|
||||
u'id': 1
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -63,6 +63,11 @@ class JobsModel(TreeherderModelBase):
|
|||
],
|
||||
"result_set": [
|
||||
"id"
|
||||
],
|
||||
"bug_job_map": [
|
||||
"job_id",
|
||||
"bug_id",
|
||||
"type"
|
||||
]
|
||||
|
||||
}
|
||||
|
@ -116,31 +121,18 @@ class JobsModel(TreeherderModelBase):
|
|||
|
||||
def get_job_list(self, offset, limit, full=True, conditions=None):
|
||||
"""
|
||||
Retrieve a list of jobs.
|
||||
Mainly used by the restful api to list the jobs
|
||||
|
||||
joblist: a list of job ids to limit which jobs are returned.
|
||||
full: whether to return all reference data or just what is
|
||||
needed for the UI.
|
||||
Retrieve a list of jobs. It's mainly used by the restful api to list
|
||||
the jobs. The conditions parameter is a dict containing a set of
|
||||
conditions for each key. e.g.:
|
||||
{
|
||||
'who': set([('=', 'john')]),
|
||||
'result': set([('IN', ("success", "retry"))])
|
||||
}
|
||||
"""
|
||||
|
||||
placeholders = []
|
||||
replace_str = ""
|
||||
if conditions:
|
||||
for column, condition in conditions.items():
|
||||
if column in self.INDEXED_COLUMNS["job"]:
|
||||
for operator, value in condition:
|
||||
replace_str += "AND j.{0} {1}".format(column, operator)
|
||||
if operator == "IN":
|
||||
# create a list of placeholders of the same length
|
||||
# as the list of values
|
||||
replace_str += "({0})".format(
|
||||
",".join(["%s"] * len(value))
|
||||
)
|
||||
placeholders += value
|
||||
else:
|
||||
replace_str += " %s "
|
||||
placeholders.append(value)
|
||||
replace_str, placeholders = self._process_conditions(
|
||||
conditions, self.INDEXED_COLUMNS['job'], prefix="j."
|
||||
)
|
||||
|
||||
repl = [self.refdata_model.get_db_name(), replace_str]
|
||||
|
||||
|
@ -157,6 +149,27 @@ class JobsModel(TreeherderModelBase):
|
|||
)
|
||||
return data
|
||||
|
||||
def _process_conditions(self, conditions, allowed_fields=None, prefix=""):
|
||||
"""Transform a list of conditions into a list of placeholders and
|
||||
replacement strings to feed a datahub.execute statement."""
|
||||
placeholders = []
|
||||
replace_str = ""
|
||||
if conditions:
|
||||
for column, condition in conditions.items():
|
||||
if allowed_fields is None or column in allowed_fields:
|
||||
for operator, value in condition:
|
||||
replace_str += "AND {0}{1} {2}".format(prefix, column, operator)
|
||||
if operator == "IN":
|
||||
# create a list of placeholders of the same length
|
||||
# as the list of values
|
||||
replace_str += "({0})".format(
|
||||
",".join(["%s"] * len(value))
|
||||
)
|
||||
placeholders += value
|
||||
else:
|
||||
replace_str += " %s "
|
||||
placeholders.append(value)
|
||||
return replace_str, placeholders
|
||||
|
||||
def set_state(self, job_id, state):
|
||||
"""Update the state of an existing job"""
|
||||
|
@ -230,6 +243,7 @@ class JobsModel(TreeherderModelBase):
|
|||
debug_show=self.DEBUG
|
||||
)
|
||||
|
||||
|
||||
self.get_jobs_dhub().execute(
|
||||
proc='jobs.updates.update_last_job_classification',
|
||||
placeholders=[
|
||||
|
@ -239,6 +253,64 @@ class JobsModel(TreeherderModelBase):
|
|||
debug_show=self.DEBUG
|
||||
)
|
||||
|
||||
|
||||
def insert_bug_job_map(self, job_id, bug_id, assignment_type):
|
||||
"""
|
||||
Store a new relation between the given job and bug ids.
|
||||
"""
|
||||
self.get_jobs_dhub().execute(
|
||||
proc='jobs.inserts.insert_bug_job_map',
|
||||
placeholders=[
|
||||
job_id,
|
||||
bug_id,
|
||||
assignment_type
|
||||
],
|
||||
debug_show=self.DEBUG
|
||||
)
|
||||
|
||||
def delete_bug_job_map(self, job_id, bug_id):
|
||||
"""
|
||||
Delete a bug-job entry identified by bug_id and job_id
|
||||
"""
|
||||
self.get_jobs_dhub().execute(
|
||||
proc='jobs.deletes.delete_bug_job_map',
|
||||
placeholders=[
|
||||
job_id,
|
||||
bug_id
|
||||
],
|
||||
debug_show=self.DEBUG
|
||||
)
|
||||
|
||||
def get_bug_job_map_list(self, offset, limit, conditions=None):
|
||||
"""
|
||||
Retrieve a list of bug_job_map entries. The conditions parameter is a
|
||||
dict containing a set of conditions for each key. e.g.:
|
||||
{
|
||||
'job_id': set([('IN', (1, 2))])
|
||||
}
|
||||
"""
|
||||
|
||||
replace_str, placeholders = self._process_conditions(
|
||||
conditions, self.INDEXED_COLUMNS['bug_job_map']
|
||||
)
|
||||
|
||||
repl = [replace_str]
|
||||
|
||||
proc = "jobs.selects.get_bug_job_map"
|
||||
|
||||
print repl
|
||||
print placeholders
|
||||
|
||||
data = self.get_jobs_dhub().execute(
|
||||
proc=proc,
|
||||
replace=repl,
|
||||
placeholders=placeholders,
|
||||
limit="{0},{1}".format(offset, limit),
|
||||
debug_show=self.DEBUG,
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
def get_result_set_ids(self, revision_hashes, where_in_list):
|
||||
"""Return the a dictionary of revision_hash to id mappings given
|
||||
a list of revision_hashes and a where_in_list.
|
||||
|
@ -277,24 +349,10 @@ class JobsModel(TreeherderModelBase):
|
|||
|
||||
Mainly used by the restful api to list the pushes in the UI
|
||||
"""
|
||||
placeholders = []
|
||||
replace_str = ""
|
||||
|
||||
if conditions:
|
||||
for column, condition in conditions.items():
|
||||
if column in self.INDEXED_COLUMNS["result_set"]:
|
||||
for operator, value in condition:
|
||||
replace_str += "AND rs.{0} {1}".format(column, operator)
|
||||
if operator == "IN":
|
||||
# create a list of placeholders of the same length
|
||||
# as the list of values
|
||||
replace_str += "({0})".format(
|
||||
",".join(["%s"] * len(value))
|
||||
)
|
||||
placeholders += value
|
||||
else:
|
||||
replace_str += " %s "
|
||||
placeholders.append(value)
|
||||
replace_str, placeholders = self._process_conditions(
|
||||
conditions, self.INDEXED_COLUMNS['result_set'], prefix="rs."
|
||||
)
|
||||
|
||||
# If a push doesn't have jobs we can just
|
||||
# message the user, it would save us a very expensive join
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
{
|
||||
"deletes":{
|
||||
"delete_bug_job_map":{
|
||||
"sql":"DELETE FROM bug_job_map WHERE job_id = ? and bug_id = ?",
|
||||
"host": "master_host"
|
||||
}
|
||||
},
|
||||
"inserts":{
|
||||
"create_job_data":{
|
||||
|
||||
|
@ -110,9 +116,19 @@
|
|||
`note_timestamp`)
|
||||
VALUES (?,?,?,?,?)",
|
||||
|
||||
"host":"master_host"
|
||||
},
|
||||
"insert_bug_job_map":{
|
||||
"sql":"INSERT INTO `bug_job_map` (
|
||||
`job_id`,
|
||||
`bug_id`,
|
||||
`type`,
|
||||
`active_status`)
|
||||
VALUES (?,?,?,'active')",
|
||||
"host":"master_host"
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
"updates": {
|
||||
|
@ -482,6 +498,13 @@
|
|||
"sql":"SELECT `name`, `type`, `blob` FROM `job_artifact`",
|
||||
|
||||
"host":"read_host"
|
||||
},
|
||||
"get_bug_job_map":{
|
||||
"sql":"SELECT `job_id`, `bug_id`, `type`
|
||||
FROM `bug_job_map`
|
||||
WHERE 1
|
||||
REP0",
|
||||
"host": "read_host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,12 @@ project_bound_router.register(
|
|||
base_name='revision-lookup',
|
||||
)
|
||||
|
||||
project_bound_router.register(
|
||||
r'bug-job-map',
|
||||
views.BugJobMapViewSet,
|
||||
base_name='bug-job-map',
|
||||
)
|
||||
|
||||
|
||||
# this is the default router for plain restful endpoints
|
||||
|
||||
|
|
|
@ -564,6 +564,67 @@ class RevisionLookupSetViewSet(viewsets.ViewSet):
|
|||
return Response(jm.get_revision_resultset_lookup(revision_list))
|
||||
|
||||
|
||||
class BugJobMapViewSet(viewsets.ViewSet):
|
||||
|
||||
@with_jobs
|
||||
def create(self, request, project, jm):
|
||||
"""
|
||||
Add a new relation between a job and a bug
|
||||
"""
|
||||
jm.insert_bug_job_map(
|
||||
request.DATA['job_id'],
|
||||
request.DATA['bug_id'],
|
||||
request.DATA['type']
|
||||
)
|
||||
|
||||
return Response({"message": "Bug job map stored"})
|
||||
|
||||
@with_jobs
|
||||
def destroy(self, request, project, jm, pk=None):
|
||||
"""
|
||||
Delete bug-job-map entry. pk is a composite key in the form
|
||||
bug_id-job_id
|
||||
"""
|
||||
job_id, bug_id = pk.split("-")
|
||||
jm.delete_bug_job_map(job_id, bug_id)
|
||||
return Response({"message": "Bug job map deleted"})
|
||||
|
||||
@with_jobs
|
||||
def retrieve(self, request, project, jm, pk=None):
|
||||
"""
|
||||
Retrieve a bug-job-map entry. pk is a composite key in the form
|
||||
bug_id-job_id
|
||||
"""
|
||||
job_id, bug_id = pk.split("-")
|
||||
params = {
|
||||
"bug_id": bug_id,
|
||||
"job_id": job_id
|
||||
}
|
||||
params.update(request.QUERY_PARAMS)
|
||||
filters = UrlQueryFilter(params).parse()
|
||||
obj = jm.get_bug_job_map_list(0, 1, filters)
|
||||
if obj:
|
||||
return Response(obj[0])
|
||||
else:
|
||||
return Response("Object not found", 404)
|
||||
|
||||
|
||||
|
||||
@with_jobs
|
||||
def list(self, request, project, jm):
|
||||
filters = UrlQueryFilter(request.QUERY_PARAMS).parse()
|
||||
|
||||
limit_condition = filters.pop("limit", set([("=", "0,10")])).pop()
|
||||
offset, limit = limit_condition[1].split(",")
|
||||
|
||||
objs = jm.get_bug_job_map_list(
|
||||
offset,
|
||||
limit,
|
||||
filters
|
||||
)
|
||||
return Response(objs)
|
||||
|
||||
|
||||
|
||||
#####################
|
||||
# Refdata ViewSets
|
||||
|
|
Загрузка…
Ссылка в новой задаче