This repository has been archived by the owner on Dec 15, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
expose API endpoint for bazel targets
Summary: This exposes per-build targets at /builds/{build_id}/targets/ Test Plan: unit tests Reviewers: anupc Reviewed By: anupc Subscribers: changesbot, anupc, kylec, wwu Differential Revision: https://tails.corp.dropbox.com/D229202
- Loading branch information
Naphat Sanguansin
committed
Sep 15, 2016
1 parent
e74b9c0
commit bc3eff8
Showing
5 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from __future__ import absolute_import, division, unicode_literals | ||
|
||
from flask.ext.restful import reqparse, types | ||
from sqlalchemy.orm import contains_eager | ||
from sqlalchemy.sql import func, asc, desc | ||
|
||
from changes.api.base import APIView | ||
from changes.constants import Result | ||
from changes.models.bazeltarget import BazelTarget | ||
from changes.models.build import Build | ||
from changes.models.job import Job | ||
|
||
|
||
SORT_CHOICES = ( | ||
'duration', | ||
'name' | ||
) | ||
|
||
RESULT_CHOICES = [r.name for r in Result] + [''] | ||
|
||
|
||
class BuildTargetIndexAPIView(APIView): | ||
parser = reqparse.RequestParser() | ||
parser.add_argument('query', type=unicode, location='args') | ||
parser.add_argument('result', type=unicode, location='args', | ||
choices=RESULT_CHOICES) | ||
parser.add_argument('sort', type=unicode, location='args', | ||
choices=SORT_CHOICES, default='duration') | ||
parser.add_argument('reverse', type=types.boolean, location='args', | ||
default=False) | ||
|
||
def get(self, build_id): | ||
build = Build.query.get(build_id) | ||
if build is None: | ||
return self.respond({}, status_code=404) | ||
|
||
args = self.parser.parse_args() | ||
|
||
target_list = BazelTarget.query.options( | ||
contains_eager('job') | ||
).join( | ||
Job, BazelTarget.job_id == Job.id, | ||
).filter( | ||
Job.build_id == build.id, | ||
) | ||
|
||
if args.query: | ||
target_list = target_list.filter( | ||
func.lower(BazelTarget.name).contains(args.query.lower()), | ||
) | ||
|
||
if args.result: | ||
target_list = target_list.filter( | ||
BazelTarget.result == Result[args.result], | ||
) | ||
|
||
sort_col, sort_dir = None, None | ||
if args.sort == 'duration': | ||
sort_col, sort_dir = BazelTarget.duration, desc | ||
elif args.sort == 'name': | ||
sort_col, sort_dir = BazelTarget.name, asc | ||
|
||
if args.reverse: | ||
sort_dir = {asc: desc, desc: asc}[sort_dir] | ||
|
||
target_list = target_list.order_by(sort_dir(sort_col)) | ||
|
||
return self.paginate(target_list, max_per_page=None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from changes.api.serializer import Crumbler, register | ||
from changes.models.bazeltarget import BazelTarget | ||
|
||
|
||
@register(BazelTarget) | ||
class BazelTargetCrumbler(Crumbler): | ||
def crumble(self, instance, attrs): | ||
return { | ||
'id': instance.id.hex, | ||
'job': {'id': instance.job_id.hex}, | ||
'step': {'id': instance.step_id.hex}, | ||
'name': instance.name, | ||
'duration': instance.duration or 0, | ||
'status': instance.status, | ||
'result': instance.result, | ||
'dateCreated': instance.date_created, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from datetime import datetime | ||
|
||
from changes.api.serializer import serialize | ||
from changes.constants import Result, Status | ||
from changes.testutils import TestCase | ||
|
||
|
||
class BazelTargetCrumblerTestCase(TestCase): | ||
def test_simple(self): | ||
project = self.create_project() | ||
build = self.create_build(project=project) | ||
job = self.create_job(build=build) | ||
phase = self.create_jobphase(job) | ||
step = self.create_jobstep(phase) | ||
target = self.create_target(job, step, | ||
name='target_foo', | ||
duration=134, | ||
result=Result.failed, | ||
status=Status.finished, | ||
date_created=datetime(2013, 9, 19, 22, 15, 22), | ||
) | ||
result = serialize(target) | ||
assert result['id'] == str(target.id.hex) | ||
assert result['job']['id'] == str(job.id.hex) | ||
assert result['name'] == 'target_foo' | ||
assert result['dateCreated'] == '2013-09-19T22:15:22' | ||
assert result['result']['id'] == 'failed' | ||
assert result['status']['id'] == 'finished' | ||
assert result['duration'] == 134 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
from uuid import uuid4 | ||
|
||
from changes.constants import Result, Status | ||
from changes.testutils import APITestCase | ||
|
||
|
||
class BuildTargetIndexTest(APITestCase): | ||
def test_simple(self): | ||
fake_id = uuid4() | ||
|
||
project = self.create_project() | ||
build = self.create_build(project, result=Result.failed) | ||
job = self.create_job(build, status=Status.finished) | ||
phase = self.create_jobphase(job) | ||
step = self.create_jobstep(phase) | ||
target = self.create_target( | ||
job=job, | ||
jobstep=step, | ||
name='bar', | ||
result=Result.failed, | ||
status=Status.finished, | ||
duration=15, | ||
) | ||
target2 = self.create_target( | ||
job=job, | ||
jobstep=step, | ||
name='foo', | ||
result=Result.failed, | ||
status=Status.finished, | ||
duration=10, | ||
) | ||
|
||
path = '/api/0/builds/{0}/targets/'.format(fake_id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 404 | ||
|
||
# test each sort option just to ensure it doesnt straight up fail | ||
path = '/api/0/builds/{0}/targets/?sort=duration'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 2 | ||
assert data[0]['id'] == target.id.hex | ||
assert data[1]['id'] == target2.id.hex | ||
|
||
path = '/api/0/builds/{0}/targets/?sort=name'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 2 | ||
assert data[0]['id'] == target.id.hex | ||
assert data[1]['id'] == target2.id.hex | ||
|
||
path = '/api/0/builds/{0}/targets/?sort=name&reverse=true'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 2 | ||
assert data[0]['id'] == target2.id.hex | ||
assert data[1]['id'] == target.id.hex | ||
|
||
path = '/api/0/builds/{0}/targets/?per_page='.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 2 | ||
assert data[0]['id'] == target.id.hex | ||
assert data[1]['id'] == target2.id.hex | ||
|
||
path = '/api/0/builds/{0}/targets/?query=foo'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 1 | ||
assert data[0]['id'] == target2.id.hex | ||
|
||
path = '/api/0/builds/{0}/targets/?query=somethingelse'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 0 | ||
|
||
path = '/api/0/builds/{0}/targets/?result='.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 2 | ||
|
||
path = '/api/0/builds/{0}/targets/?result=passed'.format(build.id.hex) | ||
|
||
resp = self.client.get(path) | ||
assert resp.status_code == 200 | ||
data = self.unserialize(resp) | ||
assert len(data) == 0 |