Comments (14)
Personally, I don't like that jsonapi doesn't provide a means to perform rpc as it's strictly focused on CRUD.
Anyway, would it have been possible for you to create your objects in init, for example, adding extra arguments in **kwargs and deleting them befor instantiating the sqla model?, eg.
def __init__(self, *args, **kwargs):
some_arg = kwargs.pop("some_arg", None)
if not some_arg is None: self.do_stuff() # do the rpc stuff
super().__init__(self, *args, **kwargs)
this way you can do a POST and the result will be a regular jsonapi response.
Another option may be to return a HTTP 201 and a Location header like in https://jsonapi.org/format/#crud-creating-responses-201
It is possible to return whatever you want by wrapping your request in a decorator, for example
import json
def test_decorator(f):
@wraps(f)
def fwrapper(*args, **kwargs):
result = f(*args, **kwargs)
result.status_code = 201
result.headers['Location'] = 'https://blah/bleh'
result.data = json.dumps({'hoho' : 'ddd' })
return result
return fwrapper
class Book(SAFRSBase, db.Model):
"""
description: Book description
"""
__tablename__ = "Books"
id = db.Column(db.String, primary_key=True)
title = db.Column(db.String, default="")
reader_id = db.Column(db.String, db.ForeignKey("People.id"))
author_id = db.Column(db.String, db.ForeignKey("People.id"))
publisher_id = db.Column(db.String, db.ForeignKey("Publishers.id"))
publisher = db.relationship("Publisher", back_populates="books")
reviews = db.relationship(
"Review", backref="book", cascade="save-update, merge, delete, delete-orphan"
)
custom_decorators = [test_decorator]
@classmethod
@jsonapi_rpc(http_methods=["GET"])
def get_by_name(cls, *args, **kwargs):
"""
description : Generate and return a Thing based on name
args:
- name: name
type: string
default: xx
pageable: false
"""
return { "result" : 1}
Can you provide some code so I can see what it is you're trying to achieve exactly, and maybe I can implement it generically
from safrs.
well, the example wraps the SAFRSRestMethodAPI.get
, not get_by_name
.
The point remains that it's possible to do whatever you want by implementing the appropriate hooks.
from safrs.
There's also SAFRSFormattedResponse
, like in
@classmethod
@jsonapi_rpc(http_methods=['POST'])
def startswith(cls, **kwargs):
"""
pageable: True
description : lookup column names
args:
name: ""
"""
# from .jsonapi import SAFRSFormattedResponse, paginate, jsonapi_format_response
result = cls
response = SAFRSFormattedResponse()
try:
instances = result.query
links, instances, count = paginate(instances)
data = [item for item in instances]
meta = {}
errors = None
response.response = jsonapi_format_response(data, meta, links, errors, count)
except Exception as exc:
raise GenericError("Failed to execute query {}".format(exc))
for key, value in kwargs.items():
column = getattr(cls, key, None)
if not column:
raise ValidationError('Invalid Column "{}"'.format(key))
try:
instances = result.query.filter(column.like(value + "%"))
links, instances, count = paginate(instances)
data = [item for item in instances]
meta = {}
errors = None
response.response = jsonapi_format_response(data, meta, links, errors, count)
except Exception as exc:
raise GenericError("Failed to execute query {}".format(exc))
return response
Line 62 in efef060
from safrs.
Thank you for the pointers, I've experimented a bit but didn't make it all the way..
Doing hacks in __init__
feels kinda dirty, and doesn't allow for correct swagger docs.
A 201 redirect could be a pretty good workaround, but it's still a workaround - needed because I can't form the response of my custom endpoint the way I want. Still, I'm not sure how I would pull it off?
I tried using a SAFRSFormattedResponse
and jsonapi_format_response
like in startswith
, like so:
response = SAFRSFormattedResponse()
result = jsonapi_format_response(thing, meta={}, count=1)
response.response = result
return response
...and that gives me a proper jsonapi-structure for the instance, but: the structure is still wrapped in the "meta": {"result"
-structure:
{
"meta": {
"result": {
"data": {
"type": "thing",
"attributes": {
"name": "test"
...
Do you agree that there should be a way to control this response structure, or do you need a more complete example use case?
from safrs.
Hi,
The startswith is implemented here as a POST request
http://thomaxxl.pythonanywhere.com/api#!/People/InvokePersonstartswith_0 , it does return the data[] , I'll figure out why it doesn't work for GET.
from safrs.
I commited some updates, can you check this example and see if it makes sense to you?
safrs/examples/demo_pythonanywhere_com.py
Lines 106 to 132 in fcc5d6f
The example runs here:
http://thomaxxl.pythonanywhere.com/api#!/People/InvokePersonmyrpc_0
Apparently query string arguments were already implemented, using the "paramaters" docstring key. I changed it a bit however.
I'll add some documentation later.
from safrs.
Firstly, thanks for the fix. Returning a SAFRSFormattedResponse
now works as expected 👍
Secondly, some thoughts on the doc structure (do with it what you will):
I don't think it's exactly self explanatory; args
represent keys in post body that are mapped to **kwargs
, and parameters
represent query parameters that are mapped to *args
, ..right?
Maybe they should just be called body_schema
and queryargs
or something?
Or maybe it'd make more sense to use raw swagger-syntax and just pass it through? Passing required parameters to *args
and optionals to **kwargs
? (This way you could just refer to the openapi/swagger docs for syntax). i.e:
@classmethod
@jsonapi_rpc(http_methods=["GET"])
def get_by_name(cls, some_query_parameter, some_body_key=None):
"""
description : Generate and return a Thing based on name
parameters:
- name: some_query_parameter
type : string
in: query
required: true
- name: some_body_key
type : integer
in: body
description: Some body key
"""
Just some ideas since you asked if it made sense :)
from safrs.
Hi, can you check if the latest version still works for you? I'm going to release it like this for now.
from safrs.
Sure, tomorrow I'll be able to check it out. I'll get back to you then.
from safrs.
Tried the latest version/commit and it still works for my use cases at least :)
from safrs.
v2.2.2
from safrs.
Hello again! Sorry to say this didn't work all the way. Once I turned off some debugging it doesn't work anymore because of this: https://github.com/thomaxxl/safrs/blob/master/safrs/json_encoder.py#L77.
WARNING safrs: Unknown obj type "<class 'safrs.json_encoder.SAFRSFormattedResponse'>" for <safrs.json_encoder.SAFRSFormattedResponse object at 0x7f641bccd358>
(Also related lines 83 and 85 are duplicates?)
from safrs.
Hi,
can you try again with the latest commit please?
from safrs.
Yes, that'll work! Thanks :)
Edit: oh, right, I'm still running in debug mode.. should be ok though. Will try again..
Edit2: yep, works 👍
from safrs.
Related Issues (20)
- Invalid swagger for "properties" under "definitions" breaks defintions display and validation HOT 7
- Patch method unreliable? HOT 2
- Read-only Columns HOT 2
- Flask-SQLAlchemy weakref KeyError
- in version 3.0 _s_count function does not apply filters to query HOT 1
- `count` property value is wrong for `GET` methods HOT 1
- Unable to delete entries HOT 5
- overriding methods does not work HOT 2
- extend API with custom classes HOT 1
- requirements.txt too restrictive HOT 1
- Duplicate definitions generated HOT 5
- safrs doesn't work in python 3.9 - TypeError HOT 4
- expose_existing starts but no swagger.json found HOT 1
- Run in example demo_relationship.py creating users or books in flask_admin returns Integrity Errors HOT 17
- Doubts about SAFRS HOT 2
- JSONAPI relationship missing in the swagger documentation HOT 4
- Swagger generation removing plurals HOT 1
- Flask 2.0 / Flask-restful "ImportError: cannot import name '_endpoint_from_view_func'" HOT 1
- Possible issue with BIGINT primary keys? HOT 5
- Make many to many relationship methods extendable HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from safrs.