From 3a37ad5eff9af642424c519159d74ce9abe7a926 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 10 Mar 2019 14:20:31 +0200 Subject: Add: PUT to effiapi to update member resource --- effiapi | 41 +++++++++++++++++++++++++++++++++++---- yarns/000.yarn | 61 +++++++++++++++++++++++++++++++++++++++++++++------------- yarns/lib.py | 5 +++++ 3 files changed, 90 insertions(+), 17 deletions(-) diff --git a/effiapi b/effiapi index 5575a37..f3894fc 100755 --- a/effiapi +++ b/effiapi @@ -92,7 +92,19 @@ class FakeHTTPAPI(HTTPAPI): return FakeResponse(201, headers, copy.deepcopy(body)) def PUT(self, url, headers, body): - raise NotImplementedError() + logging.debug( + 'FakeHTTPAPI.PUT url=%r headers=%r body=r', + url, headers, body) + + rid = headers.get('Muck-Id') + if not rid: + return FakeResponse(400, {}, 'No Muck-Id in request') + + self._memb[rid] = copy.deepcopy(body) + headers = { + 'Muck-Id': rid, + } + return FakeResponse(200, headers, copy.deepcopy(body)) def GET(self, url, headers=None, body=None): logging.info( @@ -183,12 +195,12 @@ class MuckAPI: raise MuckError('{} {}'.format(r.status_code, r.text)) return r.json() - def create(self, member): + def _write(self, member, func): url = self.url('/res') headers = self._get_headers() headers['Content-Type'] = 'application/json' - r = self._httpapi.POST(url, headers, json.dumps(member)) - logging.info('Show result: %s %s', r.status_code, r.text) + r = func(url, headers, json.dumps(member)) + logging.info('Write result: %s %s', r.status_code, r.text) if not r.ok: raise MuckError('{} {}'.format(r.status_code, r.text)) rid = r.headers.get('Muck-Id') @@ -196,6 +208,12 @@ class MuckAPI: raise MuckError('Muck did not return Muck-Id') return rid, r.json() + def create(self, member): + return self._write(member, self._httpapi.POST) + + def update(self, member): + return self._write(member, self._httpapi.PUT) + def search(self, cond): url = self.url('/search') headers = self._get_headers() @@ -235,6 +253,11 @@ class API: 'path': '/memb', 'callback': self._call(self._create), }, + { + 'method': 'PUT', + 'path': '/memb', + 'callback': self._call(self._update), + }, ] for route in routes: @@ -302,6 +325,16 @@ class API: rid, newobj = self._muck.create(obj) return response(201, rid, newobj) + def _update(self): + r = bottle.request + if r.content_type != 'application/json': + return response(400, None, 'wrong content type') + + obj = bottle.request.json + logging.info('UPDATE %r', repr(obj)) + rid, newobj = self._muck.update(obj) + return response(200, rid, newobj) + def response(status, rid, body): headers = { diff --git a/yarns/000.yarn b/yarns/000.yarn index 9225c70..adff964 100644 --- a/yarns/000.yarn +++ b/yarns/000.yarn @@ -42,19 +42,31 @@ member, to update and retrieve it, and to search memberships. WHEN admin requests GET /status THEN HTTP status is 200 - AND HTTP body matches { "resources": 0 } + AND body matches { "resources": 0 } WHEN admin requests POST /memb with body { "fullname": "James Bond" } THEN HTTP status is 201 - AND the member id is ID + AND remember header Muck-Id as ID + AND remember header Muck-Revision as REV1 WHEN admin requests GET /status THEN HTTP status is 200 - AND HTTP body matches { "resources": 1 } + AND body matches { "resources": 1 } WHEN admin requests GET /memb with header Muck-Id: ${ID} THEN HTTP status is 200 - AND HTTP body matches { "fullname": "James Bond" } + AND body matches { "fullname": "James Bond" } + AND header Muck-Revision is ${REV1} + + WHEN admin requests PUT /memb with id ${ID}, revision ${REV1}, + ... and body { "fullname": "Alfred Pennyworth" } + THEN HTTP status is 200 + AND remember header Muck-Revision as REV2 + + WHEN admin requests GET /memb with header Muck-Id: ${ID} + THEN HTTP status is 200 + AND body matches { "fullname": "Alfred Pennyworth" } + AND header Muck-Revision is ${REV2} FINALLY Effiapi is terminated @@ -86,6 +98,19 @@ TODO: IMPLEMENTS WHEN admin requests GET /status effiapi.GET('/status', {}, None) + IMPLEMENTS WHEN admin requests PUT /memb with id (\S+), revision (\S+), and body (.+) + rid = get_expanded_match() + rev = get_expanded_match() + body = get_json_match() + print('rid', repr(rid)) + print('rev', repr(rev)) + print('body', repr(body)) + headers = { + 'Muck-Id': rid, + 'Muck-Revision': rev, + } + effiapi.PUT('/memb', headers, body) + IMPLEMENTS WHEN admin requests GET /memb with header (\S+): (\S+) header = get_next_match() print('header', repr(header)) @@ -102,14 +127,6 @@ TODO: ## Inspect HTTP responses - IMPLEMENTS THEN the member id is (\S+) - print('member id') - name = get_next_match() - print 'name', repr(name), name - value = effiapi.get_header('Muck-Id') - print 'value', repr(value) - save_for_expansion(name, value) - IMPLEMENTS THEN HTTP status is (\d+) expected = int(get_next_match()) actual = effiapi.get_status_code() @@ -117,7 +134,25 @@ TODO: print 'expecting:', repr(expected) assertEqual(effiapi.get_status_code(), expected) - IMPLEMENTS THEN HTTP body matches (.+) + IMPLEMENTS THEN remember header (\S+) as (.+) + header = get_next_match() + varname = get_next_match() + value = effiapi.get_header(header) + print 'header:', repr(header) + print 'value:', repr(value) + print 'varname:', repr(varname) + save_for_expansion(varname, value) + + IMPLEMENTS THEN header (\S+) is (.+) + header = get_next_match() + expected = get_expanded_match() + actual = effiapi.get_header(header) + print 'header:', repr(header) + print 'expected:', repr(expected) + print 'actual:', repr(actual) + assertEqual(actual, expected) + + IMPLEMENTS THEN body matches (.+) expected = get_json_match() actual = effiapi.get_json_body() print 'expected:', expected diff --git a/yarns/lib.py b/yarns/lib.py index a776fda..100511e 100644 --- a/yarns/lib.py +++ b/yarns/lib.py @@ -96,6 +96,11 @@ class EffiAPI: body = json.dumps(body) self.request(requests.post, path, headers, body) + def PUT(self, path, headers, body): + headers['Content-Type'] = 'application/json' + body = json.dumps(body) + self.request(requests.put, path, headers, body) + def GET(self, path, headers, body): self.request(requests.get, path, headers, body) -- cgit v1.2.1