def test_router_get_subresource_attr(db_session): """Test getting an identified subresource attr works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums/1/tracks/14/name", query_params=query_params) assert result == "Spellbound"
def test_router_get_resource_collection_first_page(db_session): """Test getting the first page of a resource collection.""" query_params = {"sort": "album_id", "limit": 30} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums", query_params=query_params) assert len(result) == 30 assert result[0]["album_id"] == 1
def test_router_get_empty_non_list_child(db_session): """Test getting an empty many to one fails.""" resource = EmployeeResource(session=db_session) router = ModelResourceRouter(session=db_session, resource=resource) # note that employee 1 has no manager... with raises(ResourceNotFoundError): router.get(path="/employees/1/manager")
def test_router_get_resource_collection_ordered(db_session): """Test getting an ordered resource collection via a router.""" query_params = {"sort": "-album_id,title"} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums", query_params=query_params) assert len(result) == 347 assert result[0]["album_id"] == 347
def test_router_subresource_post_list(db_session): """Test that posting a subresource list via a router works.""" router = ModelResourceRouter(session=db_session, context={}) tracks = [ { "track_id": "4000", "name": "Test Track Seven", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", }, { "track_id": "4001", "name": "Test Track Eight", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", }, ] result = router.post("/albums/1/tracks", data=tracks) assert len(result) == 12
def test_router_subresource_post_list_fail(db_session): """Test posting a bad subresource list via a router fails.""" router = ModelResourceRouter(session=db_session, context={}) tracks = [ { "track_id": "ERROR", "name": "Test Track Seven", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", }, { "track_id": "4001", "name": "Test Track Eight", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", }, ] with raises(UnprocessableEntityError): router.post(path="/albums/1/tracks", data=tracks)
def test_router_get_subresource_collection_second_page(db_session): """Test getting the second page of a subresource collection.""" query_params = {"sort": "track_id", "limit": 5, "page": "2"} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums/1/tracks", query_params=query_params) assert len(result) == 5 assert result[0]["track_id"] == 10
def test_router_get_subresource_collection_ordered(db_session): """Test getting an ordered subresource collection via router.""" query_params = {"sort": "-track_id,name"} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums/1/tracks", query_params=query_params) assert len(result) == 10 assert result[0]["track_id"] == 14
def test_router_query_filter_parse_error(db_session): """Test a get with bad query filters raises an error.""" resource = AlbumResource(session=db_session) query_params = {"query": "[}"} router = ModelResourceRouter(session=db_session, resource=resource) with raises(BadRequestError): router.get(path="/albums", query_params=query_params)
def test_router_delete_attr(db_session): """Test deleting an identified resource attr works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("/customer/1/address", query_params=query_params) assert result is None
def test_router_subquery_parse_error(db_session): """Test a get with bad subquery params raises an error.""" query_params = {"tracks._subquery_": 5} resource = AlbumResource(session=db_session) router = ModelResourceRouter(session=db_session, resource=resource) with raises(BadRequestError): router.get(path="/album/1", query_params=query_params)
def test_router_get_subresource_attr_not_found(db_session): """Test getting a subresource attr fails with bad attr name.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) with raises(ResourceNotFoundError): router.get(path="/albums/1/tracks/14/dne", query_params=query_params)
def test_router_get_subresource_not_found(db_session): """Test getting an identified subresource fails with bad id.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) with raises(ResourceNotFoundError): router.get(path="/albums/1/tracks/1000000", query_params=query_params)
def test_router_delete_subresource_child(db_session): """Test deleting a child subresource works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("/tracks/1/genre", query_params=query_params) assert result is None track = db_session.query(Track).filter(Track.track_id == 1).first() assert track.genre is None
def test_router_delete_subresource_list(db_session): """Test deleting a subresource list works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("albums/1/tracks", query_params=query_params) assert result is None album = db_session.query(Album).filter(Album.album_id == 1).first() assert len(album.tracks) == 0
def test_router_get_subresource_collection_offset_no_strict(db_session): """Providing a bad offset subresource query_param ignored.""" query_params = {"offset": "test"} router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums/1/tracks", query_params=query_params, strict=False) assert result[0]["track_id"] == 1
def test_router_subquery_parse_error_ignore(db_session): """Test bad subquery params are ignored (non strict).""" query_params = {"tracks._subquery_": 5} resource = AlbumResource(session=db_session) router = ModelResourceRouter(session=db_session, resource=resource) result = router.get(path="/album/1", query_params=query_params, strict=False) assert result.get("album_id") == 1
def test_router_query_filter_parse_error_ignore(db_session): """Test a get with bad query filters is ignored (non strict).""" resource = AlbumResource(session=db_session) query_params = {"query": "[}"} router = ModelResourceRouter(session=db_session, resource=resource) result = router.get(path="/albums", query_params=query_params, strict=False) assert len(result) == 347
def test_router_delete_collection_filtered(db_session): """Test deleting a resource collection works with filters.""" query_params = {"track_id": "5"} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("/tracks", query_params=query_params) assert result is None track = db_session.query(Track).filter(Track.track_id == 5).first() assert track is None track = db_session.query(Track).filter(Track.track_id == 1).first() assert track is not None
def test_router_delete(db_session): """Test deleting an identified resource works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("/tracks/5", query_params=query_params) assert result is None track = db_session.query(Track).filter(Track.track_id == 5).first() assert track is None track = db_session.query(Track).filter(Track.track_id == 1).first() assert track is not None
def test_router_get_subresource_collection_filtered(db_session): """Test getting a subresource collection via a router works.""" query_params = { "track_id-lt": "10", "name-like": "Finger", "track_id-gt": 5, "track_id-gte": 6, "track_id-lte": 6, "track_id-eq": 6, "track_id": 6, "track_id-ne": 7, "query": json.dumps({"name": "Put The Finger On You"}) } router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums/1/tracks", query_params=query_params) assert len(result) == 1 assert result[0]["track_id"] == 6
def test_router_get_collection_filtered(db_session): """Test getting a resource collection via a router works.""" query_params = { "album_id-lt": "10", "title-like": "Big", "album_id-gt": 4, "album_id-gte": 5, "album_id-lte": 5, "album_id-eq": 5, "album_id": 5, "album_id-ne": 6, "query": json.dumps({"title": "Big Ones"}) } router = ModelResourceRouter(session=db_session, context={}) result = router.get("/albums", query_params=query_params) assert len(result) == 1 assert result[0]["album_id"] == 5
def test_router_dispatch_post(db_session): """Test that auto router dispatch to post works.""" router = ModelResourceRouter(session=db_session) data = { "track_id": "4000", "name": "Test Track Seven", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", } result = router.dispatcher(method="post", path="/tracks", data=data) assert result["track_id"] == 4000
def test_router_post(db_session): """Test that posting a resource via a router works.""" router = ModelResourceRouter(session=db_session, context={}) tracks = [{ "track_id": "4000", "name": "Test Track Seven", "media_type": { "media_type_id": "2" }, "genre": { "genre_id": "10" }, "composer": "Nick Repole", "milliseconds": "206009", "bytes": "3305166", "unit_price": "0.99", }] result = router.post("/tracks", data=tracks[0]) assert result["track_id"] == 4000
def test_router_put(db_session): """Test that putting a resource via a router works.""" router = ModelResourceRouter(session=db_session, context={}) track = { "album": "/tracks/1/album", "bytes": 11170334, "composer": "Angus Young, Malcolm Young, Brian Johnson", "genre": { "genre_id": 1 }, "media_type": { "media_type_id": 1 }, "milliseconds": 4000000, "name": "For Those About To Rock (We Salute You)", "playlists": "/tracks/1/playlists", "self": "/tracks/1", "track_id": 1, "unit_price": 0.99 } result = router.put("/tracks/1", data=track) assert result["milliseconds"] == 4000000
def test_router_dispatch_put(db_session): """Test that auto router dispatch to put works.""" router = ModelResourceRouter(session=db_session) data = { "album": "/tracks/1/album", "bytes": 11170334, "composer": "Angus Young, Malcolm Young, Brian Johnson", "genre": { "genre_id": 1 }, "media_type": { "media_type_id": 1 }, "milliseconds": 4000000, "name": "For Those About To Rock (We Salute You)", "playlists": "/tracks/1/playlists", "self": "/tracks/1", "track_id": 1, "unit_price": 0.99 } result = router.dispatcher(method="put", path="/tracks/1", data=data) assert result["milliseconds"] == 4000000
def api_router(path): """Generic API router. You'll probably want to be more specific with your routing. We're using the ModelResourceRouter, which automatically routes based on the class name of each Resource, and handles nested routing, querying, and updating automatically. """ # get your SQLAlchemy db session however you normally would db_session = get_scoped_db_session("video") # This should be some context related to the current request. # Note that context can be used by resources/schemas to help # handle things like permissions/access, and would typically # contain any user related info for this request. context = {} router = ModelResourceRouter(session=db_session, context=context) # query params are used to parse fields to include, embeds, # sorts, and filters. query_params = request.values.to_dict() status = 200 response_headers = {} if request.method.upper() == "POST": status = 201 result = router.dispatcher(request.method, path, query_params=query_params, data=request.json) if request.method.upper() == "OPTIONS": response_headers["Allow"] = ",".join(result) result = None if isinstance(result, ResourceCollection): # Handle providing prev, next, first, last page links header links = [] if result.current_page is not None: link_template = '<{link}>; rel="{rel}"' if result.first_page: links.append( link_template.format(link=url_for_other_page( result.first_page), rel="first")) if result.previous_page: links.append( link_template.format(link=url_for_other_page( result.previous_page), rel="prev")) if result.next_page: links.append( link_template.format(link=url_for_other_page( result.next_page), rel="next")) if result.last_page: links.append( link_template.format(link=url_for_other_page( result.last_page), rel="last")) links_str = ",".join(links) if links_str: response_headers["Link"] = links_str # Handle successful HEAD requests if request.method.upper() == "HEAD": result = None if result is not None: result = json.dumps(result) return Response(result, headers=response_headers, mimetype="application/json", status=status)
def test_router_delete_attr_not_found(db_session): """Test deleting an unfindable identified resource attr fails.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) with raises(ResourceNotFoundError): router.delete("/album/1/test", query_params=query_params)
def api_router(path): """Generic API router. You'll probably want to be more specific with your routing. We're using the ModelResourceRouter, which automatically routes based on the class name of each Resource, and handles nested routing, querying, and updating automatically. """ # This should be some context related to the current request. # Note that context can be used by resources/schemas to help # handle things like permissions/access, and would typically # contain any user related info for this request. context = {} router = ModelResourceRouter(session=g.db_session, context=context) # query params are used to parse fields to include, embeds, # sorts, and filters. query_params = request.values.to_dict() errors = None status = 200 response_headers = {} try: if request.method.upper() == "POST": status = 201 result = router.dispatcher( request.method, path, query_params=query_params, data=request.json) if result is None: status = 204 if request.method.upper() == "OPTIONS": response_headers["Allow"] = ",".join(result) result = None if isinstance(result, ResourceCollection): # Handle providing prev, next, first, last page links header links = [] if result.current_page is not None: link_template = '<{link}>; rel="{rel}"' if result.first_page: links.append(link_template.format( link=url_for_other_page(result.first_page), rel="first")) if result.previous_page: links.append(link_template.format( link=url_for_other_page(result.previous_page), rel="prev")) if result.next_page: links.append(link_template.format( link=url_for_other_page(result.next_page), rel="next")) if result.last_page: links.append(link_template.format( link=url_for_other_page(result.last_page), rel="last")) links_str = ",".join(links) if links_str: response_headers["Link"] = links_str # Handle successful HEAD requests if request.method.upper() == "HEAD": result = None if result is not None: result = json.dumps(result) return Response( result, headers=response_headers, mimetype="application/json", status=status ) except UnprocessableEntityError as exc: status = 433 errors = exc.errors message = exc.message code = exc.code except MethodNotAllowedError as exc: status = 405 message = exc.message code = exc.code except BadRequestError as exc: status = 400 message = exc.message code = exc.code except ResourceNotFoundError as exc: status = 404 message = exc.message code = exc.code if code is not None or message: if request.method.upper() == "HEAD": result = None else: result = {"message": message, "code": code} if errors: result["errors"] = errors return Response( json.dumps(result), mimetype="application/json", status=status)
def test_router_delete_collection(db_session): """Test deleting a resource collection works.""" query_params = {} router = ModelResourceRouter(session=db_session, context={}) result = router.delete("/invoices", query_params=query_params) assert result is None