graphql-example
Advanced tools
Sorry, the diff of this file is too big to display
| Metadata-Version: 1.1 | ||
| Name: graphql-example | ||
| Version: 0.3.1 | ||
| Version: 0.3.2 | ||
| Summary: A simple graphql server | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/knowsuchagency/graphql-example |
@@ -27,2 +27,3 @@ attrs | ||
| jupyter | ||
| jupyter-contrib-nbextensions | ||
@@ -45,2 +46,3 @@ [development] | ||
| jupyter | ||
| jupyter-contrib-nbextensions | ||
@@ -63,2 +65,3 @@ [test] | ||
| jupyter | ||
| jupyter-contrib-nbextensions | ||
@@ -81,1 +84,2 @@ [testing] | ||
| jupyter | ||
| jupyter-contrib-nbextensions |
@@ -12,2 +12,4 @@ LICENSE | ||
| graphql_example/graphql_example.py | ||
| graphql_example/graphql_example.slides.html | ||
| graphql_example/log.json | ||
| graphql_example/logging_utilities.py | ||
@@ -14,0 +16,0 @@ graphql_example/model.py |
@@ -7,2 +7,2 @@ # -*- coding: utf-8 -*- | ||
| __email__ = 'knowsuchagency@gmail.com' | ||
| __version__ = '0.3.1' | ||
| __version__ = '0.3.2' |
| # coding: utf-8 | ||
| # In[6]: | ||
| # In[1]: | ||
@@ -58,22 +58,30 @@ | ||
| # # An example graphql backend implented in aiohttp | ||
| # # graphql + aiohttp | ||
| # ## Installation - requires Python 3.6 | ||
| # | ||
| # `pip install graphql-example` | ||
| # preferably within a virtualenv: | ||
| # | ||
| # `pip3 install graphql-example` | ||
| # | ||
| # ### to run the web-app | ||
| # | ||
| # (after pip install) | ||
| # | ||
| # `graphql_example runserver` | ||
| # | ||
| # ## to run tests | ||
| # ### to run tests | ||
| # | ||
| # `pip install graphql-example[dev]` | ||
| # | ||
| # `graphql_example test` | ||
| # ``` | ||
| # git clone https://github.com/knowsuchagency/graphql-example | ||
| # pip3 install .[dev] | ||
| # fab test | ||
| # ``` | ||
| # ### In the beginning... there was REST. | ||
| # (really Genesis, shortly thereafter followed by SOAP, but we'll ignore that for now) | ||
| # Really Genesis, shortly thereafter followed by SOAP | ||
| # ### The Model | ||
| # Let's say we had a simple data model such as the following: | ||
@@ -83,3 +91,3 @@ # | ||
| # In[7]: | ||
| # In[2]: | ||
@@ -100,3 +108,3 @@ | ||
| # In a RESTful server, the endpoints used to retrieve that data might look like the following | ||
| # In a RESTful server, the endpoints used to retrieve that data might look something like this: | ||
| # | ||
@@ -149,3 +157,3 @@ # --- | ||
| # However we may still have some things to consider: | ||
| # We still have some things to consider... | ||
| # | ||
@@ -184,3 +192,3 @@ # * What happens if we type `/rest/authors?age=34&no_books=True` or `/rest/authors?age=34&no_books=t`? In general, how do we define and interpret the arguments passed as query params and how to we enforce that contract with our clients and give them helpful feedback when mistakes are made? | ||
| # But really, it is first-and-foremost a declarative language specification for client-side data fetching. | ||
| # But really, GraphQL is first-and-foremost a declarative language specification for client-side data fetching. | ||
| # | ||
@@ -235,3 +243,3 @@ # And, despite the snark, the buzz is important. GraphQL is created and backed by Facebook, and there is a rapidly growing community and ecosystem of libraries that make GraphQL compelling over other standards like JSON-API, Falcor, or OData. | ||
| # In[2]: | ||
| # In[3]: | ||
@@ -242,2 +250,4 @@ | ||
| """Redirect to greet route.""" | ||
| # this logging sexiness is a talk for another time | ||
| # but it's a thin wrapper around eliot.start_action | ||
| with log_request(request): | ||
@@ -285,5 +295,34 @@ | ||
| # In[5]: | ||
| # In[4]: | ||
| async def book(request): | ||
| """Return a single book for a given id.""" | ||
| connection = request.app['connection'] | ||
| with log_request(request): | ||
| try: | ||
| db_query = partial( | ||
| fetch_books, connection, id=int(request.match_info['id'])) | ||
| book, *_ = await request.loop.run_in_executor(None, db_query) | ||
| except ValueError: | ||
| book = None | ||
| if not book: | ||
| log_message('Book not found', id=request.match_info['id']) | ||
| raise web.HTTPNotFound | ||
| response = web.json_response(book) | ||
| with log_response(response): | ||
| return response | ||
| # In[ ]: | ||
| async def author(request): | ||
@@ -328,31 +367,5 @@ """Return a single author for a given id.""" | ||
| async def book(request): | ||
| """Return a single book for a given id.""" | ||
| # In[ ]: | ||
| connection = request.app['connection'] | ||
| with log_request(request): | ||
| try: | ||
| db_query = partial( | ||
| fetch_books, connection, id=int(request.match_info['id'])) | ||
| book, *_ = await request.loop.run_in_executor(None, db_query) | ||
| except ValueError: | ||
| book = None | ||
| if not book: | ||
| log_message('Book not found', id=request.match_info['id']) | ||
| raise web.HTTPNotFound | ||
| response = web.json_response(book) | ||
| with log_response(response): | ||
| return response | ||
| # In[4]: | ||
| async def books(request): | ||
@@ -388,2 +401,6 @@ """Return json response of books based on query params.""" | ||
| # In[5]: | ||
| async def authors(request): | ||
@@ -431,3 +448,3 @@ """Return json response of authors based on query params.""" | ||
| # In[6]: | ||
| # In[ ]: | ||
@@ -460,2 +477,6 @@ | ||
| # In[6]: | ||
| async def configure_graphql(app): | ||
@@ -462,0 +483,0 @@ """ |
@@ -1,22 +0,21 @@ | ||
| import os | ||
| try: | ||
| from graphql_example.logging_utilities import * | ||
| except ModuleNotFoundError: | ||
| from logging_utilities import * | ||
| async def drop_tables(app): | ||
| print('dropping tables') | ||
| with log_action('dropping tables'): | ||
| connection = app['connection'] | ||
| connection = app['connection'] | ||
| with connection: | ||
| connection.executescript(""" | ||
| DROP TABLE author; | ||
| DROP TABLE book; | ||
| """) | ||
| with connection: | ||
| connection.executescript(""" | ||
| DROP TABLE author; | ||
| DROP TABLE book; | ||
| """) | ||
| print('tables dropped') | ||
| # close the database connection on shutdown | ||
| async def close_db(app): | ||
| print('closing database connection') | ||
| app['connection'].close() | ||
| print('database connection closed') | ||
| with log_action('closing database connection'): | ||
| app['connection'].close() |
| import typing as T | ||
| import random | ||
| import sqlite3 | ||
| import tempfile | ||
| import pkg_resources | ||
| try: | ||
| from graphql_example.factories import book_factory, author_factory | ||
| from graphql_example.logging_utilities import * | ||
| except ModuleNotFoundError: | ||
| from factories import book_factory, author_factory | ||
| from logging_utilities import * | ||
@@ -17,8 +16,6 @@ from eliot import to_file, use_asyncio_context | ||
| async def configure_logging(app): | ||
| print('configuring logging') | ||
| use_asyncio_context() | ||
| logfile = app['config'].get( | ||
| 'logfile', 'log.json' | ||
| ) | ||
| logfile = app['config'].get('logfile', 'log.json') | ||
| to_file(open(logfile, 'w')) | ||
@@ -28,69 +25,68 @@ | ||
| async def configure_database(app): | ||
| # configure database | ||
| connection = sqlite3.connect( | ||
| app['config'].get('db') or ':memory:', | ||
| # here be dragons | ||
| check_same_thread=False) | ||
| # moar dragons | ||
| connection.execute('PRAGMA synchronous = OFF') | ||
| with log_action('configuring database'): | ||
| db = app['config'].get('db') or ':memory:' | ||
| connection = sqlite3.connect( | ||
| db, | ||
| # here be dragons | ||
| check_same_thread=False) | ||
| # moar dragons | ||
| connection.execute('PRAGMA synchronous = OFF') | ||
| app['connection'] = connection | ||
| app['connection'] = connection | ||
| log_message('connection established', db=db) | ||
| async def create_tables(app): | ||
| print('creating tables') | ||
| with log_action('creating tables'): | ||
| CREATE_TABLES = """ | ||
| CREATE_TABLES = """ | ||
| CREATE TABLE IF NOT EXISTS author( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||
| first_name TEXT NOT NULL, | ||
| last_name TEXT NOT NULL, | ||
| age INTEGER NOT NULL | ||
| ); | ||
| CREATE TABLE IF NOT EXISTS book( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||
| title TEXT NOT NULL, | ||
| published TEXT NOT NULL, | ||
| author_id INTEGER NOT NULL REFERENCES author(id) | ||
| ); | ||
| """ | ||
| CREATE TABLE IF NOT EXISTS author( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||
| first_name TEXT NOT NULL, | ||
| last_name TEXT NOT NULL, | ||
| age INTEGER NOT NULL | ||
| ); | ||
| app['connection'].executescript(CREATE_TABLES) | ||
| CREATE TABLE IF NOT EXISTS book( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||
| title TEXT NOT NULL, | ||
| published TEXT NOT NULL, | ||
| author_id INTEGER NOT NULL REFERENCES author(id) | ||
| ); | ||
| """ | ||
| app['connection'].executescript(CREATE_TABLES) | ||
| print('tables created') | ||
| async def seed_db(app): | ||
| print('seeding database') | ||
| with log_action('seeding database'): | ||
| connection = app['connection'] | ||
| connection = app['connection'] | ||
| authors: T.Tuple[T.Tuple] = tuple( | ||
| (a.first_name, a.last_name, a.age) | ||
| for a in (author_factory() for _ in range(200))) | ||
| authors: T.Tuple[T.Tuple] = tuple( | ||
| (a.first_name, a.last_name, a.age) | ||
| for a in (author_factory() for _ in range(200))) | ||
| with connection: | ||
| # insert 200 authors | ||
| connection.executemany( | ||
| 'INSERT INTO author (first_name, last_name, age) VALUES (?, ?, ?)', | ||
| authors) | ||
| with connection: | ||
| # insert 200 authors | ||
| connection.executemany( | ||
| 'INSERT INTO author (first_name, last_name, age) VALUES (?, ?, ?)', | ||
| authors) | ||
| # insert 500 books | ||
| author_ids = tuple( | ||
| id for id, *_ in connection.execute('SELECT id FROM author')) | ||
| # insert 500 books | ||
| author_ids = tuple( | ||
| id for id, *_ in connection.execute('SELECT id FROM author')) | ||
| books = [] | ||
| books = [] | ||
| for _ in range(500): | ||
| book = book_factory() | ||
| author_id = random.choice(author_ids) | ||
| books.append((book.title, book.published, author_id)) | ||
| for _ in range(500): | ||
| book = book_factory() | ||
| author_id = random.choice(author_ids) | ||
| books.append((book.title, book.published, author_id)) | ||
| connection.executemany( | ||
| 'INSERT INTO book (title, published, author_id) VALUES (?, ?, ?)', | ||
| books) | ||
| print('database seeded') | ||
| connection.executemany( | ||
| 'INSERT INTO book (title, published, author_id) VALUES (?, ?, ?)', | ||
| books) |
+1
-0
@@ -37,1 +37,2 @@ [[source]] | ||
| jupyter = "*" | ||
| jupyter-contrib-nbextensions = "*" |
+1
-1
| Metadata-Version: 1.1 | ||
| Name: graphql-example | ||
| Version: 0.3.1 | ||
| Version: 0.3.2 | ||
| Summary: A simple graphql server | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/knowsuchagency/graphql-example |
+55
-38
@@ -55,22 +55,30 @@ | ||
| # An example graphql backend implented in aiohttp | ||
| # graphql + aiohttp | ||
| ## Installation - requires Python 3.6 | ||
| `pip install graphql-example` | ||
| preferably within a virtualenv: | ||
| `pip3 install graphql-example` | ||
| ### to run the web-app | ||
| (after pip install) | ||
| `graphql_example runserver` | ||
| ## to run tests | ||
| ### to run tests | ||
| `pip install graphql-example[dev]` | ||
| ``` | ||
| git clone https://github.com/knowsuchagency/graphql-example | ||
| pip3 install .[dev] | ||
| fab test | ||
| ``` | ||
| `graphql_example test` | ||
| ### In the beginning... there was REST. | ||
| (really Genesis, shortly thereafter followed by SOAP, but we'll ignore that for now) | ||
| Really Genesis, shortly thereafter followed by SOAP | ||
| ### The Model | ||
| Let's say we had a simple data model such as the following: | ||
@@ -95,3 +103,3 @@ | ||
| In a RESTful server, the endpoints used to retrieve that data might look like the following | ||
| In a RESTful server, the endpoints used to retrieve that data might look something like this: | ||
@@ -144,3 +152,3 @@ --- | ||
| However we may still have some things to consider: | ||
| We still have some things to consider... | ||
@@ -179,3 +187,3 @@ * What happens if we type `/rest/authors?age=34&no_books=True` or `/rest/authors?age=34&no_books=t`? In general, how do we define and interpret the arguments passed as query params and how to we enforce that contract with our clients and give them helpful feedback when mistakes are made? | ||
| But really, it is first-and-foremost a declarative language specification for client-side data fetching. | ||
| But really, GraphQL is first-and-foremost a declarative language specification for client-side data fetching. | ||
@@ -235,2 +243,4 @@ And, despite the snark, the buzz is important. GraphQL is created and backed by Facebook, and there is a rapidly growing community and ecosystem of libraries that make GraphQL compelling over other standards like JSON-API, Falcor, or OData. | ||
| """Redirect to greet route.""" | ||
| # this logging sexiness is a talk for another time | ||
| # but it's a thin wrapper around eliot.start_action | ||
| with log_request(request): | ||
@@ -280,2 +290,30 @@ | ||
| ```python | ||
| async def book(request): | ||
| """Return a single book for a given id.""" | ||
| connection = request.app['connection'] | ||
| with log_request(request): | ||
| try: | ||
| db_query = partial( | ||
| fetch_books, connection, id=int(request.match_info['id'])) | ||
| book, *_ = await request.loop.run_in_executor(None, db_query) | ||
| except ValueError: | ||
| book = None | ||
| if not book: | ||
| log_message('Book not found', id=request.match_info['id']) | ||
| raise web.HTTPNotFound | ||
| response = web.json_response(book) | ||
| with log_response(response): | ||
| return response | ||
| ``` | ||
| ```python | ||
| async def author(request): | ||
@@ -318,29 +356,2 @@ """Return a single author for a given id.""" | ||
| return response | ||
| async def book(request): | ||
| """Return a single book for a given id.""" | ||
| connection = request.app['connection'] | ||
| with log_request(request): | ||
| try: | ||
| db_query = partial( | ||
| fetch_books, connection, id=int(request.match_info['id'])) | ||
| book, *_ = await request.loop.run_in_executor(None, db_query) | ||
| except ValueError: | ||
| book = None | ||
| if not book: | ||
| log_message('Book not found', id=request.match_info['id']) | ||
| raise web.HTTPNotFound | ||
| response = web.json_response(book) | ||
| with log_response(response): | ||
| return response | ||
| ``` | ||
@@ -379,3 +390,6 @@ | ||
| return response | ||
| ``` | ||
| ```python | ||
| async def authors(request): | ||
@@ -449,3 +463,6 @@ """Return json response of authors based on query params.""" | ||
| ``` | ||
| ```python | ||
| async def configure_graphql(app): | ||
@@ -648,3 +665,3 @@ """ | ||
| (Press CTRL+C to quit) | ||
| dropping table | ||
| dropping tables | ||
| tables dropped | ||
@@ -651,0 +668,0 @@ closing database connection |
+1
-1
| [metadata] | ||
| name = graphql-example | ||
| version = 0.3.1 | ||
| version = 0.3.2 | ||
| description = A simple graphql server | ||
@@ -5,0 +5,0 @@ long_description = file: README.rst |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
18499141
1.75%1152
0.17%252838
0