Skip to content

Commit 924f1a5

Browse files
committedJun 27, 2017
WIP Add callback
1 parent c6a333d commit 924f1a5

File tree

7 files changed

+159
-11
lines changed

7 files changed

+159
-11
lines changed
 

‎dev-requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ py==1.4.33 \
2727
--hash=sha256:1f9a981438f2acc20470b301a07a496375641f902320f70e31916fe3377385a9
2828
requests-mock==1.3.0 \
2929
--hash=sha256:23edd6f7926aa13b88bf79cb467632ba2dd5a253034e9f41563f60ed305620c7
30+
pytest-mock==1.6.0 \
31+
--hash=sha256:83a17cbcd4dbc7c6c9dc885a0d598f9acd11f2d5142e0718ed32e14538670c1f \
32+
--hash=sha256:29bf62c12a67dc0e7f6d02efa2404f0b4d068b38cd64f22a06c8a16cb545a03e

‎docker-compose.yml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ services:
1717
- PHABRICATOR_UNPRIVILEGED_API_KEY=api-123456789
1818
- TRANSPLANT_URL=https://stub.transplant.example.com
1919
- DATABASE_URL=sqlite:////db/sqlite.db
20+
- HOST_URL=https://landoapi.mozilla.org
2021
volumes:
2122
- ./:/app
2223
- ./.db/:/db/

‎landoapi/api/landings.py

+17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"""
88
from connexion import problem
99
from flask import request
10+
from sqlalchemy.orm.exc import NoResultFound
1011
from landoapi.models.landing import (
1112
Landing,
1213
LandingNotCreatedException,
@@ -71,3 +72,19 @@ def get(landing_id):
7172
)
7273

7374
return landing.serialize(), 200
75+
76+
77+
def update(landing_id, auth, data):
78+
"""Update landing on pingback from Transplant."""
79+
try:
80+
landing = Landing.query.filter_by(id=landing_id, auth=auth).one()
81+
except NoResultFound:
82+
return problem(
83+
404,
84+
'Landing not found',
85+
'The requested Landing does not exist',
86+
type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404'
87+
)
88+
landing.status = data['status']
89+
landing.save()
90+
return landing.serialize(), 202

‎landoapi/models/landing.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# This Source Code Form is subject to the terms of the Mozilla Public
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
import os
5+
import random
6+
import string
7+
48
from landoapi.models.storage import db
59
from landoapi.phabricator_client import PhabricatorClient
610
from landoapi.transplant_client import TransplantClient
@@ -9,6 +13,10 @@
913
TRANSPLANT_JOB_FINISHED = 'finished'
1014

1115

16+
def _hashtag(size=6, chars=string.ascii_uppercase + string.digits):
17+
return ''.join(random.choice(chars) for _ in range(size))
18+
19+
1220
def _get_revision(revision_id, api_key=None):
1321
""" Gets revision from Phabricator.
1422
@@ -42,35 +50,41 @@ class Landing(db.Model):
4250
request_id = db.Column(db.Integer, unique=True)
4351
revision_id = db.Column(db.String(30))
4452
status = db.Column(db.Integer)
53+
auth = db.Column(db.String(30))
4554

4655
def __init__(
4756
self, request_id=None, revision_id=None, status=TRANSPLANT_JOB_STARTED
4857
):
4958
self.request_id = request_id
5059
self.revision_id = revision_id
5160
self.status = status
61+
self.auth = _hashtag()
5262

5363
@classmethod
54-
def create(cls, revision_id, phabricator_api_key=None, save=True):
64+
def create(cls, revision_id, phabricator_api_key=None):
5565
""" Land revision and create a Transplant item in storage. """
5666
revision = _get_revision(revision_id, phabricator_api_key)
5767
if not revision:
5868
raise RevisionNotFoundException(revision_id)
5969

70+
# save landing to make sure we've got the callback
71+
landing = cls(
72+
revision_id=revision_id,
73+
)
74+
landing.save()
75+
6076
trans = TransplantClient()
77+
callback = '%s/landings/%s/update?auth=%s' % (
78+
os.environ.get('HOST_URL'), landing.id, landing.auth
79+
)
6180
request_id = trans.land(
62-
'ldap_username@example.com', revision['repo_url']
81+
'ldap_username@example.com', revision['repo_url'], callback
6382
)
6483
if not request_id:
6584
raise LandingNotCreatedException
6685

67-
landing = cls(
68-
revision_id=revision_id,
69-
request_id=request_id,
70-
status=TRANSPLANT_JOB_STARTED
71-
)
72-
if save:
73-
landing.save(create=True)
86+
landing.request_id = request_id
87+
landing.save(create=True)
7488

7589
return landing
7690

‎landoapi/spec/swagger.yml

+45
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,51 @@ paths:
121121
schema:
122122
allOf:
123123
- $ref: '#/definitions/Error'
124+
/landings/{landing_id}/update:
125+
post:
126+
operationId: landoapi.api.landings.update
127+
description: |
128+
Sends request to transplant service and responds just the status code
129+
parameters:
130+
- name: data
131+
in: body
132+
description: |
133+
Retrieve status of the landing job
134+
required: true
135+
schema:
136+
type: object
137+
required:
138+
- request_id
139+
properties:
140+
request_id:
141+
type: integer
142+
- name: landing_id
143+
in: path
144+
type: string
145+
description: |
146+
The id of the landing to return
147+
required: true
148+
- name: auth
149+
in: query
150+
type: string
151+
description: |
152+
Random hashtag saved in schema to authenticate the pingback
153+
required: true
154+
responses:
155+
201:
156+
description: OK
157+
schema:
158+
$ref: '#/definitions/Landing'
159+
404:
160+
description: Landing does not exist
161+
schema:
162+
allOf:
163+
- $ref: '#/definitions/Error'
164+
default:
165+
description: Unexpected error
166+
schema:
167+
allOf:
168+
- $ref: '#/definitions/Error'
124169
/landings/{landing_id}:
125170
get:
126171
description: |

‎landoapi/transplant_client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self):
1717
self.api_url = os.getenv('TRANSPLANT_URL')
1818

1919
@requests_mock.mock()
20-
def land(self, ldap_username, tree, request):
20+
def land(self, ldap_username, tree, pingback, request):
2121
""" Sends a push request to Transplant API to land a revision.
2222
2323
Returns request_id received from Transplant API.

‎tests/test_landings.py

+69-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import pytest
66

77
from landoapi.models.storage import db as _db
8-
from landoapi.models.landing import (Landing, TRANSPLANT_JOB_STARTED)
8+
from landoapi.models.landing import (
9+
Landing, TRANSPLANT_JOB_STARTED, TRANSPLANT_JOB_FINISHED
10+
)
911

1012
from tests.canned_responses.phabricator.revisions import *
1113
from tests.canned_responses.lando_api.revisions import *
@@ -112,3 +114,69 @@ def test_get_jobs(db, client):
112114
response = client.get('/landings?revision_id=D1&status=finished')
113115
assert response.status_code == 200
114116
assert len(response.json) == 1
117+
118+
119+
def test_update_landing(db, client, mocker):
120+
m_hashtag = mocker.patch('landoapi.models.landing._hashtag')
121+
m_hashtag.return_value = 'abc'
122+
Landing(1, 'D1', 'started').save(True)
123+
124+
response = client.post(
125+
'/landings/1/update?auth=abc',
126+
data=json.dumps(
127+
{
128+
'request_id': 1,
129+
'status': TRANSPLANT_JOB_FINISHED,
130+
'revision_id': 'D1'
131+
}
132+
),
133+
content_type='application/json'
134+
)
135+
136+
assert response.status_code == 202
137+
response = client.get('/landings/1')
138+
assert response.json['status'] == TRANSPLANT_JOB_FINISHED
139+
140+
141+
def test_update_landing_bad_id(db, client, mocker):
142+
m_hashtag = mocker.patch('landoapi.models.landing._hashtag')
143+
m_hashtag.return_value = 'abc'
144+
Landing(1, 'D1', 'started').save(True)
145+
146+
response = client.post(
147+
'/landings/2/update?auth=abc',
148+
data=json.dumps(
149+
{
150+
'request_id': 1,
151+
'status': TRANSPLANT_JOB_FINISHED,
152+
'revision_id': 'D1'
153+
}
154+
),
155+
content_type='application/json'
156+
)
157+
158+
assert response.status_code == 404
159+
response = client.get('/landings/1')
160+
assert response.json['status'] == TRANSPLANT_JOB_STARTED
161+
162+
163+
def test_update_landing_bad_auth(db, client, mocker):
164+
m_hashtag = mocker.patch('landoapi.models.landing._hashtag')
165+
m_hashtag.return_value = 'abc'
166+
Landing(1, 'D1', 'started').save(True)
167+
168+
response = client.post(
169+
'/landings/1/update?auth=cde',
170+
data=json.dumps(
171+
{
172+
'request_id': 1,
173+
'status': TRANSPLANT_JOB_FINISHED,
174+
'revision_id': 'D1'
175+
}
176+
),
177+
content_type='application/json'
178+
)
179+
180+
assert response.status_code == 404
181+
response = client.get('/landings/1')
182+
assert response.json['status'] == TRANSPLANT_JOB_STARTED

0 commit comments

Comments
 (0)
Failed to load comments.