diff --git a/.isort.cfg b/.isort.cfg index 06551a7..1517121 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -9,4 +9,4 @@ line_length=79 known_odoo=odoo known_odoo_addons=odoo.addons sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER -known_third_party=addons,cStringIO,openerp,requests,setuptools,werkzeug,xlsxwriter +known_third_party=addons,cStringIO,lxml,openerp,requests,setuptools,werkzeug,xlsxwriter diff --git a/easy_my_coop_api/README.rst b/easy_my_coop_api/README.rst new file mode 100644 index 0000000..f6ee9ae --- /dev/null +++ b/easy_my_coop_api/README.rst @@ -0,0 +1,85 @@ +================ +Easy My Coop API +================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-coopiteasy%2Fvertical--cooperative-lightgray.png?logo=github + :target: https://github.com/coopiteasy/vertical-cooperative/tree/12.0/easy_my_coop_api + :alt: coopiteasy/vertical-cooperative + +|badge1| |badge2| |badge3| + +Open Easy My Coop to the world: RESTful API. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To give access to the API to a structure, go to + +- Settings > Technical (debug mode) > Auth API Key +- click create and select a user, save. +- communicate the API-KEY to the structure. + +It is recommended to create a technical user for the structure belonging to the group "Easy My Coop User". +For example, for the structure Coop IT Easy, create partner with + +- name = coopiteasy-api-user +- Application Accesses = Cooperative Management / User +- Platform Structure = Coop IT Easy + +Known issues / Roadmap +====================== + +The API should generate and use an external id for records instead +of odoo's generated id. It would make importing and export data as +well as migrating across versions easier. + +One way would be to use uuid but the default BaseRESTController +routes would need to be rewritten: they only take integer as ids. +Another way is to define a sequence per model. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Coop IT Easy SCRLfs + +Contributors +~~~~~~~~~~~~ + +* Coop IT Easy SCRLfs +* Robin Keunen + +Maintainers +~~~~~~~~~~~ + +This module is part of the `coopiteasy/vertical-cooperative `_ project on GitHub. + +You are welcome to contribute. diff --git a/easy_my_coop_api/__init__.py b/easy_my_coop_api/__init__.py new file mode 100644 index 0000000..c312a84 --- /dev/null +++ b/easy_my_coop_api/__init__.py @@ -0,0 +1,3 @@ +from . import controllers +from . import models +from . import services diff --git a/easy_my_coop_api/__manifest__.py b/easy_my_coop_api/__manifest__.py new file mode 100644 index 0000000..f42c066 --- /dev/null +++ b/easy_my_coop_api/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Easy My Coop API", + "version": "12.0.0.0.1", + "depends": [ + "base_rest", + "easy_my_coop", + "auth_api_key", # todo conf running_env = dev + ], + "author": "Coop IT Easy SCRLfs", + "category": "Cooperative management", + "website": "www.coopiteasy.be", + "license": "AGPL-3", + "summary": """ + Open Easy My Coop to the world: RESTful API. + """, + "data": [], + "demo": [], + "installable": True, + "application": False, +} diff --git a/easy_my_coop_api/controllers/__init__.py b/easy_my_coop_api/controllers/__init__.py new file mode 100644 index 0000000..e046e49 --- /dev/null +++ b/easy_my_coop_api/controllers/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/easy_my_coop_api/controllers/controllers.py b/easy_my_coop_api/controllers/controllers.py new file mode 100644 index 0000000..8badeb2 --- /dev/null +++ b/easy_my_coop_api/controllers/controllers.py @@ -0,0 +1,25 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo.http import route + +from odoo.addons.base_rest.controllers import main + + +class UserController(main.RestController): + _root_path = "/api/" + _collection_name = "emc.services" + _default_auth = "api_key" + + @route( + _root_path + "/test", + methods=["GET"], + auth="public", + csrf=False, + ) + def test(self, _service_name): + return self._process_method( + _service_name, "test", _id=None, params=None + ) diff --git a/easy_my_coop_api/models/__init__.py b/easy_my_coop_api/models/__init__.py new file mode 100644 index 0000000..6dfe3f7 --- /dev/null +++ b/easy_my_coop_api/models/__init__.py @@ -0,0 +1 @@ +from . import auth_api_key diff --git a/easy_my_coop_api/models/auth_api_key.py b/easy_my_coop_api/models/auth_api_key.py new file mode 100644 index 0000000..75446ad --- /dev/null +++ b/easy_my_coop_api/models/auth_api_key.py @@ -0,0 +1,33 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import uuid + +from odoo import api, fields, models + + +class AuthApiKey(models.Model): + _inherit = "auth.api.key" + + def _default_key(self): + return uuid.uuid4() + + # overloaded fields + # required is set to false to allow for a computed field, + # it will always be set. + name = fields.Char(required=False, compute="_compute_name", store=True) + key = fields.Char(default=_default_key) + + @api.multi + @api.depends("user_id") + def _compute_name(self): + for key in self: + if key.user_id: + now = fields.Datetime.now() + + key.name = "{login}-{now}".format( + now=fields.Datetime.to_string(now), login=key.user_id.login + ) + else: + key.name = "no-user" diff --git a/easy_my_coop_api/readme/CONTRIBUTORS.rst b/easy_my_coop_api/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..f8672e6 --- /dev/null +++ b/easy_my_coop_api/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Coop IT Easy SCRLfs +* Robin Keunen diff --git a/easy_my_coop_api/readme/DESCRIPTION.rst b/easy_my_coop_api/readme/DESCRIPTION.rst new file mode 100644 index 0000000..8a4c837 --- /dev/null +++ b/easy_my_coop_api/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Open Easy My Coop to the world: RESTful API. diff --git a/easy_my_coop_api/readme/ROADMAP.rst b/easy_my_coop_api/readme/ROADMAP.rst new file mode 100644 index 0000000..5698982 --- /dev/null +++ b/easy_my_coop_api/readme/ROADMAP.rst @@ -0,0 +1,7 @@ +The API should generate and use an external id for records instead +of odoo's generated id. It would make importing and export data as +well as migrating across versions easier. + +One way would be to use uuid but the default BaseRESTController +routes would need to be rewritten: they only take integer as ids. +Another way is to define a sequence per model. diff --git a/easy_my_coop_api/readme/USAGE.rst b/easy_my_coop_api/readme/USAGE.rst new file mode 100644 index 0000000..ee1feef --- /dev/null +++ b/easy_my_coop_api/readme/USAGE.rst @@ -0,0 +1,12 @@ +To give access to the API to a structure, go to + +- Settings > Technical (debug mode) > Auth API Key +- click create and select a user, save. +- communicate the API-KEY to the structure. + +It is recommended to create a technical user for the structure belonging to the group "Easy My Coop User". +For example, for the structure Coop IT Easy, create partner with + +- name = coopiteasy-api-user +- Application Accesses = Cooperative Management / User +- Platform Structure = Coop IT Easy diff --git a/easy_my_coop_api/services/__init__.py b/easy_my_coop_api/services/__init__.py new file mode 100644 index 0000000..d657c40 --- /dev/null +++ b/easy_my_coop_api/services/__init__.py @@ -0,0 +1,2 @@ +from . import ping_service +from . import subscription_request_service diff --git a/easy_my_coop_api/services/ping_service.py b/easy_my_coop_api/services/ping_service.py new file mode 100644 index 0000000..49b37d1 --- /dev/null +++ b/easy_my_coop_api/services/ping_service.py @@ -0,0 +1,37 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited + + +from odoo import _ + +from odoo.addons.component.core import Component + + +class PingService(Component): + _inherit = "base.rest.service" + _name = "emc.services" + _usage = "ping" # service_name + _collection = "emc.services" + _description = """ + Ping services (test the api) + """ + + def search(self): + return {"message": _("Called search on ping API")} + + def test(self): + return {"message": _("Called ping on ping API")} + + def _validator_test(self): + return {} + + def _validator_return_test(self): + return {"message": {"type": "string"}} + + def _validator_search(self): + return {} + + def _validator_return_search(self): + return {"message": {"type": "string"}} diff --git a/easy_my_coop_api/services/schemas.py b/easy_my_coop_api/services/schemas.py new file mode 100644 index 0000000..f20dab7 --- /dev/null +++ b/easy_my_coop_api/services/schemas.py @@ -0,0 +1,95 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import _ +from odoo.fields import Date + + +def date_validator(field, value, error): + try: + Date.from_string(value) + except ValueError as e: + return error( + field, _("{} does not match format '%Y-%m-%d'".format(value)) + ) + + +S_SUBSCRIPTION_REQUEST_GET = {"_id": {"type": "integer"}} + +S_SUBSCRIPTION_REQUEST_RETURN_GET = { + "id": {"type": "integer", "required": True}, + "email": {"type": "string", "required": True, "empty": False}, + "name": {"type": "string", "required": True, "empty": False}, + "date": {"type": "string", "required": True, "empty": False}, + "state": {"type": "string", "required": True, "empty": False}, + "ordered_parts": {"type": "integer", "required": True}, + "share_product": { + "type": "dict", + "schema": { + "id": {"type": "integer", "required": True}, + "name": {"type": "string", "required": True, "empty": False}, + }, + }, + "address": { + "type": "dict", + "schema": { + "street": {"type": "string", "required": True, "empty": False}, + "zip_code": {"type": "string", "required": True, "empty": False}, + "city": {"type": "string", "required": True, "empty": False}, + "country": {"type": "string", "required": True, "empty": False}, + }, + }, + "lang": {"type": "string", "required": True, "empty": False}, +} + +S_SUBSCRIPTION_REQUEST_SEARCH = { + "date_from": {"type": "string", "check_with": date_validator}, + "date_to": {"type": "string", "check_with": date_validator}, +} + +S_SUBSCRIPTION_REQUEST_RETURN_SEARCH = { + "count": {"type": "integer", "required": True}, + "rows": { + "type": "list", + "schema": { + "type": "dict", + "schema": S_SUBSCRIPTION_REQUEST_RETURN_GET, + }, + }, +} + +S_SUBSCRIPTION_REQUEST_CREATE = { + "name": {"type": "string", "required": True, "empty": False}, + "email": {"type": "string", "required": True, "empty": False}, + "ordered_parts": {"type": "integer", "required": True}, + "share_product": {"type": "integer", "required": True}, + "address": { + "type": "dict", + "schema": { + "street": {"type": "string", "required": True, "empty": False}, + "zip_code": {"type": "string", "required": True, "empty": False}, + "city": {"type": "string", "required": True, "empty": False}, + "country": {"type": "string", "required": True, "empty": False}, + }, + }, + "lang": {"type": "string", "required": True, "empty": False}, +} + +S_SUBSCRIPTION_REQUEST_UPDATE = { + "name": {"type": "string"}, + "email": {"type": "string"}, + "ordered_parts": {"type": "integer"}, + "state": {"type": "string"}, + "address": { + "type": "dict", + "schema": { + "street": {"type": "string"}, + "zip_code": {"type": "string"}, + "city": {"type": "string"}, + "country": {"type": "string"}, + }, + }, + "lang": {"type": "string"}, + "share_product": {"type": "integer"}, +} diff --git a/easy_my_coop_api/services/subscription_request_service.py b/easy_my_coop_api/services/subscription_request_service.py new file mode 100644 index 0000000..a64de5d --- /dev/null +++ b/easy_my_coop_api/services/subscription_request_service.py @@ -0,0 +1,167 @@ +# Copyright 2019 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +# pylint: disable=consider-merging-classes-inherited + +import logging + +from werkzeug.exceptions import BadRequest, NotFound + +from odoo import _ +from odoo.fields import Date + +from odoo.addons.base_rest.http import wrapJsonException +from odoo.addons.component.core import Component + +from . import schemas + +_logger = logging.getLogger(__name__) + + +class SubscriptionRequestService(Component): + _inherit = "base.rest.service" + _name = "subscription.request.services" + _usage = "subscription-request" + _collection = "emc.services" + _description = """ + Subscription requests + """ + + def get(self, _id): + sr = self.env["subscription.request"].browse(_id) + if sr: + return self._to_dict(sr) + else: + raise wrapJsonException( + NotFound(_("No subscription request for id %s") % _id) + ) + + def search(self, date_from=None, date_to=None): + _logger.info("search from {} to {}".format(date_from, date_to)) + + domain = [] + if date_from: + date_from = Date.from_string(date_from) + domain.append(("date", ">=", date_from)) + if date_to: + date_to = Date.from_string(date_to) + domain.append(("date", "<=", date_to)) + + requests = self.env["subscription.request"].search(domain) + + response = { + "count": len(requests), + "rows": [self._to_dict(sr) for sr in requests], + } + return response + + def create(self, **params): # pylint: disable=method-required-super + + params = self._prepare_create(params) + sr = self.env["subscription.request"].create(params) + return self._to_dict(sr) + + def update(self, _id, **params): + params = self._prepare_update(params) + sr = self.env["subscription.request"].browse(_id) + if not sr: + raise wrapJsonException( + NotFound(_("No subscription request for id %s") % _id) + ) + sr.write(params) + return self._to_dict(sr) + + def _to_dict(self, sr): + sr.ensure_one() + return { + "id": sr.id, + "name": sr.name, + "email": sr.email, + "state": sr.state, + "date": Date.to_string(sr.date), + "ordered_parts": sr.ordered_parts, + "share_product": { + "id": sr.share_product_id.id, + "name": sr.share_product_id.name, + }, + "address": { + "street": sr.address, + "zip_code": sr.zip_code, + "city": sr.city, + "country": sr.country_id.code, + }, + "lang": sr.lang, + } + + def _get_country(self, code): + country = self.env["res.country"].search([("code", "=", code)]) + if country: + return country + else: + raise wrapJsonException( + BadRequest(_("No country for isocode %s") % code) + ) + + def _prepare_create(self, params): + address = params["address"] + country = self._get_country(address["country"]) + + return { + "name": params["name"], + "email": params["email"], + "ordered_parts": params["ordered_parts"], + "share_product_id": params["share_product"], + "address": address["street"], + "zip_code": address["zip_code"], + "city": address["city"], + "country_id": country.id, + "lang": params["lang"], + } + + def _prepare_update(self, params): + if "address" in params: + address = params["address"] + if "country" in address: + country = self._get_country(address["country"]).id + address["country"] = country + else: + address = {} + + params = { + "name": params.get("name"), + "email": params.get("email"), + "state": params.get("state"), + "ordered_parts": params.get("ordered_parts"), + "share_product_id": params.get("share_product"), + "address": address.get("street"), + "zip_code": address.get("zip_code"), + "city": address.get("city"), + "country_id": address.get("country"), + "lang": params.get("lang"), + } + params = {k: v for k, v in params.items() if v is not None} + return params + + def _validator_get(self): + return schemas.S_SUBSCRIPTION_REQUEST_GET + + def _validator_return_get(self): + return schemas.S_SUBSCRIPTION_REQUEST_RETURN_GET + + def _validator_search(self): + return schemas.S_SUBSCRIPTION_REQUEST_SEARCH + + def _validator_return_search(self): + return schemas.S_SUBSCRIPTION_REQUEST_RETURN_SEARCH + + def _validator_create(self): + return schemas.S_SUBSCRIPTION_REQUEST_CREATE + + def _validator_return_create(self): + return schemas.S_SUBSCRIPTION_REQUEST_RETURN_GET + + def _validator_update(self): + return schemas.S_SUBSCRIPTION_REQUEST_UPDATE + + def _validator_return_update(self): + return schemas.S_SUBSCRIPTION_REQUEST_RETURN_GET diff --git a/easy_my_coop_api/static/description/index.html b/easy_my_coop_api/static/description/index.html new file mode 100644 index 0000000..81a2b67 --- /dev/null +++ b/easy_my_coop_api/static/description/index.html @@ -0,0 +1,442 @@ + + + + + + +Easy My Coop API + + + +
+

Easy My Coop API

+ + +

Beta License: AGPL-3 coopiteasy/vertical-cooperative

+

Open Easy My Coop to the world: RESTful API.

+

Table of contents

+ +
+

Usage

+

To give access to the API to a structure, go to

+
    +
  • Settings > Technical (debug mode) > Auth API Key
  • +
  • click create and select a user, save.
  • +
  • communicate the API-KEY to the structure.
  • +
+

It is recommended to create a technical user for the structure belonging to the group “Easy My Coop User”. +For example, for the structure Coop IT Easy, create partner with

+
    +
  • name = coopiteasy-api-user
  • +
  • Application Accesses = Cooperative Management / User
  • +
  • Platform Structure = Coop IT Easy
  • +
+
+
+

Known issues / Roadmap

+

The API should generate and use an external id for records instead +of odoo’s generated id. It would make importing and export data as +well as migrating across versions easier.

+

One way would be to use uuid but the default BaseRESTController +routes would need to be rewritten: they only take integer as ids. +Another way is to define a sequence per model.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Coop IT Easy SCRLfs
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the coopiteasy/vertical-cooperative project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/easy_my_coop_api/tests/__init__.py b/easy_my_coop_api/tests/__init__.py new file mode 100644 index 0000000..f831997 --- /dev/null +++ b/easy_my_coop_api/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_ping +from . import test_registry +from . import test_subscription_requests diff --git a/easy_my_coop_api/tests/common.py b/easy_my_coop_api/tests/common.py new file mode 100644 index 0000000..1e15a7f --- /dev/null +++ b/easy_my_coop_api/tests/common.py @@ -0,0 +1,106 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +import json + +import requests +from lxml import html + +import odoo +from odoo.fields import Date + +from odoo.addons.base_rest.tests.common import BaseRestCase + +HOST = "127.0.0.1" +PORT = odoo.tools.config["http_port"] + + +def _add_api_key(headers): + key_dict = {"API-KEY": "api-key"} + if headers: + headers.update(key_dict) + else: + headers = key_dict + return headers + + +class BaseEMCRestCase(BaseRestCase): + @classmethod + def setUpClass(cls, *args, **kwargs): + super().setUpClass(*args, **kwargs) + cls.AuthApiKey = cls.env["auth.api.key"] + emc_manager = cls.env.ref("easy_my_coop.res_users_manager_emc_demo") + cls.api_key_test = cls.AuthApiKey.create( + {"name": "test-key", "key": "api-key", "user_id": emc_manager.id} + ) + + def setUp(self): + super().setUp() + self.session = requests.Session() + self.demo_request_1 = self.browse_ref( + "easy_my_coop.subscription_request_1_demo" + ) + self.demo_share_product = self.demo_request_1.share_product_id + + date = Date.to_string(self.demo_request_1.date) + self.demo_request_1_dict = { + "id": self.demo_request_1.id, + "name": "Manuel Dublues", + "email": "manuel@demo.net", + "date": date, + "state": "draft", + "ordered_parts": 3, + "share_product": { + "id": self.demo_share_product.id, + "name": self.demo_share_product.name, + }, + "address": { + "street": "schaerbeekstraat", + "zip_code": "1111", + "city": "Brussels", + "country": "BE", + }, + "lang": "en_US", + } + + def http_get(self, url, headers=None): + headers = _add_api_key(headers) + if url.startswith("/"): + url = "http://{}:{}{}".format(HOST, PORT, url) + + return self.session.get(url, headers=headers) + + def http_get_content(self, route, headers=None): + response = self.http_get(route, headers=headers) + self.assertEquals(response.status_code, 200) + content = response.content.decode("utf-8") + return json.loads(content) + + def http_post(self, url, data, headers=None): + headers = _add_api_key(headers) + if url.startswith("/"): + url = "http://{}:{}{}".format(HOST, PORT, url) + + return self.session.post(url, json=data, headers=headers) + + @staticmethod + def html_doc(response): + """Get an HTML LXML document.""" + return html.fromstring(response.content) + + def login(self, login, password): + url = "/web/login" + response = self.http_get(url) + self.assertEquals(response.status_code, 200) + + doc = self.html_doc(response) + token = doc.xpath("//input[@name='csrf_token']")[0].get("value") + + response = self.http_post( + url=url, + data={"login": login, "password": password, "csrf_token": token}, + ) + self.assertEquals(response.status_code, 200) + return response diff --git a/easy_my_coop_api/tests/test_ping.py b/easy_my_coop_api/tests/test_ping.py new file mode 100644 index 0000000..ffb757b --- /dev/null +++ b/easy_my_coop_api/tests/test_ping.py @@ -0,0 +1,42 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +import json + +import requests + +from odoo.addons.base_rest.controllers.main import _PseudoCollection +from odoo.addons.component.core import WorkContext + +from .common import HOST, PORT, BaseEMCRestCase + + +class TestPing(BaseEMCRestCase): + def test_public_service(self): + collection = _PseudoCollection("emc.services", self.env) + emc_services_env = WorkContext( + model_name="rest.service.registration", collection=collection + ) + + service = emc_services_env.component(usage="ping") + result = service.test() + + self.assertTrue("message" in result) + + def test_ping_route(self): + # public route + path = "/api/ping/test" + url = "http://{}:{}{}".format(HOST, PORT, path) + response = requests.get(url) + self.assertEquals(response.status_code, 200) + content = json.loads(response.content.decode("utf-8")) + self.assertTrue("message" in content) + + def test_search_route(self): + response = self.http_get("/api/ping") + self.assertEquals(response.status_code, 200) + + content = json.loads(response.content.decode("utf-8")) + self.assertTrue("message" in content) diff --git a/easy_my_coop_api/tests/test_registry.py b/easy_my_coop_api/tests/test_registry.py new file mode 100644 index 0000000..b420eff --- /dev/null +++ b/easy_my_coop_api/tests/test_registry.py @@ -0,0 +1,28 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +import odoo +from odoo.http import controllers_per_module + +from odoo.addons.base_rest.tests.common import BaseRestCase + +from ..controllers.controllers import UserController + +HOST = "127.0.0.1" +PORT = odoo.tools.config["http_port"] + + +class TestControllerRegistry(BaseRestCase): + def test_controller_registry(self): + controllers = controllers_per_module["easy_my_coop_api"] + self.assertEqual(len(controllers), 1) + self.assertIn( + ( + "odoo.addons.easy_my_coop_api" + ".controllers.controllers.UserController", + UserController, + ), + controllers, + ) diff --git a/easy_my_coop_api/tests/test_subscription_requests.py b/easy_my_coop_api/tests/test_subscription_requests.py new file mode 100644 index 0000000..405205b --- /dev/null +++ b/easy_my_coop_api/tests/test_subscription_requests.py @@ -0,0 +1,143 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import json +from datetime import timedelta + +import odoo +from odoo.fields import Date + +from odoo.addons.base_rest.controllers.main import _PseudoCollection +from odoo.addons.component.core import WorkContext + +from .common import BaseEMCRestCase + + +class TestSRController(BaseEMCRestCase): + def setUp(self): + super().setUp() + collection = _PseudoCollection("emc.services", self.env) + emc_services_env = WorkContext( + model_name="rest.service.registration", collection=collection + ) + + self.service = emc_services_env.component(usage="subscription-request") + + def test_service(self): + # kept as example + # useful if you need to change data in database and check db type + + result = self.service.get(self.demo_request_1.id) + self.assertEquals(self.demo_request_1_dict, result) + + all_sr = self.service.search() + self.assertTrue(all_sr) + + sr_date = self.demo_request_1.date + date_from = Date.to_string(sr_date - timedelta(days=1)) + date_to = Date.to_string(sr_date + timedelta(days=1)) + + date_sr = self.service.search(date_from=date_from, date_to=date_to) + self.assertTrue(date_sr) + + def test_route_get(self): + id_ = self.demo_request_1.id + route = "/api/subscription-request/%s" % id_ + content = self.http_get_content(route) + self.assertEquals(self.demo_request_1_dict, content) + + @odoo.tools.mute_logger("odoo.addons.base_rest.http") + def test_route_get_returns_not_found(self): + route = "/api/subscription-request/%s" % "99999" + response = self.http_get(route) + self.assertEquals(response.status_code, 404) + + def test_route_get_string_returns_method_not_allowed(self): + route = "/api/subscription-request/%s" % "abc" + response = self.http_get(route) + self.assertEquals(response.status_code, 405) + + def test_route_search_all(self): + route = "/api/subscription-request" + content = self.http_get_content(route) + self.assertIn(self.demo_request_1_dict, content["rows"]) + + def test_route_search_by_date(self): + sr_date = self.demo_request_1.date + date_from = Date.to_string(sr_date - timedelta(days=1)) + date_to = Date.to_string(sr_date + timedelta(days=1)) + + route = "/api/subscription-request?date_from=%s" % date_from + content = self.http_get_content(route) + self.assertIn(self.demo_request_1_dict, content["rows"]) + + route = "/api/subscription-request?date_to=%s" % date_to + content = self.http_get_content(route) + self.assertIn(self.demo_request_1_dict, content["rows"]) + + route = "/api/subscription-request?date_from={}&date_to={}".format( + date_from, date_to + ) + content = self.http_get_content(route) + self.assertIn(self.demo_request_1_dict, content["rows"]) + + route = "/api/subscription-request?date_from=%s" % "2300-01-01" + content = self.http_get_content(route) + self.assertEquals(content["count"], 0) + + route = "/api/subscription-request?date_to=%s" % "1900-01-01" + content = self.http_get_content(route) + self.assertEquals(content["count"], 0) + + @odoo.tools.mute_logger("odoo.addons.base_rest.http") + def test_route_search_acd_date_returns_bad_request(self): + route = "/api/subscription-request?date_from=%s" % "20200101" + response = self.http_get(route) + self.assertEquals(response.status_code, 400) + + def test_route_create(self): + url = "/api/subscription-request" + data = { + "name": "Lisa des Danses", + "email": "lisa@desdanses.be", + "ordered_parts": 3, + "share_product": self.demo_share_product.id, + "address": { + "street": "schaerbeekstraat", + "zip_code": "1111", + "city": "Brussels", + "country": "BE", + }, + "lang": "en_US", + } + + response = self.http_post(url, data=data) + self.assertEquals(response.status_code, 200) + content = json.loads(response.content.decode("utf-8")) + + content.pop("id") # can't know id in advance + expected = { + **data, + **{ + "date": Date.to_string(Date.today()), + "state": "draft", + "share_product": { + "id": self.demo_share_product.id, + "name": self.demo_share_product.name, + }, + }, + } + self.assertEquals(expected, content) + + def test_route_update(self): + url = "/api/subscription-request/%s" % self.demo_request_1.id + data = {"state": "done"} + + response = self.http_post(url, data=data) + self.assertEquals(response.status_code, 200) + content = json.loads(response.content.decode("utf-8")) + + expected = self.demo_request_1_dict + expected["state"] = "done" + self.assertEquals(expected, content) diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 4a5c828..b9e8e82 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,5 +1,7 @@ # List the OCA project dependencies, one per line # Add a repository url and branch if you need a forked version -partner-contact addons https://github.com/coopiteasy/addons +partner-contact +rest-framework +server-auth