Git Product home page Git Product logo

rest-framework's Introduction

Runboat Pre-commit Status Build Status codecov Translation Status

Rest Frameworks

This repository has nice modules to interact with Odoo using JSON and HTTP requests.

Available addons

addon version maintainers summary
base_rest 16.0.1.0.2 Develop your own high level REST APIs for Odoo thanks to this addon.
base_rest_auth_api_key 16.0.1.0.0 lmignon Base Rest: Add support for the auth_api_key security policy into the openapi documentation
base_rest_datamodel 16.0.1.0.0 Datamodel binding for base_rest
base_rest_demo 16.0.2.0.2 lmignon Demo addon for Base REST
base_rest_pydantic 16.0.2.0.1 Pydantic binding for base_rest
datamodel 16.0.1.0.1 lmignon This addon allows you to define simple data models supporting serialization/deserialization
extendable 16.0.1.0.1 lmignon Extendable classes registry loader for Odoo
extendable_fastapi 16.0.2.1.1 lmignon Allows the use of extendable into fastapi apps
fastapi 16.0.1.2.9 lmignon Odoo FastAPI endpoint
fastapi_auth_jwt 16.0.1.0.4 sbidoul JWT bearer token authentication for FastAPI.
fastapi_auth_jwt_demo 16.0.2.0.1 sbidoul Test/demo module for fastapi_auth_jwt.
graphql_base 16.0.1.0.1 sbidoul Base GraphQL/GraphiQL controller
graphql_demo 16.0.1.0.1 sbidoul GraphQL Demo
pydantic 16.0.1.0.0 lmignon Utility addon to ease mapping between Pydantic and Odoo models
rest_log 16.0.1.0.1 simahawk Track REST API calls into DB

Unported addons

addon version maintainers summary
base_rest_auth_jwt 15.0.1.1.0 (unported) lmignon Base Rest: Add support for the auth_jwt security policy into the openapi documentation
base_rest_auth_user_service 15.0.1.0.1 (unported) Login/logout from session using a REST call
model_serializer 15.0.1.2.0 (unported) fdegrave Automatically translate Odoo models into Datamodels for (de)serialization

Licenses

This repository is licensed under AGPL-3.0.

However, each module can have a totally different license, as long as they adhere to Odoo Community Association (OCA) policy. Consult each module's __manifest__.py file, which contains a license key that explains its license.


OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

rest-framework's People

Contributors

acsonefho avatar anizr avatar bealdav avatar carlosroca13 avatar davejames avatar dreispt avatar etobella avatar fmdl avatar francomaxime avatar guewen avatar gurneyalex avatar hailangvn avatar ivantodorovich avatar kevinkhao avatar lmignon avatar marielejeune avatar mymage avatar nilshamerlinck avatar oca-git-bot avatar oca-travis avatar opa-cormaza avatar qgroulard avatar robinetdenisacsone avatar sbejaoui avatar sbidoul avatar sebastienbeau avatar silvioc2c avatar simahawk avatar stefanrijnhart avatar yankinmax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rest-framework's Issues

Support for authentication

Unless I overshot snippets of code in base_rest, it looks like the module does not allow to authenticate to private services in a RESTful way. I guess most of the APIs written with this framework will only be available to authenticated users, so it would be great to add support for authentication with a standard request shared among services.

By the way, how do you manage authentication with the current module? An RPC call to login and get the session?

why the duplicate methods?

I'm wondering why base_rest introduces duplicate endpoints.

  • "GET /{id}", "GET /{id}/get" behave the same
  • "GET /" and "GET /search" behave the same
  • "POST /{id}/delete" and "DELETE /{id}" behave the same
  • "POST /{id}/update" and "PUT /{id}" behave the same

There may be more.

I find this confusing, and I think it clutters the interface without a good reason, and it makes interacting with these services more complicated than needed. IMO base_rest should only provide

  • GET /{id}
  • GET /
  • DELETE /{id}
  • PUT /{id}

Am I missing a design point?

maximum recursion error with new api

Hy!

I've made a REST endpoint similar to the base_rest_demo. i search products and create from odoo product.product object an datamodel object.

this is my datamodel:
`
from marshmallow import fields

from odoo.addons.datamodel.core import Datamodel
#from odoo.addons.datamodel.fields import NestedModel

class SCDatamodelProduct(Datamodel):
_name = "sc.datamodel.product"

id = fields.Integer(required=True, allow_none=False)
name = fields.String(required=True, allow_none=False)
price = fields.Float(required=True, allow_none=False)
taxes_id = fields.Integer(allow_none=True)
tax_included = fields.Boolean(required=True, allow_none=False)
tax_percentage = fields.Float(allow_none=False, default=0.0)
barcode = fields.String(allow_none=True)
internal_reference = fields.String(allow_none=True)
qty_on_hand = fields.Float(allow_none=False, default=0.0)
category = fields.String(allow_none=True)
product_groups = fields.String(allow_none=True)
size_list = fields.String(allow_none=True)
style_list = fields.String(allow_none=True)
weight = fields.String(allow_none=True)`

first time when calling the enpoint, everything works correctly. but at all following calls i get an error:

Traceback (most recent call last):\n File \"/Users/js/DEV/odoo14/odoo/odoo/addons/base/models/ir_http.py\", line 237, in _dispatch\n result = request.dispatch()\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 806, in dispatch\n r = self._call_function(**self.params)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 359, in _call_function\n return checked_call(self.db, *args, **kwargs)\n File \"/Users/js/DEV/odoo14/odoo/odoo/service/model.py\", line 94, in wrapper\n return f(dbname, *args, **kwargs)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 347, in checked_call\n result = self.endpoint(*a, **kw)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 912, in __call__\n return self.method(*args, **kw)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 531, in response_wrap\n response = f(*args, **kw)\n File \"<string>\", line 6, in get_search\n File \"/Users/js/DEV/odoo14/scrimo/dieroester/scrimo_dieroester_api/controllers/controllers.py\", line 23, in _process_method\n response = super(ScrimoRoesterApiServices, self)._process_method(service_name, method_name, *args, params=params)\n File \"/Users/js/DEV/odoo14/oca/rest-framework/base_rest/controllers/main.py\", line 142, in _process_method\n result = service.dispatch(method_name, *args, params=params)\n File \"/Users/js/DEV/odoo14/oca/rest-framework/base_rest/components/service.py\", line 160, in dispatch\n res = method(*args, secure_params)\n File \"/Users/js/DEV/odoo14/oca/rest-framework/base_rest/restapi.py\", line 61, in response_wrap\n response = f(*args, **kw)\n File \"/Users/js/DEV/odoo14/scrimo/dieroester/scrimo_dieroester_api/services/sc_product_service.py\", line 46, in search\n res.append(self._get_product_datamodel(p))\n File \"/Users/js/DEV/odoo14/scrimo/dieroester/scrimo_dieroester_api/services/sc_product_service.py\", line 55, in _get_product_datamodel\n product_datamodel = ProductDatamodel(partial=True)\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow_objects/models.py\", line 81, in __call__\n obj = cls.load(kwargs, many=many, context=context, partial=partial, unknown=unknown,)\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow_objects/models.py\", line 229, in load\n loaded = schema.load(data, many=many)\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow/schema.py\", line 728, in load\n data, many=many, partial=partial, unknown=unknown, postprocess=True\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow/schema.py\", line 902, in _do_load\n partial=partial,\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow/schema.py\", line 1101, in _invoke_load_processors\n partial=partial,\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow/schema.py\", line 1227, in _invoke_processors\n data = processor(data, many=many, **kwargs)\n File \"/Users/js/DEV/odoo14/oca/rest-framework/datamodel/core.py\", line 97, in __make_object__\n return datamodel(__post_load__=True, __schema__=self, **data)\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow_objects/models.py\", line 75, in __call__\n obj.__init__(*args, **kwargs)\n File \"/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/functools.py\", line 363, in _method\n return self.func(*call_args, **call_keywords)\n File \"/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/functools.py\", line 363, in _method\n return self.func(*call_args, **call_keywords)\n File \"/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/functools.py\", line 363, in _method\n return self.func(*call_args, **call_keywords)\n [Previous line repeated 933 more times]\n File \"/Users/js/DEV/odoo14/oca/rest-framework/datamodel/core.py\", line 193, in __init__\n self._env = env\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow_objects/models.py\", line 142, in __setattr__\n self.__setattr_func__(key, value)\n File \"/Users/js/DEV/odoo14/odoo14venv/lib/python3.7/site-packages/marshmallow_objects/models.py\", line 151, in __getattribute__\n if object.__getattribute__(self, \"__dump_mode__\"):\nException\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n File \"/Users/js/DEV/odoo14/oca/rest-framework/base_rest/http.py\", line 182, in _handle_exception\n return super(HttpRestRequest, self)._handle_exception(exception)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 744, in _handle_exception\n return super(HttpRequest, self)._handle_exception(exception)\n File \"/Users/js/DEV/odoo14/odoo/odoo/http.py\", line 315, in _handle_exception\n raise exception.with_traceback(None) from new_cause\nRecursionError: maximum recursion depth exceeded while calling a Python object\n",

The REST Endpoint is defined as followed:
@restapi.method(
[(["/", "/search"], "GET")],
input_param=Datamodel("sc.datamodel.product.search"),
output_param=Datamodel("sc.datamodel.product", is_list=True),
auth="api_key"
)
def search(self, product_search_param):
"""
Search for products with name or id. No parameters returns all products.
"""
domain = [('sc_is_webshop_product', '=', True)]
if product_search_param.name:
domain.append(("name", "like", product_search_param.name))
if product_search_param.id:
domain.append(("id", "=", product_search_param.id))
res = []
for p in self.env["product.product"].search(domain):
# res.append(self._get_product_data(p))
res.append(self._get_product_datamodel(p))
return res

def _get_product_datamodel(self, product_odoo):
    ProductDatamodel = self.env.datamodels["sc.datamodel.product"]
    product_datamodel = ProductDatamodel(partial=True)
    if product_odoo:
        product_datamodel.id = product_odoo.id
        product_datamodel.name = product_odoo.sc_webshop_name if product_odoo.sc_webshop_name else product_odoo.name or ''
        product_datamodel.price = product_odoo.list_price
        product_datamodel.tax_included = product_odoo.sc_tax_included
        if product_odoo.taxes_id and len(product_odoo.taxes_id) > 0:
            product_datamodel.taxes_id = product_odoo.taxes_id.id
            product_datamodel.tax_percentage = product_odoo.taxes_id[0].amount
        product_datamodel.barcode = product_odoo.barcode or ''
        product_datamodel.internal_reference = product_odoo.default_code or ''
        product_datamodel.qty_on_hand = product_odoo.qty_available or 0.0
        #because of email from wolfgang winkler, that categories are set in webshop
        #product_datamodel.category = product_odoo.categ_id.display_name if product_odoo.categ_id else None
        product_datamodel.product_groups = product_odoo.sc_webshop_product_groups or ''
        product_datamodel.size_list = product_odoo.sc_webshop_size_list or ''
        product_datamodel.style_list = product_odoo.sc_webshop_style_list or ''
        product_datamodel.weight = product_odoo.weight_uom_name or ''

    return product_datamodel

product_datamodel = ProductDatamodel(partial=True) this line produces the error.

i searched nearly everything but can't find the solution. the product_datamodel has no nested other models...

any hint how to handle such an error?

Not fount url

After installing this module without any changes, getting "Not Found

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again."
my URL: http://localhost:8004/api-docs?debug=true
Please tell me how to add my custom URL and where?

Restapi method not working

I try to use the suggest of use decorators on service methods, like this:

`

@restapi.method([("/delete","DELETE")])
  def delete_user(self):
    return {"success": True}`

But is'nt working...when I look into swagger all my endpoints have POST method, no matter if I try with get, post ,put, delete, always creates them with POST method.

[14.0] base_rest: Create endpoint function won't work on website form submit POST

Issue summary:

Can't get create() endpoint working on website form submit POST

Version: 14.0 (not tested on previous versions)

Current behaviour:
I have a create function like this:

from odoo.addons.component.core import Component

class PartnerPublicService(Component):
    _inherit = "base.rest.service"
    _name = "partner.service"
    _usage = "partners"
    _collection = "custom.public.services"  # collection class added on another file
    _description = """
        Random description
    """
    
    def create(self, **params):
        partner = self.env["res.partner"].create({"name": params["name"], "vat": params["vat"]})
        return {"message": "Partner successfully created with ID %s" % partner.id}

    def _validator_create(self):
        return {"name": {"type": "string", "required": True}, "vat": {"type": "string", "required": False}}

I tried to send a POST request to it from outside Odoo and it works great but when try on a website form params came empty.

Expected behaviour:

create() endpoint works well on both outside POST and website form submit POST requests.

Additional info:

Website form submit POST works when uninstalling base_rest and its dependency (component) and using a common Odoo controller endpoint.

datamodels default value have no effect

Hi, I try to write default value in datamodels like this:
image
And i used in services like this:
image
The values(spu_search_param) i printed like normal. But when i print limit/offset/order, it's None.
image
I try to give a breakpoint for it.
image
This is what confused me. Do you have any suggestions for me? Thank you so much!

Pagination

Theres any way to paginate search result?

[11.0] [?10.0?] base_rest: RFC: Annotation for the methods exposed by the services

Current context

The current implementation is based on implicit rules:

  • public methods defined into a component inheriting of 'base.rest.service' are implicitly available into the REST API.
class PartnerService(Component):
    _inherit = 'base.rest.service'
    _name = 'partner.service'
    _usage = 'partner'
    _collection = 'base.rest.demo.private.services'
    _description = """
Partner Services
    """

    def get(self, _id):
        """
        Get partner's informations
        """
        return self._to_json(self._get(_id))
  • schemas used to validate the data transferred by these services (in/out) are provided by protected methods (validator...). The link between services methods and methods providing validation schemas is based on naming conventions.
    def get(self, _id):
        """
        Get partner's informations
        """
        return self._to_json(self._get(_id))

    # Validator
    def _validator_get(self):
        return {}

    def _validator_return_get(self):
        res = self._validator_create()
        res.update({
            'id': {'type': 'integer', 'required': True, 'empty': False},
        })
        return res
  • It's possible to return a result in a different format than JSON. Nevertheless there is no way to document/enforce this type of result
from odoo.http import Response
...
    def get_image(self, _id):
        ....
        return Response(
            output.getvalue(),
            headers={
                'Content-Type': mimetype,
                'Content-Disposition': 'inline; filename="%s"' % filename,
            })
  • By default, the base_rest addon register generic http routes. To avoid the call of methods that could do updates by an HTTP GET request (which is against the HTTP) we enforce only 2 methods available by default via HTTP GET. If you need to add another method, you must declare it into the controller.

  • It's not possible to specify that a method takes as input our returns as output a list of items...

Proposal

I I think that the development of a specific decorator to annotate the exposed methods would allow us to pass from an implicit mode to an explicit mode for the various stated elements. This would also allow us to have a stronger semantic in the definition of methods.

    @restapi.method(
        input = None,
        output = File(),
        auth='User',
        method='GET'
    )
    def get_image(self, _id):
        ....
        return {
            'mimetype': ...
            'filename': ....
            'data': output.getvalue(),
        }

    @restapi.method(
        input = JSon(schema_method=`_validator_get`, is_list=False, # default ),
        output = JSon(schema_method=`_validator_return_get`,  is_list=True),
        auth='User'
    )
    def get(self, _id):
        """
        Get partner's informations
        """
        return self._get(_id)

    # Validator
    def _validator_get(self):
        return {}

    def _validator_return_get(self):
        res = self._validator_create()
        res.update({
            'id': {'type': 'integer', 'required': True, 'empty': False},
        })
        return res

Serialize / deserialize could be done by the decorator....

Return 409 Conflict on concurrent update errors

Currently, when a concurrent update error happens (the famous could not serialize access due to concurrent update), the endpoints return an HTTP error code 500 Internal Server Error.

Odoo RPC handles retries on such errors such that a client gets this error only after all the tries have failed, which could lead to a long delay before getting a response.

@TDu proposed to return 409 Conflict on such errors. I really like it, because then it leaves control to the client application to decide if and when they retry.

Should I propose a PR?

From https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10

409 Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

Can't generate swagger document

Hi! Thanks for this great piece of code!

I was building on top of your base_rest_demo addon. But I can't find a way to generate Swagger API definitions automatically. I tried to c&p and edit .json file in data folder but that didn't work. Here is what I get in Swagger:
screenshot_2019-01-25 odoo

If you can put somewhere in the docs how this can be generated that will be awesome!

Prevent errors in case of public methods without validator

If a public method is declared into a rest.service without validator, an error will be raised when accessing the swagger definition and prevent it's normal display.

We should at least avoid this kind of crash and display a proper error.
Thinking further, I think we should put in place a mechanism that detects this type of inconsistency (public method without a validator) and prevents Odoo from starting in such cases.

[ERROR] Cerberus==1.3.3 AttributeError: 'BareValidator' object has no attribute 'rules'

Have no time to go deeper, but seems that since cerberus released version 1.3.3 (two days ago) this error is appearing. Coming back to 1.3.2 solved the issue on my side.

Traceback (most recent call last):
  File "/opt/odoo/auto/addons/base_rest/http.py", line 182, in _handle_exception
    return super(HttpRestRequest, self)._handle_exception(exception)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 750, in _handle_exception
    return super(HttpRequest, self)._handle_exception(exception)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 310, in _handle_exception
    raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])
  File "/opt/odoo/custom/src/odoo/odoo/tools/pycompat.py", line 14, in reraise
    raise value
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_http.py", line 234, in _dispatch
    result = request.dispatch()
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 809, in dispatch
    r = self._call_function(**self.params)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 350, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/service/model.py", line 94, in wrapper
    return f(dbname, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 339, in checked_call
    result = self.endpoint(*a, **kw)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 915, in __call__
    return self.method(*args, **kw)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 515, in response_wrap
    response = f(*args, **kw)
  File "<string>", line 6, in get_search
  File "/opt/odoo/auto/addons/base_rest/controllers/main.py", line 144, in _process_method
    result = service.dispatch(method_name, *args, params=params)
  File "/opt/odoo/auto/addons/shopinvader/services/service.py", line 238, in dispatch
    res = super().dispatch(method_name, *args, params=params)
  File "/opt/odoo/auto/addons/base_rest/components/service.py", line 154, in dispatch
    secure_params = self._prepare_input_params(method, params)
  File "/opt/odoo/auto/addons/base_rest/components/service.py", line 106, in _prepare_input_params
    return input_param.from_params(self, params)
  File "/opt/odoo/auto/addons/base_rest/restapi.py", line 120, in from_params
    validator = self.get_cerberus_validator(service, "input")
  File "/opt/odoo/auto/addons/base_rest/restapi.py", line 182, in get_cerberus_validator
    return Validator(schema, purge_unknown=True)
  File "/usr/local/lib/python3.6/site-packages/cerberus/validator.py", line 193, in __init__
    self.schema = kwargs.get('schema', None)
  File "/usr/local/lib/python3.6/site-packages/cerberus/validator.py", line 604, in schema
    self._schema = DefinitionSchema(self, schema)
  File "/usr/local/lib/python3.6/site-packages/cerberus/schema.py", line 72, in __init__
    self.validation_schema = SchemaValidationSchema(validator)
  File "/usr/local/lib/python3.6/site-packages/cerberus/schema.py", line 307, in __init__
    'schema': validator.rules,
AttributeError: 'BareValidator' object has no attribute 'rules'

@lmignon

Can't install it on Odoo 14

Hi dear,

Could you give me a hand for this?
I tried to install the branch 14 on Odoo 14. While installing it on the Apps, it popped up an error.

image

Form submit validation and error handling: how to do it properly?

Context
Imagine you a form that sends data to an endpoint which has its own validator.
The validator states that "country" field is required (or any other constrain).

Problem
If form data have an error you get this:

  File "/odoo/external-src/rest-framework/base_rest/components/service.py", line 142, in _secure_input
    raise UserError(_('BadRequest %s') % v.errors)
odoo.exceptions.UserError: ("BadRequest {'country': ['required field']}", '')

which, as far as I see, is not really nice to be handle as a response. The only way to handle it would be to parse the text of the response and look for a specific match with form fields.
This way seems very hard to handle form errors nicely.

Solutions?
@sbidoul @lmignon I wonder how you guys are handling this issue.
I think we should have a way to return a plain dict w/ detailed information on the errors w/ specific error codes. For instance:

    {
        'errors': {
            'country': 'missing',
            'phone': 'bad_lenght',
        },
        'errors_message': {
            'country': 'Country is required',
            'phone': 'Phone number must contain XX chars',
        }
    }

I'm doing something similar in cms_form and it helps very well on error handling.
BTW I'm thinking about an integration w/ cms_form to be able to define forms and expose their schema via rest api.

Possibly related to #2.

Thanks for your insights :)

Install Question

When I click the install button I get an error on the service saying "INFO demo odoo.addons.base.module.module: ALLOW access to module.button_install on ['base_rest'] to user...."
I have attempted to install in v11.0 - seems to need dependency "component" - where do I obtain this module.

schema2parameters() got an unexpected keyword argument 'location' using base_rest_demo

Hello,

I am trying to run the base_rest_demo module inside a Modified Odoo Docker Image (which includes all the installs needed by the rest framework modules), and after installing the rest modules on a database (no apparent issues while doing so) I open the Swagger page (http://localhost:8069/api-docs), but get a Fetch Error.

The logs show that schema2parameters() got an unexpected keyword argument 'location':

Traceback (most recent call last): ... File "/mnt/extra-addons/oca/rest-framework/base_rest/controllers/api_docs.py", line 38, in api return self.make_json_response(service.to_openapi()) File "/mnt/extra-addons/oca/rest-framework/base_rest/components/service.py", line 183, in to_openapi return BaseRestServiceAPISpec(self).to_dict() File "/mnt/extra-addons/oca/rest-framework/base_rest/apispec/base_rest_service_apispec.py", line 36, in __init__ self._generate_paths() File "/mnt/extra-addons/oca/rest-framework/base_rest/apispec/base_rest_service_apispec.py", line 74, in _generate_paths self._add_method_path(method) File "/mnt/extra-addons/oca/rest-framework/base_rest/apispec/base_rest_service_apispec.py", line 66, in _add_method_path routing=routing, File "/usr/local/lib/python3.5/dist-packages/apispec/core.py", line 280, in path plugin.operation_helper(path=path, operations=operations, **kwargs) File "/mnt/extra-addons/oca/rest-framework/base_rest/apispec/rest_method_param_plugin.py", line 43, in operation_helper input_param.to_openapi_query_parameters(self._service) File "/mnt/extra-addons/oca/rest-framework/base_rest_datamodel/restapi.py", line 48, in to_openapi_query_parameters return converter.schema2parameters(schema, location="query") TypeError: schema2parameters() got an unexpected keyword argument 'location' - - -

Is there a specific version that apispec (or any any of the other libraries) needs to be on? I have tried several versions (including 3.00 and 4.3.0), but still get the same error.

Any help is much appreciated.

auto-gerenated requirements.txt is broken on branch 14.0

indeed it starts as:

# generated from manifests external_dependencies
apispec
apispec>=4.0.0
cerberus

but pip doesn't like such apispec repetition and blows out with:

ERROR: Double requirement given: apispec>=4.0.0 (from -r /odoo/requirements.txt (line 5)) (already in apispec (from -r /odoo/requirements.txt (line 4)), name='apispec')
ERROR: Service 'odoo' failed to build : The command '/bin/sh -c pip install -r /odoo/requirements.txt' returned a non-zero code: 1

strangely the requirements.txt is fine on the branch 13.0, was it auto-generated like on branch14.0?

How to run graphiql

Have installed modules locally and have Rest methods (search partner etc) all working OK in V12.0 but I have no idea how to get graphql working.

I am new to Odoo development.

Should the url be:
http://localhost:8069/graphql?
or maybe
http://localhost:8069/graphql/demo

with say a post query like:
{
Country(code: "BR", name: "Brazil" )
{
id
}
}

A simple example for graphiql or Postman would be really appreciated.

Odoo crashes after dropping a database

It might happen that a database is deleted, expecially while developing new features.
If module base_rest is installed in at least one DB (even the deleted one), the get_request override in base_rest/http.py will eventually try to initialize the deleted database without checking if it still exists. So when the odoo.registry(db) line is reached, Odoo tries to initialize a Registry for a non existing database, and it crashes.

PRs:

Canno't open swagger document (method not allowed)

Hello ,
I'am trying to create rest api using your module in odoo .
when i put this url : (http://localhost:8079/rest_api/public/product ) doesn't work and show me method is not allowed (capture 1)
capture1

my code source is :

from odoo.addons.base_rest.controllers import main
class MyRestController(main.RestController):
_collection_name = "base.rest.public.services"
_root_path = '/rest_api/public'
_default_auth = "public"


class ProductService(Component):
_inherit = 'base.rest.service'
_name = 'product.service'
_collection = 'paradon.rest.public.services'
_usage = 'product'
_description= """ Product Services """

def get(self ,_id):
    """"
    Get product's informations
    """
    return self._to_json(self._get(_id))

def _validator_get(self):

    return{}


def _validator_return_get(self):
    res = {
        "id": {"type":"integer","required":True,"empty": False},
        "name": {"type": "string", "required": True, "empty": False},
        "price": {"type": "float", "required": True, "empty": False}

    }
    return res

Which i had understand that i'am not allowed to use request GET so how can I use request POST . Is it possible to give me an example of post request and how to solve problem of method not allowed. Finally when i open this link (http://localhost:8079/rest_api/public ) it should open the documentation swagger for my rest api is it truth ?

Thanks for helps.
Cordially Haitham

Unable to fetch record from postman

Hi

while use graphiql in browser its works.But while using in postman unable to fetch.
how to pass authorization token with requests if there is way where to fetch authorization token from this module

{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": 100,
"message": "Odoo Session Expired",
"data": {
"name": "odoo.http.SessionExpiredException",
"debug": "Traceback (most recent call last):\n File "/home/ssd-01/Documents/Projects/school_bus/odoo/http.py", line 619, in _handle_exception\n return super(JsonRequest, self)._handle_exception(exception)\n File "/home/ssd-01/Documents/Projects/school_bus/odoo/http.py", line 309, in _handle_exception\n raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])\n File "/home/ssd-01/Documents/Projects/school_bus/odoo/tools/pycompat.py", line 14, in reraise\n raise value\n File "/home/ssd-01/Documents/Projects/school_bus/addons/http_routing/models/ir_http.py", line 443, in _dispatch\n cls._authenticate(func.routing['auth'])\n File "/home/ssd-01/Documents/Projects/school_bus/odoo/addons/base/models/ir_http.py", line 124, in _authenticate\n getattr(cls, "auth_method%s" % auth_method)()\n File "/home/ssd-01/Documents/Projects/school_bus/odoo/addons/base/models/ir_http.py", line 97, in _auth_method_user\n raise http.SessionExpiredException("Session expired")\nodoo.http.SessionExpiredException: Session expired\n",
"message": "Session expired",
"arguments": [
"Session expired"
],
"exception_type": "internal_error"
}
}
}

how to define a parameter in header

For example, I have one Datamodel defined as below:

class ReqGetToken(Datamodel):
    _name = "req.get.token"

    authorization = fields.String(allow_none=False,
                                  description='the username:password pair which should be put in header, for example: curl -u {username}:{password} -X GET "https://.../token"')

However, in real world, the authorization information username:password need to be sent in the HTTP header, say

 curl -u {username}:{password} -X GET "https://.../token"

How should I tell rest framework that username password is a header paramter?

Thanks,Xu

[11.0] base_rest: RFC: Improve request/response Schema description

Current context

In base_Rest, information exchanged by services is validated according to a Cerberus schema defined as dict.

    address_request_schema = {
        'name': {'type': 'string', 'required': True, 'empty': False},
        'street': {'type': 'string'},
    }

Defining such schemas could become boring and lead to a hard to read/maintain result when the schema is complex. This kind of definition is also hard to extend, reuse etc...

Proposal

After much reflection and discussion with @sbidoul and some research into the web, I think that I'll base the schema definition of our data transfer objects on the pydantic library. This library comes with a clean and standard way to define the structure of data transfer objects. It also provides methods to serialize/deserialize defined data transfer object to/from json and to generate the jsonschema from the objet definition.

Odoo already provides a clean api to define models. This API provides a pythonic way to define fields and relation between models. It also provides a clean way to override and extend models across modules. The Idea is to define a new base class based on BaseModel to define the messages exchanged by the services.

class RestModel(models.BaseModel):
    _name='rest.model'
    
    def from_json(self, json_data):
        return self.new(....)

    def to_json(self):
        return {}      
    
    def validate(self):
        return True

    def to_json_schema(self):
        """ Return the definition of the model as json schema
        see http://json-schema.org/
        """
        retrun {}


class AddressRequest(RestModel):
    _name = 'address.request'

    name = fields.Char(required=True)
    street = fields.Char()

    ....

Services will be annoted to specify the request/response models instead of the current implicit properties. This annotation will also be used to say that the request / response is a list or not (default) see #6

Expected benefits:

  • More modular
  • Easier to extend / reuse / redefine
  • Easier to manipulate into the services implementation. my_address['name'] vs my_address.name
  • Easier to document (thanks to string and helpattributes of the fields we'll be able to provide a description into the json schema)
  • ....

request with xml params

I find this addons. It's very cool. But my new partner require my API with XML, not JSON params. Is it possible? I'd be very thankful for any advice to resolve that with this addon or other.

How to write GET all method?

Hi,

How should I define a GET all partner method in base_rest_demo?

For example:

/partners --> GET all partners
/partners/{id} --> GET a specific partner

Thanks

How to validate 'array' type?

Hi,

I try to create a validator for PUT.
but I cannot get what I want
my code: I define sub_checklist_record_ids is 'array' type and the items have dict type
image

But what I get is 'string' items
image

So how could I fix this?
Thank you

How to test endpoints with data

Dear contributors,

I am currently struggling with unit tests for my endpoints.

  1. How can I do a assertRaises(CustomExceptionType) ? When I try to do so, it says that the exception was not raised, although it did (exception printed correctly in the logs). I assume that the test and the endpoint being tested, where the request it posted against, are running in two different threads, so any idea on this one?
  2. When I set up test data in the unit test and try to test against those by posting references to those created data in a request to the endpoint, those data are not found. Again, different threads and different ORM environments. So we have two different database transactions, what results in not accessible test data...

Does anyone have a solution for my issue?

Thanks in advance!

control the accessibility of swagger spec

For now, all the swagger specs are available by accessing /api-docs

I'm wondering is there a way to control the accessibility of the specs. for example,

  • Some specs are public available
  • Some specs are available for log in user
  • Some specs are available only for user in specific odoo group

Any idea?

Thanks,Xu

How to not a sorting method in service

How to not a sorting method in service, i confused, because there are not "sort" name associated with the method. thanks you..

qqq

i have writen method bbbb() after that method aaaa(), but show as above
i want to show method bbbb() after that method aaaa()

there somebody can help me ?

Documentation not clear for odoo.addons.component.core dependency source

I'm using Odoo v 10

This module is breaking my deployment because it can find odoo.addons.component.core

I have the https://github.com/OCA/connector module (according to oca_requirements.txt) and from that project, I installed cachetools>=2.0.1,<3.0.0

What provides odoo.addons.component.core.AbstractComponent ?

Trace from fresh install:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/werkzeug/serving.py", line 177, in run_wsgi
    execute(self.server.app)
  File "/usr/lib/python2.7/dist-packages/werkzeug/serving.py", line 165, in execute
    application_iter = app(environ, start_response)
  File "/usr/lib/python2.7/dist-packages/odoo/service/wsgi_server.py", line 186, in application
    return application_unproxied(environ, start_response)
  File "/usr/lib/python2.7/dist-packages/odoo/service/wsgi_server.py", line 172, in application_unproxied
    result = handler(environ, start_response)
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 1325, in __call__
    self.load_addons()
  File "/usr/lib/python2.7/dist-packages/odoo/http.py", line 1346, in load_addons
    m = __import__('odoo.addons.' + module)
  File "/usr/lib/python2.7/dist-packages/odoo/modules/module.py", line 81, in load_module
    execfile(modfile, new_mod.__dict__)
  File "/mnt/extra-addons/base_rest/__init__.py", line 2, in <module>
    from . import components
  File "/mnt/extra-addons/base_rest/components/__init__.py", line 1, in <module>
    from . import service
  File "/mnt/extra-addons/base_rest/components/service.py", line 11, in <module>
    from odoo.addons.component.core import AbstractComponent
  File "/usr/lib/python2.7/dist-packages/odoo/modules/module.py", line 60, in load_module
    f, path, (_suffix, _mode, type_) = imp.find_module(addon_name, ad_paths)
ImportError: No module named component

strategies to avoid conflicting datamodel names ?

Hi,

We have just a few modules using this and already we came across name conflicts.

Datamodel names like "sale.order" and "sale.order.line" make sense to me when I'm writing a module. It makes so much sense that I forget that other modules are very likely to do the same. It seems likely that this will occur if there are many public modules using base_rest_datamodel.

Have you found a good strategy or convention to avoid this ? When I had the problem I just prefixed the datamodel names with the module name.

Not able to run the demo

Hi,

Thank you for your contribution to this module. I have downloaded the base_rest with the base_rest_demo and add them to folders in odoo, but when calling from postman i get 500 internal error and when checking logs there is this ๐Ÿ‘
odoo.addons.component.exception.NoComponentError: No component found for collection 'base.rest.demo.public.services', usage 'ping', model_name 'rest.service.registration'.

from postman i used to call this url : http://localhost:8069/base_rest_demo_api/public/ping

Internal server error when user has an old db in session

When a user accesses Odoo (any URL) while previously logged in on a database that has been removed, the server returns an 500 with the following stack trace:

2021-02-14 20:02:47,914 1794 ERROR **** werkzeug: Error on request:
Traceback (most recent call last):
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/werkzeug/serving.py", line 270, in run_wsgi
    execute(self.server.app)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/werkzeug/serving.py", line 258, in execute
    application_iter = app(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/service/wsgi_server.py", line 140, in application
    return ProxyFix(application_unproxied)(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/werkzeug/contrib/fixers.py", line 152, in __call__
    return self.app(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/service/wsgi_server.py", line 117, in application_unproxied
    result = odoo.http.root(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/http.py", line 1287, in __call__
    return self.dispatch(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/http.py", line 1257, in __call__
    return self.app(environ, start_wrapped)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/werkzeug/wsgi.py", line 766, in __call__
    return self.app(environ, start_response)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/http.py", line 1424, in dispatch
    request = self.get_request(httprequest)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/addons/base_rest/http.py", line 219, in get_request
    odoo.registry(db)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/__init__.py", line 104, in registry
    return modules.registry.Registry(database_name)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/modules/registry.py", line 62, in __new__
    return cls.new(db_name)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/modules/registry.py", line 74, in new
    registry.init(db_name)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/modules/registry.py", line 143, in init
    with closing(self.cursor()) as cr:
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/modules/registry.py", line 542, in cursor
    return self._db.cursor()
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/sql_db.py", line 664, in cursor
    return Cursor(self.__pool, self.dbname, self.dsn, serialized=serialized)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/sql_db.py", line 196, in __init__
    self._cnx = pool.borrow(dsn)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/sql_db.py", line 547, in _locked
    return fun(self, *args, **kwargs)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/odoo/sql_db.py", line 615, in borrow
    **connection_info)
  File "/home/odoo-dev/instance/venv-7.1.85/lib/python3.6/site-packages/psycopg2/__init__.py", line 130, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: FATAL:  database "****" does not exist - - -

Since base_rest is the only unusual thing in this stack trace... The issue might follow from #83.

CORS not allowing to make requests

Hello guys, first of all thank you very much for this awesome library.
I'm trying to make request from a react app but getting this error

Screen Shot 2021-04-04 at 10 47 15 PM

Not sure why since I have set cors as follows

cors="http://localhost:3000"

And I get the correct headers in swagger

Screen Shot 2021-04-04 at 10 53 20 PM

but when I try to make the request in the client the response header for cors is not set

Screen Shot 2021-04-04 at 10 47 27 PM

What might be the cause?
BTW. I'm developing my module using odoo 14.0 docker image.

api-docs broken as headers are not documented with new implementation

Before the refactoring #71 every service was relying on _get_openapi_default_parameters to document endpoints (eg: required headers).
When using restapi.method this is ignored and docs are not usable at all.

@lmignon shouldn't RestApiMethodRoutePlugin take care of this as well?

Example:

@restapi.method(
        [(["/", "/search"], "GET")], auth="api_key",
    )
    def search(self):
        return self._paginate_search()

IMO we should support old behavior and allow to pass per method parameters via decorator.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.