Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit be777ce

Browse files
committed
Merge branch 'master' of github.com:miLibris/flask-rest-jsonapi into develop
2 parents 403a66a + 472bfe5 commit be777ce

File tree

3 files changed

+130
-19
lines changed

3 files changed

+130
-19
lines changed

README.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
Flask-REST-JSONAPI
1212
##################
1313

14-
Flask-REST-JSONAPI is a flask extension to build REST api. It combines the power of `Flask-Restless <https://flask-restless.readthedocs.io/en/stable/>`_ and the flexibility of `Flask-RESTful <http://flask-restful-cn.readthedocs.io/en/0.3.5/a>`_ around a strong specification `JSONAPI 1.0 <http://jsonapi.org/>`_. This framework is design to quickly build REST api and to fit the complexity of real life projects with legacy data and multiple data storages.
14+
Flask-REST-JSONAPI is a flask extension for building REST APIs. It combines the power of `Flask-Restless <https://flask-restless.readthedocs.io/en/stable/>`_ and the flexibility of `Flask-RESTful <http://flask-restful-cn.readthedocs.io/en/0.3.5/a>`_ around a strong specification `JSONAPI 1.0 <http://jsonapi.org/>`_. This framework is designed to quickly build REST APIs and fit the complexity of real life projects with legacy data and multiple data storages.
1515

1616
Install
1717
=======
1818

1919
pip install Flask-REST-JSONAPI
2020

21-
A minimal api
21+
A minimal API
2222
=============
2323

2424
.. code-block:: python
@@ -67,19 +67,19 @@ A minimal api
6767
data_layer = {'session': db.session,
6868
'model': Person}
6969
70-
# Create the api
70+
# Create the API object
7171
api = Api(app)
7272
api.route(PersonList, 'person_list', '/persons')
7373
api.route(PersonDetail, 'person_detail', '/persons/<int:id>')
7474
75-
# start the flask loop
75+
# Start the flask loop
7676
if __name__ == '__main__':
7777
app.run()
7878
79-
This example provides this api:
79+
This example provides the following API structure:
8080

8181
======================== ====== ============= ===========================
82-
url method endpoint Usage
82+
URL method endpoint Usage
8383
======================== ====== ============= ===========================
8484
/persons GET person_list Get a collection of persons
8585
/persons POST person_list Create a person
@@ -91,18 +91,18 @@ url method endpoint Usage
9191
Flask-REST-JSONAPI vs `Flask-RESTful <http://flask-restful-cn.readthedocs.io/en/0.3.5/a>`_
9292
==========================================================================================
9393

94-
* Instead of Flask-RESTful, Flask-REST-JSONAPI provides a default implementation of get, post, patch and delete methods around a strong specification JSONAPI 1.0. Thanks to this you can build REST api very quickly.
95-
* Flask-REST-JSONAPI is as flexible as Flask-RESTful. You can rewrite every default methods implementation to make custom work like distributing object creation.
94+
* Instead of Flask-RESTful, Flask-REST-JSONAPI provides a default implementation of get, post, patch and delete methods around a strong specification JSONAPI 1.0. Thanks to this you can build REST API very quickly.
95+
* Flask-REST-JSONAPI is as flexible as Flask-RESTful. You can rewrite every default method implementation to make custom work like distributing object creation.
9696

9797
Flask-REST-JSONAPI vs `Flask-Restless <https://flask-restless.readthedocs.io/en/stable/>`_
9898
==========================================================================================
9999

100-
* Flask-REST-JSONAPI is a real implementation of JSONAPI 1.0 specification. So instead of Flask-Restless, Flask-REST-JSONAPI force you to create a real logical abstration over your data models with `Marshmallow <https://marshmallow.readthedocs.io/en/latest/>`_. So you can create complex resource over your data.
101-
* Instead of Flask-Restless, Flask-REST-JSONAPI can use any ORM or data storage through the data layer concept, not only `SQLAlchemy <http://www.sqlalchemy.org/>`_. A data layer is a CRUD interface between your resource and one or more data storage so you can fetch data from any data storage of your choice or create resource that use multiple data storage.
102-
* Like i said previously, Flask-REST-JSONAPI is a real implementation of JSONAPI 1.0 specification. So instead of Flask-Restless you can manage relationships via REST. You can create dedicated url to create a CRUD api to manage relationships.
100+
* Flask-REST-JSONAPI is a real implementation of JSONAPI 1.0 specification. So instead of Flask-Restless, Flask-REST-JSONAPI forces you to create a real logical abstration over your data models with `Marshmallow <https://marshmallow.readthedocs.io/en/latest/>`_. So you can create complex resource over your data.
101+
* Instead of Flask-Restless, Flask-REST-JSONAPI can use any ORM or data storage through the data layer concept, not only `SQLAlchemy <http://www.sqlalchemy.org/>`_. A data layer is a CRUD interface between your resource and one or more data storage so you can fetch data from any data storage of your choice or create resource that use multiple data storages.
102+
* Like I said previously, Flask-REST-JSONAPI is a real implementation of JSONAPI 1.0 specification. So instead of Flask-Restless you can manage relationships via REST. You can create dedicated URL to create a CRUD API to manage relationships.
103103
* Plus Flask-REST-JSONAPI helps you to design your application with strong separation between resource definition (schemas), resource management (resource class) and route definition to get a great organization of your source code.
104-
* Instead of Flask-Restless, Flask-REST-JSONAPI is highly customizable. For example you can entirely customize you urls, define multiple urls for the same resource manager,control serialization parameters of each methods and lot of very useful parameters.
105-
* Finally instead of Flask-Restless, Flask-REST-JSONAPI provides a great error handling system according to JSONAPI 1.0. Plus the exception handling system really helps the api developer to quickly find missing resources requirements.
104+
* Instead of Flask-Restless, Flask-REST-JSONAPI is highly customizable. For example you can entirely customize your URLs, define multiple URLs for the same resource manager, control serialization parameters of each method and lots of very useful parameters.
105+
* Finally instead of Flask-Restless, Flask-REST-JSONAPI provides a great error handling system according to JSONAPI 1.0. Plus the exception handling system really helps the API developer to quickly find missing resources requirements.
106106

107107
Documentation
108108
=============
@@ -112,4 +112,4 @@ Documentation available here: http://flask-rest-jsonapi.readthedocs.io/en/latest
112112
Thanks
113113
======
114114

115-
Flask, marshmallow, marshmallow_jsonapi, sqlalchemy, Flask-RESTful and Flask-Restless are awesome project. These librairies give me inspiration to create Flask-REST-JSONAPI so huge thanks to authors and contributors.
115+
Flask, marshmallow, marshmallow_jsonapi, sqlalchemy, Flask-RESTful and Flask-Restless are awesome projects. These libraries gave me inspiration to create Flask-REST-JSONAPI, so huge thanks to authors and contributors.

docs/index.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Flask-REST-JSONAPI
33

44
.. module:: flask_rest_jsonapi
55

6-
**Flask-REST-JSONAPI** is an extension for Flask that adds support for quickly building REST APIs with hudge flexibility around JSONAPI 1.0 specification. It is design to fit the complexity of real life environnement so Flask-REST-JSONAPI helps you to create a logicial abstraction of your data called "resource" and can interface any kind of ORMs or data storage through data layer concept.
6+
**Flask-REST-JSONAPI** is an extension for Flask that adds support for quickly building REST APIs with huge flexibility around the JSONAPI 1.0 specification. It is designed to fit the complexity of real life environments so Flask-REST-JSONAPI helps you to create a logical abstraction of your data called "resource" and can interface any kind of ORMs or data storage through data layer concept.
77

88
Main concepts
99
-------------
@@ -12,11 +12,11 @@ Main concepts
1212
:width: 900px
1313
:alt: Architecture
1414

15-
| * `JSON API 1.0 specification <http://jsonapi.org/>`_: it is a very popular specification about client server interactions for REST JSON API. It helps you to work in team because it is very precise and sharable. Thanks to this specification your api offers lot of features like a strong structure of request and response, filtering, pagination, sparse fieldsets, including related objects, great error formatting etc.
15+
| * `JSON API 1.0 specification <http://jsonapi.org/>`_: it is a very popular specification about client server interactions for REST JSON API. It helps you work in a team because it is very precise and sharable. Thanks to this specification your API offers lots of features like a strong structure of request and response, filtering, pagination, sparse fieldsets, including related objects, great error formatting etc.
1616
|
17-
| * **Logical data abstration**: you usually need to expose resources to clients that don't fit your data table architecture. For example sometimes you don't want to expose all attributes of a table, compute additional attributes or create a resource that use data from multiple data storage. Flask-REST-JSONAPI helps you to create a logical abstraction of your data with `Marshmallow <https://marshmallow.readthedocs.io/en/latest/>`_ / `marshmallow-jsonapi <https://marshmallow-jsonapi.readthedocs.io/>`_ so you can expose your data through a very flexible way.
17+
| * **Logical data abstration**: you usually need to expose resources to clients that don't fit your data table architecture. For example sometimes you don't want to expose all attributes of a table, compute additional attributes or create a resource that uses data from multiple data storages. Flask-REST-JSONAPI helps you create a logical abstraction of your data with `Marshmallow <https://marshmallow.readthedocs.io/en/latest/>`_ / `marshmallow-jsonapi <https://marshmallow-jsonapi.readthedocs.io/>`_ so you can expose your data through a very flexible way.
1818
|
19-
| * **Data layer**: the data layer is a CRUD interface between your resource manager and your data. Thanks to it you can use any data storage or ORMs. There is an already full featured data layer that use SQLAlchemy ORM but you can create and use your own custom data layer to use data from your data storage(s). You can even create a data layer that use multiple data storage and ORMs, send notifications or make any custom work during CRUD operations.
19+
| * **Data layer**: the data layer is a CRUD interface between your resource manager and your data. Thanks to it you can use any data storage or ORMs. There is an already full featured data layer that uses the SQLAlchemy ORM but you can create and use your own custom data layer to use data from your data storage(s). You can even create a data layer that uses multiple data storages and ORMs, send notifications or make any custom work during CRUD operations.
2020
2121
Features
2222
--------

tests/test_sqlalchemy_data_layer.py

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@
1010
from flask import Blueprint, make_response
1111
from marshmallow_jsonapi.flask import Schema, Relationship
1212
from marshmallow_jsonapi import fields
13+
from marshmallow import ValidationError
1314

1415
from flask_rest_jsonapi import Api, ResourceList, ResourceDetail, ResourceRelationship, JsonApiException
15-
from flask_rest_jsonapi.exceptions import RelationNotFound, InvalidSort
16+
from flask_rest_jsonapi.pagination import add_pagination_links
17+
from flask_rest_jsonapi.exceptions import RelationNotFound, InvalidSort, InvalidFilters, InvalidInclude, BadRequest
1618
from flask_rest_jsonapi.querystring import QueryStringManager as QSManager
1719
from flask_rest_jsonapi.data_layers.alchemy import SqlalchemyDataLayer
1820
from flask_rest_jsonapi.data_layers.base import BaseDataLayer
21+
from flask_rest_jsonapi.data_layers.filtering.alchemy import Node
22+
from flask_rest_jsonapi.schema import get_relationships
23+
import flask_rest_jsonapi.decorators
24+
import flask_rest_jsonapi.resource
25+
import flask_rest_jsonapi.schema
1926

2027

2128
@pytest.fixture(scope="module")
@@ -315,11 +322,115 @@ class get_object(object):
315322
})()
316323
})()
317324
})()
325+
318326
def __init__(self, kwargs):
319327
pass
320328
return get_object
321329

322330

331+
def test_add_pagination_links():
332+
qs = {'page[number]': '15', 'page[size]': '10'}
333+
qsm = QSManager(qs, None)
334+
add_pagination_links(dict(), 1000, qsm, str())
335+
336+
337+
def test_Node(person_model, person_schema, monkeypatch):
338+
from copy import deepcopy
339+
filt = {
340+
'val': '0000',
341+
'field': True,
342+
'not': dict(),
343+
'name': 'name',
344+
'op': 'eq',
345+
'strip': lambda: 's'
346+
}
347+
filt['not'] = deepcopy(filt)
348+
del filt['not']['not']
349+
n = Node(person_model,
350+
filt,
351+
None,
352+
person_schema)
353+
with pytest.raises(TypeError):
354+
# print(n.val is None and n.field is None)
355+
# # n.column
356+
n.resolve()
357+
with pytest.raises(AttributeError):
358+
n.model = None
359+
n.column
360+
with pytest.raises(InvalidFilters):
361+
n.model = person_model
362+
n.filter_['op'] = ''
363+
n.operator
364+
with pytest.raises(InvalidFilters):
365+
n.related_model
366+
with pytest.raises(InvalidFilters):
367+
n.related_schema
368+
369+
370+
def test_check_method_requirements(monkeypatch):
371+
self = type('self', (object,), dict())
372+
request = type('request', (object,), dict(method='GET'))
373+
monkeypatch.setattr(flask_rest_jsonapi.decorators, 'request', request)
374+
with pytest.raises(Exception):
375+
flask_rest_jsonapi.\
376+
decorators.check_method_requirements(lambda: 1)(self())
377+
378+
379+
def test_json_api_exception():
380+
JsonApiException(None, None, title='test', status='test')
381+
382+
383+
def test_query_string_manager(person_schema):
384+
query_string = {'page[slumber]': '3'}
385+
qsm = QSManager(query_string, person_schema)
386+
with pytest.raises(BadRequest):
387+
qsm.pagination
388+
qsm.qs['sort'] = 'computers'
389+
with pytest.raises(InvalidSort):
390+
qsm.sorting
391+
392+
393+
def test_resource(person_model, person_schema, session, monkeypatch):
394+
def schema_load_mock(*args):
395+
raise ValidationError(dict(errors=[dict(status=None, title=None)]))
396+
query_string = {'page[slumber]': '3'}
397+
app = type('app', (object,), dict(config=dict(DEBUG=True)))
398+
headers = {'Content-Type': 'application/vnd.api+json'}
399+
request = type('request', (object,), dict(method='POST',
400+
headers=headers,
401+
get_json=dict,
402+
args=query_string))
403+
dl = SqlalchemyDataLayer(dict(session=session, model=person_model))
404+
rl = ResourceList()
405+
rd = ResourceDetail()
406+
rl._data_layer = dl
407+
rl.schema = person_schema
408+
rd._data_layer = dl
409+
rd.schema = person_schema
410+
monkeypatch.setattr(flask_rest_jsonapi.resource, 'request', request)
411+
monkeypatch.setattr(flask_rest_jsonapi.resource, 'current_app', app)
412+
monkeypatch.setattr(flask_rest_jsonapi.decorators, 'request', request)
413+
monkeypatch.setattr(rl.schema, 'load', schema_load_mock)
414+
r = super(flask_rest_jsonapi.resource.Resource, ResourceList)\
415+
.__new__(ResourceList)
416+
with pytest.raises(Exception):
417+
r.dispatch_request()
418+
rl.post()
419+
rd.patch()
420+
421+
422+
def test_compute_schema(person_schema):
423+
query_string = {'page[number]': '3', 'fields[person]': list()}
424+
qsm = QSManager(query_string, person_schema)
425+
with pytest.raises(InvalidInclude):
426+
flask_rest_jsonapi.schema.compute_schema(
427+
person_schema, dict(), qsm, ['id']
428+
)
429+
s = flask_rest_jsonapi.schema.compute_schema(
430+
person_schema, dict(only=list()), qsm, list()
431+
)
432+
433+
323434
# test good cases
324435
def test_get_list(client, register_routes, person, person_2):
325436
with client:

0 commit comments

Comments
 (0)