Skip to content

Commit

Permalink
[ADD] pms_api_rest: module created
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelpadin committed Jul 26, 2021
1 parent 63340f8 commit 111664b
Show file tree
Hide file tree
Showing 18 changed files with 639 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pms_api_rest/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import controllers
from . import datamodels
from . import models
from . import services

19 changes: 19 additions & 0 deletions pms_api_rest/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "API REST PMS",
"author": "Commit [Sun], Odoo Community Association (OCA)",
"website": "https://github.com/OCA/pms",
"category": "Generic Modules/Property Management System",
"version": "14.0.1.0.0",
"license": "AGPL-3",
"depends": [
"pms",
"base_rest",
"base_rest_datamodel",
"web",
"auth_signup",
],
"external_dependencies": {
"python": ["jwt", "simplejson", "marshmallow"],
},
"installable": True,
}
2 changes: 2 additions & 0 deletions pms_api_rest/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import jwt_controller
from . import pms_rest
110 changes: 110 additions & 0 deletions pms_api_rest/controllers/jwt_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import logging

import werkzeug

from odoo import http
from odoo.http import request

from odoo.addons.auth_signup.models.res_users import SignupError

from ..lib_jwt.jwt_http import jwt_http
from ..lib_jwt.validator import validator

_logger = logging.getLogger(__name__)

SENSITIVE_FIELDS = [
"password",
"password_crypt",
"new_password",
"create_uid",
"write_uid",
]


class JwtController(http.Controller):
# test route
@http.route("/api/info", auth="public", csrf=False, cors="*")
def index(self, **kw):
return "Hello, world"

@http.route(
"/api/login", type="http", auth="public", csrf=False, cors="*", methods=["POST"]
)
def login(self, email, password, **kw):
return jwt_http.do_login(email, password)

@http.route("/api/me", type="http", auth="public", csrf=False, cors="*")
def me(self, **kw):
http_method, body, headers, token = jwt_http.parse_request()
result = validator.verify_token(token)
if not result["status"]:
return jwt_http.errcode(code=result["code"], message=result["message"])

return jwt_http.response(request.env.user.to_dict(True))

@http.route("/api/logout", type="http", auth="public", csrf=False, cors="*")
def logout(self, **kw):
http_method, body, headers, token = jwt_http.parse_request()
result = validator.verify_token(token)
if not result["status"]:
return jwt_http.errcode(code=result["code"], message=result["message"])

jwt_http.do_logout(token)
return jwt_http.response()

@http.route(
"/api/register",
type="http",
auth="public",
csrf=False,
cors="*",
methods=["POST"],
)
def register(self, email=None, name=None, password=None, **kw):
if not validator.is_valid_email(email):
return jwt_http.errcode(code=400, message="Invalid email address")
if not name:
return jwt_http.errcode(code=400, message="Name cannot be empty")
if not password:
return jwt_http.errcode(code=400, message="Password cannot be empty")

# sign up
try:
self._signup_with_values(login=email, name=name, password=password)
except AttributeError:
return jwt_http.errcode(code=501, message="Signup is disabled")
except (SignupError, AssertionError) as e:
if request.env["res.users"].sudo().search([("login", "=", email)]):
return jwt_http.errcode(
code=400, message="Email address already exists"
)
else:
_logger.error("%s", e)
return jwt_http.response_500()
except Exception as e:
_logger.error(str(e))
return jwt_http.response_500()
# log the user in
return jwt_http.do_login(email, password)

def _signup_with_values(self, **values):
request.env["res.users"].sudo().signup(values, None)
request.env.cr.commit() # as authenticate will use its
# own cursor we need to commit the current transaction
self.signup_email(values)

def signup_email(self, values):
user_sudo = (
request.env["res.users"]
.sudo()
.search([("login", "=", values.get("login"))])
)
template = request.env.ref(
"auth_signup.mail_template_user_signup_account_created",
raise_if_not_found=False,
)
if user_sudo and template:
template.sudo().with_context(
lang=user_sudo.lang,
auth_login=werkzeug.url_encode({"auth_login": user_sudo.email}),
).send_mail(user_sudo.id, force_send=True)
22 changes: 22 additions & 0 deletions pms_api_rest/controllers/pms_rest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo.addons.base_rest.controllers import main

from ..lib_jwt.jwt_http import jwt_http
from ..lib_jwt.validator import validator


class BaseRestDemoPublicApiController(main.RestController):
_root_path = "/api/"
_collection_name = "pms.reservation.service"
_default_auth = "public"

# RestController OVERRIDE method
def _process_method(self, service_name, method_name, *args, params=None):

http_method, body, headers, token = jwt_http.parse_request()
result = validator.verify_token(token)
if not result["status"]:
return jwt_http.errcode(code=result["code"], message=result["message"])
else:
return super(BaseRestDemoPublicApiController, self)._process_method(
service_name, method_name, *args, params=params
)
2 changes: 2 additions & 0 deletions pms_api_rest/datamodels/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import pms_reservation_short_info
from . import pms_reservation_search_param
10 changes: 10 additions & 0 deletions pms_api_rest/datamodels/pms_reservation_search_param.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from marshmallow import fields

from odoo.addons.datamodel.core import Datamodel


class PmsReservationSearchParam(Datamodel):
_name = "pms.reservation.search.param"

id = fields.Integer(required=False, allow_none=False)
name = fields.String(required=False, allow_none=False)
16 changes: 16 additions & 0 deletions pms_api_rest/datamodels/pms_reservation_short_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from marshmallow import fields

from odoo.addons.datamodel.core import Datamodel


class PmsReservationShortInfo(Datamodel):
_name = "pms.reservation.short.info"

id = fields.Integer(required=True, allow_none=False)
partner = fields.String(required=True, allow_none=False)
checkin = fields.String(required=True, allow_none=False)
checkout = fields.String(required=True, allow_none=False)
preferred_room_id = fields.String(required=True, allow_none=False)
room_type_id = fields.String(required=True, allow_none=False)
name = fields.String(required=True, allow_none=False)
partner_requests = fields.String(required=False, allow_none=True)
129 changes: 129 additions & 0 deletions pms_api_rest/lib_jwt/jwt_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import simplejson as json

from odoo import http
from odoo.exceptions import AccessDenied
from odoo.http import Response, request

from .validator import validator

return_fields = ["id", "login", "name", "company_id"]


class JwtHttp:
def get_state(self):
return {"d": request.session.db}

def parse_request(self):
http_method = request.httprequest.method
try:
body = http.request.params
except Exception:
body = {}

headers = dict(list(request.httprequest.headers.items()))
if "wsgi.input" in headers:
del headers["wsgi.input"]
if "wsgi.errors" in headers:
del headers["wsgi.errors"]
if "HTTP_AUTHORIZATION" in headers:
headers["Authorization"] = headers["HTTP_AUTHORIZATION"]

# extract token
token = ""
if "Authorization" in headers:
try:
# Bearer token_string
token = headers["Authorization"].split(" ")[1]
except Exception:
pass

return http_method, body, headers, token

def date2str(self, d, f="%Y-%m-%d %H:%M:%S"):
"""
Convert datetime to string
:param self:
:param d: datetime object
:param f='%Y-%m-%d%H:%M:%S': string format
"""
try:
s = d.strftime(f)
except Exception:
s = None

return s

def response(self, success=True, message=None, data=None, code=200):
"""
Create a HTTP Response for controller
:param success=True indicate this response is successful or not
:param message=None message string
:param data=None data to return
:param code=200 http status code
"""
print('response')
print('response')
print('response')
print('response')
payload = json.dumps(
{
"success": success,
"message": message,
"data": data,
}
)

return Response(
payload,
status=code,
headers=[
("Content-Type", "application/json"),
],
)

def response_500(self, message="Internal Server Error", data=None):
return self.response(success=False, message=message, data=data, code=500)

def response_401(self, message="401 Unauthorized", data=None):
return self.response(success=False, message=message, data=data, code=401)

def response_404(self, message="404 Not Found", data=None):
return self.response(success=False, message=message, data=data, code=404)

def response_403(self, message="403 Forbidden", data=None):
return self.response(success=False, message=message, data=data, code=403)

def errcode(self, code, message=None):
return self.response(success=False, code=code, message=message)

def do_login(self, login, password):
# get current db
state = self.get_state()
try:
uid = request.session.authenticate(state["d"], login, password)
except AccessDenied:
return self.response_401()
if not uid:
return self.response_401()

# login success, generate token
user = request.env.user.read(return_fields)[0]
token = validator.create_token(user)

return self.response(data={"user": user, "token": token})

def do_logout(self, token):
request.session.logout()
request.env["jwt_provider.access_token"].sudo().search(
[("token", "=", token)]
).unlink()
return self.response()

def cleanup(self):
# Clean up things after success request
# use logout here to make request as stateless as possible
request.session.logout()
return self.response()


jwt_http = JwtHttp()
Loading

0 comments on commit 111664b

Please sign in to comment.