From 5416a2570e7167ca2e62352dda10d2f371b994a7 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Mon, 23 Apr 2018 17:36:22 -0700 Subject: start work on api client --- fatcat/api_client.py | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ fatcat_client.py | 37 ++++++++++++++++++ tests/api_client.py | 14 +++++++ tests/fixtures.py | 64 +++++++++++++++--------------- tests/test_fixtures.py | 29 ++++++++++++++ 5 files changed, 215 insertions(+), 32 deletions(-) create mode 100644 fatcat/api_client.py create mode 100755 fatcat_client.py create mode 100644 tests/api_client.py create mode 100644 tests/test_fixtures.py diff --git a/fatcat/api_client.py b/fatcat/api_client.py new file mode 100644 index 00000000..12c70407 --- /dev/null +++ b/fatcat/api_client.py @@ -0,0 +1,103 @@ + +import json +import requests + + +class FatCatApiClient: + + def __init__(self, host_url): + self.host_url = host_url + self.session = requests.Session() + + + def get(self, path): + return self.session.get(self.host_url + path) + + + def post(self, path, data=None, headers=None): + hdrs = {"content-type": "application/json"} + if headers: + hdrs.update(headers) + #if type(data) == dict: + # data = json.dumps(data, indent=None).encode('utf-8') + return self.session.post(self.host_url + path, json=data, headers=hdrs) + + + def import_crossref_file(self, json_file): + with open(json_file, 'r') as file: + for line in file: + obj = json.loads(line) + self.import_crossref_dict(obj) + + + def new_edit_group(self): + rv = self.post('/v0/editgroup') + assert rv.status_code == 200 + editgroup_id = rv.json()['id'] + return editgroup_id + + + def import_crossref_dict(self, meta): + + # creators + creators = [] + for am in meta['author']: + c = dict(name="{} {}".format(am['given'], am['family']), + sortname="{}, {}".format(am['family'], am['given']), + orcid=None) + creators.append(c) + + # container + container = dict( + issn=meta['ISSN'][0], + name=meta['container-title'][0], + #container_id=None, + #sortname=meta['short-container-title'][0]) + publisher=meta['publisher']) + #rv = self.post('/v0/container', data=container) + #assert rv.status_code == 200 + #container_id = rv.json()['id'] + + # references + refs = [] + for i, rm in enumerate(meta.get('reference', [])): + ref = dict( + doi=rm.get("DOI", None), + index=i+1, + # TODO: how to generate a proper stub here from k/v metadata? + stub="| ".join(rm.values())) + refs.append(ref) + + # work and release + title = meta['title'][0] + rv = self.post('/v0/work', + data=dict(title=title)) #work_type="book" + assert rv.status_code == 200 + work_id = rv.json()['id'] + + rv = self.post('/v0/release', data=dict( + title=title, + work=work_id, + # XXX: creators=creators, + # XXX: refs=refs, + #container=container_id, + release_type=meta['type'], + doi=meta['DOI'], + date=meta['created']['date-time'], + license=meta.get('license', [dict(URL=None)])[0]['URL'] or None, + issue=meta.get('issue', None), + volume=meta.get('volume', None), + pages=meta.get('page', None), + extra=dict(crossref={ + 'links': meta.get('link', []), + 'subject': meta['subject'], + 'type': meta['type'], + 'alternative-id': meta.get('alternative-id', [])}))) + assert rv.status_code == 200 + release_id = rv.json()['id'] + + + def health(self): + rv = self.get("/health") + assert rv.status_code == 200 + return rv.json() diff --git a/fatcat_client.py b/fatcat_client.py new file mode 100755 index 00000000..4b3e1cc7 --- /dev/null +++ b/fatcat_client.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +import argparse +from fatcat.api_client import FatCatApiClient + +def import_crossref(args): + fcc = FatCatApiClient(args.host_url) + fcc.import_crossref_file(args.json_file) + +def health(args): + fcc = FatCatApiClient(args.host_url) + print(fcc.health()) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--debug', + action='store_true', + help="enable debugging interface") + parser.add_argument('--host-url', + default="http://localhost:8040", + help="connect to this host/port") + subparsers = parser.add_subparsers() + + sub_import_crossref = subparsers.add_parser('import-crossref', + aliases=['lc']) + sub_import_crossref.set_defaults(func=import_crossref) + sub_import_crossref.add_argument('json_file', + help="") + + sub_health = subparsers.add_parser('health') + sub_health.set_defaults(func=health) + + args = parser.parse_args() + args.func(args) + +if __name__ == '__main__': + main() diff --git a/tests/api_client.py b/tests/api_client.py new file mode 100644 index 00000000..37e3da56 --- /dev/null +++ b/tests/api_client.py @@ -0,0 +1,14 @@ + +import pytest +import fatcat.api_client +from fixtures import * + + +def test_client_health(api_client): + assert api_client.health() != None + + +def test_import_crossref(api_client): + api_client.import_crossref_file('tests/files/crossref-works.2018-01-21.badsample.json') + + # TODO: use API to check that entities actually created... diff --git a/tests/fixtures.py b/tests/fixtures.py index 04b4314a..30358a5c 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,23 +1,52 @@ +import os +import time +import json import pytest +import signal import fatcat import fatcat.sql from fatcat.models import * @pytest.fixture -def app(): +def full_app(): fatcat.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' fatcat.app.testing = True - fatcat.app.debug = True + fatcat.app.debug = False fatcat.db.session.remove() fatcat.db.drop_all() fatcat.db.create_all() fatcat.sql.populate_db() - return fatcat.app.test_client() + return fatcat.app + +@pytest.fixture +def app(full_app): + return full_app.test_client() @pytest.fixture def rich_app(app): + enrichen_test_app(app) + return app + + +@pytest.fixture(scope="function") +def api_client(full_app): + + pid = os.fork() + if pid == 0: + full_app.testing = False + full_app.run(host="localhost", port=8444, debug=False) + os._exit(0) + + time.sleep(0.2) + yield fatcat.api_client.FatCatApiClient("http://localhost:8444") + os.kill(pid, signal.SIGKILL) + + +## Helpers ################################################################## + +def enrichen_test_app(app): rv = app.post('/v0/editgroup', data=json.dumps(dict( @@ -113,35 +142,6 @@ def rich_app(app): headers={"content-type": "application/json"}) assert rv.status_code == 200 - return app - -def test_rich_app_fixture(rich_app): - app = rich_app - - assert ChangelogEntry.query.count() == 1 - - for cls in (WorkIdent, WorkRev, WorkEdit, - ContainerIdent, ContainerRev, ContainerEdit, - CreatorIdent, CreatorRev, CreatorEdit, - FileIdent, FileRev, FileEdit): - assert cls.query.count() == 1 - for cls in (ReleaseIdent, ReleaseRev, ReleaseEdit): - assert cls.query.count() == 2 - - for cls in (WorkIdent, - ContainerIdent, - CreatorIdent, - FileIdent): - assert cls.query.filter(cls.is_live==True).count() == 1 - assert ReleaseIdent.query.filter(ReleaseIdent.is_live==True).count() == 2 - - # test that editor's active edit group is now invalid - editor = Editor.query.first() - assert editor.active_edit_group == None - - -## Helpers ################################################################## - def check_entity_fields(e): for key in ('rev', 'is_live', 'redirect_id'): assert key in e diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py new file mode 100644 index 00000000..2ced3bb5 --- /dev/null +++ b/tests/test_fixtures.py @@ -0,0 +1,29 @@ + +import pytest +import fatcat.api_client +from fixtures import * + + +def test_rich_app_fixture(rich_app): + app = rich_app + + assert ChangelogEntry.query.count() == 1 + + for cls in (WorkIdent, WorkRev, WorkEdit, + ContainerIdent, ContainerRev, ContainerEdit, + CreatorIdent, CreatorRev, CreatorEdit, + FileIdent, FileRev, FileEdit): + assert cls.query.count() == 1 + for cls in (ReleaseIdent, ReleaseRev, ReleaseEdit): + assert cls.query.count() == 2 + + for cls in (WorkIdent, + ContainerIdent, + CreatorIdent, + FileIdent): + assert cls.query.filter(cls.is_live==True).count() == 1 + assert ReleaseIdent.query.filter(ReleaseIdent.is_live==True).count() == 2 + + # test that editor's active edit group is now invalid + editor = Editor.query.first() + assert editor.active_edit_group == None -- cgit v1.2.3