diff --git a/examples/falcon_mongoengine/README.md b/examples/falcon_mongoengine/README.md new file mode 100644 index 00000000..98e30fce --- /dev/null +++ b/examples/falcon_mongoengine/README.md @@ -0,0 +1,81 @@ + +Example Falcon+MongoEngine Project +================================ + +This example project demos integration between Graphene, Falcon and MongoEngine. + +Getting started +--------------- + +First you'll need to get the source of the project. Do this by cloning the +whole Graphene repository: + +```bash +# Get the example project code +git clone git@github.com:abawchen/graphene-mongo.git +cd graphene-mongo/examples/falcon_mongoengine +``` + +Create a virtual environment. + +```bash +# Create a virtualenv in which we can install the dependencies +virtualenv env +source env/bin/activate +``` + +Now we can install our dependencies: + +```bash +pip install -r requirements.txt +``` + +Setup a mongodb connection and create a database. +See the mongoengine connection details in the *app.py* file + +Start the server: + +On windows: +``` +waitress-serve --port=9000 falcon_mongoengine.app:app +``` + +On Linux: +``` +gunicorn -b 0.0.0.0:9000 falcon_mongoengine.app:app +``` + +Now head on over to +[http://127.0.0.1:9000/graphql?query=](http://127.0.0.1:9000/graphql?query=) +and run some queries! + +Example: + +``` +http://127.0.0.1:9000/graphql?query=query + { + categories(first: 1, name: "Travel") + { + edges { node { name color } } + } + } +``` + +``` +http://127.0.0.1:9000/graphql?query=query + { + bookmarks(first: 10) + { + pageInfo { startCursor endCursor hasNextPage hasPreviousPage } + edges { + node { name url category { name color } tags } + } + } + } +``` + +For tests run: + +```python +pytest -v +``` diff --git a/examples/falcon_mongoengine/__init__.py b/examples/falcon_mongoengine/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/falcon_mongoengine/api.py b/examples/falcon_mongoengine/api.py new file mode 100644 index 00000000..312b1111 --- /dev/null +++ b/examples/falcon_mongoengine/api.py @@ -0,0 +1,43 @@ +import json +import falcon +from .schema import schema + + +def set_graphql_allow_header( + req: falcon.Request, + resp: falcon.Response, + resource: object, +): + resp.set_header('Allow', 'GET, POST, OPTIONS') + + +class HelloWorldResource: + + def on_get(self, req, resp): + name = "Hello World!" + resp.status = falcon.HTTP_200 + resp.body = json.dumps({"respone": name, "status": resp.status}) + + def on_post(self, req, resp): + pass + + +@falcon.after(set_graphql_allow_header) +class GraphQLResource: + + def on_get(self, req, resp): + query = req.params['query'] + result = schema.execute(query) + + if result.data: + data_ret = {'data': result.data} + resp.status = falcon.HTTP_200 + resp.body = json.dumps(data_ret, separators=(',', ':')) + + def on_post(self, req, resp): + query = req.params['query'] + result = schema.execute(query) + if result.data: + data_ret = {'data': result.data} + resp.status = falcon.HTTP_200 + resp.body = json.dumps(data_ret, separators=(',', ':')) diff --git a/examples/falcon_mongoengine/app.py b/examples/falcon_mongoengine/app.py new file mode 100644 index 00000000..82a4911e --- /dev/null +++ b/examples/falcon_mongoengine/app.py @@ -0,0 +1,12 @@ +import falcon +from mongoengine import connect +from .api import GraphQLResource, HelloWorldResource + +connect('bookmarks_db', host='127.0.0.1', port=27017) +app = application = falcon.API() + +helloWorld = HelloWorldResource() +graphQL = GraphQLResource() + +app.add_route('/', helloWorld) +app.add_route('/graphql', graphQL) diff --git a/examples/falcon_mongoengine/models.py b/examples/falcon_mongoengine/models.py new file mode 100644 index 00000000..7b5241c6 --- /dev/null +++ b/examples/falcon_mongoengine/models.py @@ -0,0 +1,16 @@ +from mongoengine import Document, CASCADE +from mongoengine.fields import StringField, ListField, ReferenceField + + +class Category(Document): + meta = {'collection': 'category'} + name = StringField(max_length=140, required=True) + color = StringField(max_length=7, required=True) + + +class Bookmark(Document): + meta = {'collection': 'bookmark'} + name = StringField(required=True) + url = StringField(required=True) + category = ReferenceField('Category', reverse_delete_rule=CASCADE) + tags = ListField(StringField(max_length=50)) diff --git a/examples/falcon_mongoengine/pytest.ini b/examples/falcon_mongoengine/pytest.ini new file mode 100644 index 00000000..f912902b --- /dev/null +++ b/examples/falcon_mongoengine/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +python_files = tests.py test_*.py *_tests.py \ No newline at end of file diff --git a/examples/falcon_mongoengine/requirements.txt b/examples/falcon_mongoengine/requirements.txt new file mode 100644 index 00000000..a7a34869 --- /dev/null +++ b/examples/falcon_mongoengine/requirements.txt @@ -0,0 +1,6 @@ +falcon==2.0.0 +mongoengine==0.17.0 +graphene-mongo +waitress==1.3.0 +pytest==4.6.3 +mongomock==3.16.0 \ No newline at end of file diff --git a/examples/falcon_mongoengine/schema.py b/examples/falcon_mongoengine/schema.py new file mode 100644 index 00000000..4fdcd014 --- /dev/null +++ b/examples/falcon_mongoengine/schema.py @@ -0,0 +1,11 @@ +import graphene +from graphene_mongo.fields import MongoengineConnectionField +from .types import CategoryType, BookmarkType + + +class Query(graphene.ObjectType): + categories = MongoengineConnectionField(CategoryType) + bookmarks = MongoengineConnectionField(BookmarkType) + + +schema = graphene.Schema(query=Query, types=[CategoryType, BookmarkType]) diff --git a/examples/falcon_mongoengine/tests/__init__.py b/examples/falcon_mongoengine/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/falcon_mongoengine/tests/fixtures.py b/examples/falcon_mongoengine/tests/fixtures.py new file mode 100644 index 00000000..b1805f8f --- /dev/null +++ b/examples/falcon_mongoengine/tests/fixtures.py @@ -0,0 +1,51 @@ +import pytest +from examples.falcon_mongoengine.models import Category, Bookmark + + +def fixture_category_data(): + Category.drop_collection() + category_one = Category( + name='Travel', + color='#ed008c' + ) + category_one.save() + + category_two = Category( + name='Work', + color='#1769ff' + ) + category_two.save() + + return category_one, category_two + + +@pytest.fixture(scope='module') +def fixtures_data(): + category_one, category_two = fixture_category_data() + + Bookmark.drop_collection() + bookmark_one = Bookmark( + name='Travel tips', + url='https://www.traveltips.test', + category=category_one, + tags=["travel", "tips", "howto", ] + ) + bookmark_one.save() + + bookmark_two = Bookmark( + name='DIY vacation', + url='https://www.diyvacation.test', + category=category_one, + tags=["travel", "diy", "holiday", "vacation", ] + ) + bookmark_two.save() + + bookmark_three = Bookmark( + name='Awesome python', + url='https://awesomelists.top/#repos/vinta/awesome-python', + category=category_two, + tags=["python", "dev", "awesome", "tutorial", ] + ) + bookmark_three.save() + + return True diff --git a/examples/falcon_mongoengine/tests/tests.py b/examples/falcon_mongoengine/tests/tests.py new file mode 100644 index 00000000..861decaf --- /dev/null +++ b/examples/falcon_mongoengine/tests/tests.py @@ -0,0 +1,173 @@ +import mongoengine +from graphene.test import Client +from examples.falcon_mongoengine.schema import schema +from .fixtures import fixtures_data + +mongoengine.connect('graphene-mongo-test', host='mongomock://localhost', alias='default') + + +def test_category_last_1_item_query(fixtures_data): + query = ''' + { + categories(last: 1){ + edges { + node { + name + color + } + } + } + }''' + + expected = { + "data": { + "categories": + { + "edges": [ + { + "node": { + "name": "Work", + "color": "#1769ff", + } + } + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected + + +def test_category_filter_item_query(fixtures_data): + query = ''' + { + categories(name: "Work"){ + edges { + node { + name + color + } + } + } + }''' + + expected = { + "data": { + "categories": + { + "edges": [ + { + "node": { + "name": "Work", + "color": "#1769ff", + } + } + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected + + +def test_bookmarks_first_2_items_query(fixtures_data): + query = ''' + { + bookmarks(first: 2){ + edges { + node { + name + url + category { + name + color + } + tags + } + } + } + }''' + + expected = { + "data": { + "bookmarks": + { + "edges": [ + { + "node": { + "name": "Travel tips", + "url": "https://www.traveltips.test", + "category": { + 'name': 'Travel', + 'color': '#ed008c' + }, + "tags": ["travel", "tips", "howto", ] + } + }, + { + "node": { + "name": "DIY vacation", + "url": "https://www.diyvacation.test", + "category": { + 'name': 'Travel', + 'color': '#ed008c' + }, + "tags": ["travel", "diy", "holiday", "vacation", ] + } + } + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected + + +def test_bookmarks_filter_items_query(fixtures_data): + query = ''' + { + bookmarks(first: 1, name: "Awesome python"){ + edges { + node { + name + url + category { + name + color + } + tags + } + } + } + }''' + + expected = { + "data": { + "bookmarks": + { + "edges": [ + { + "node": { + "name": "Awesome python", + "url": "https://awesomelists.top/#repos/vinta/awesome-python", + "category": { + 'name': 'Work', + 'color': '#1769ff' + }, + "tags": ["python", "dev", "awesome", "tutorial", ] + } + } + ] + } + } + } + + client = Client(schema) + result = client.execute(query) + assert result == expected diff --git a/examples/falcon_mongoengine/types.py b/examples/falcon_mongoengine/types.py new file mode 100644 index 00000000..2e824cb9 --- /dev/null +++ b/examples/falcon_mongoengine/types.py @@ -0,0 +1,17 @@ +import graphene +from graphene import relay +from graphene_mongo import MongoengineObjectType + +from .models import Category, Bookmark + + +class CategoryType(MongoengineObjectType): + class Meta: + model = Category + interfaces = (relay.Node,) + + +class BookmarkType(MongoengineObjectType): + class Meta: + model = Bookmark + interfaces = (relay.Node,)