@ -19,12 +19,14 @@
# If not, see <http://www.gnu.org/licenses/>.
# If not, see <http://www.gnu.org/licenses/>.
#
#
#############################################################################
#############################################################################
import json
import json
import logging
import logging
from datetime import datetime
from datetime import datetime
from odoo import http
from odoo import http
from odoo . http import request
from odoo . http import request , Response
from ast import literal_eval
_logger = logging . getLogger ( __name__ )
_logger = logging . getLogger ( __name__ )
@ -36,220 +38,232 @@ class RestApi(http.Controller):
def auth_api_key ( self , api_key ) :
def auth_api_key ( self , api_key ) :
""" This function is used to authenticate the api-key when sending a
""" This function is used to authenticate the api-key when sending a
request """
request """
user_id = request . env [ ' res.users ' ] . sudo ( ) . search ( [ ( ' api_key ' , ' = ' , api_key ) ] )
user_id = request . env [ " res.users " ] . search ( [ ( " api_key " , " = " , api_key ) ] )
if api_key is not None and user_id :
if api_key is not None and user_id :
response = True
return Response ( json . dumps ( { " message " : " Authorized " } ) , status = 200 )
elif not user_id :
elif not user_id :
response = ( ' <html><body><h2>Invalid <i>API Key</i> '
return Response ( json . dumps ( { " message " : " Invalid API Key " } ) , status = 401 )
' !</h2></body></html> ' )
return Response ( json . dumps ( { " message " : " No API Key Provided " } ) , status = 400 )
else :
response = ( " <html><body><h2>No <i>API Key</i> Provided "
def simplest_type ( self , input ) :
" !</h2></body></html> " )
""" Try cast input into native Python class, otherwise return as string """
return response
try :
return literal_eval ( input )
except Exception :
# Handle lowercase booleans
if input == " true " :
return True
if input == " false " :
return False
return input
def generate_response ( self , method , model , rec_id ) :
def sanitize_records ( self , records ) :
""" Sanitize records for response """
for record in records :
for key , value in record . items ( ) :
# Manually convert datetime fields to string format
if isinstance ( value , datetime ) :
record [ key ] = value . isoformat ( )
return records
def generate_response ( self , method , * * query ) :
""" This function is used to generate the response based on the type
""" This function is used to generate the response based on the type
of request and the parameters given """
of request and the parameters given """
option = request . env [ ' connection.api ' ] . search (
[ ( ' model_id ' , ' = ' , model ) ] , limit = 1 )
model_name = option . model_id . model
if method != ' DELETE ' :
data = json . loads ( request . httprequest . data )
else :
data = { }
fields = [ ]
if data :
for field in data [ ' fields ' ] :
fields . append ( field )
if not fields and method != ' DELETE ' :
return ( " <html><body><h2>No fields selected for the model "
" </h2></body></html> " )
if not option :
return ( " <html><body><h2>No Record Created for the model "
" </h2></body></html> " )
try :
try :
if method == ' GET ' :
model = query . pop ( " model " )
fields = [ ]
option = request . env [ " connection.api " ] . search (
for field in data [ ' fields ' ] :
[ ( " model_id " , " = " , model ) ] , limit = 1
)
model_name = option . model_id . model
model_display_name = option . model_id . name
try :
data = json . loads ( request . httprequest . data )
except Exception :
data = { }
fields = [ ]
if data :
for field in data [ " fields " ] :
fields . append ( field )
fields . append ( field )
# Return records' ID by default if not specified
if not fields :
fields . append ( " id " )
# Get all model's fields if wildcard is used
if " * " in fields :
fields = [ ]
record_fields = request . env [ str ( model_name ) ] . fields_get (
[ ] , attributes = [ " type " ]
)
for field , value in record_fields . items ( ) :
value_type = value . get ( " type " )
if not ( value_type == " binary " ) :
fields . append ( field )
if not option :
raise NotImplementedError ( " No Record Created for the model. " )
if method == " GET " :
if not option . is_get :
if not option . is_get :
return ( " <html><body><h2>Method Not Allowed "
raise NameError ( )
" </h2></body></html> " )
limit = 0
else :
if query . get ( " limit " ) :
datas = [ ]
limit = int ( str ( query . get ( " limit " ) ) )
if rec_id != 0 :
offset = 0
partner_records = request . env [
if query . get ( " offset " ) :
str ( model_name )
offset = int ( str ( query . get ( " offset " ) ) )
] . search_read (
domain = [ ( ' id ' , ' = ' , rec_id ) ] ,
domains = [ ]
fields = fields
for key , value in query . items ( ) :
)
if not ( key == " limit " or key == " offset " ) :
domains . append ( ( key , " = " , self . simplest_type ( value ) ) )
# Manually convert datetime fields to string format
partner_records = request . env [ str ( model_name ) ] . search_read (
for record in partner_records :
domains , fields , limit = limit , offset = offset
for key , value in record . items ( ) :
)
if isinstance ( value , datetime ) :
record [ key ] = value . isoformat ( )
return Response (
data = json . dumps ( {
json . dumps ( { " records " : self . sanitize_records ( partner_records ) } )
' records ' : partner_records
)
} )
if method == " POST " :
datas . append ( data )
if not option . is_post :
return request . make_response ( data = datas )
raise NotImplementedError ( )
else :
if not data or " values " not in data :
partner_records = request . env [
raise ValueError ( " No Data Provided " )
str ( model_name )
] . search_read (
data = json . loads ( request . httprequest . data )
domain = [ ] ,
new_resource = request . env [ str ( model_name ) ] . create ( data [ " values " ] )
fields = fields
partner_records = request . env [ str ( model_name ) ] . search_read (
)
[ ( " id " , " = " , new_resource . id ) ] , fields
)
# Manually convert datetime fields to string format
return Response (
for record in partner_records :
json . dumps ( { " new_record " : self . sanitize_records ( partner_records ) } ) ,
for key , value in record . items ( ) :
status = 201 ,
if isinstance ( value , datetime ) :
)
record [ key ] = value . isoformat ( )
if method == " PUT " :
if not option . is_put :
data = json . dumps ( {
raise NotImplementedError ( )
' records ' : partner_records
} )
if " id " not in query :
datas . append ( data )
raise ValueError ( " No ID Provided " )
return request . make_response ( data = datas )
if not data or " values " not in data :
except :
raise ValueError ( " No Data Provided " )
return ( " <html><body><h2>Invalid JSON Data "
" </h2></body></html> " )
resource_id = str ( query . get ( " id " ) )
if method == ' POST ' :
resource = request . env [ str ( model_name ) ] . browse ( int ( resource_id ) )
if not option . is_post :
if not resource . exists ( ) :
return ( " <html><body><h2>Method Not Allowed "
raise ValueError ( " Resource not found " )
" </h2></body></html> " )
else :
data = json . loads ( request . httprequest . data )
try :
resource . write ( data [ " values " ] )
data = json . loads ( request . httprequest . data )
partner_records = request . env [ str ( model_name ) ] . search_read (
datas = [ ]
[ ( " id " , " = " , resource . id ) ] , fields
new_resource = request . env [ str ( model_name ) ] . create (
)
data [ ' values ' ] )
return Response (
partner_records = request . env [
json . dumps (
str ( model_name ) ] . search_read (
{ " updated_record " : self . sanitize_records ( partner_records ) }
domain = [ ( ' id ' , ' = ' , new_resource . id ) ] ,
fields = fields
)
)
new_data = json . dumps ( { ' New resource ' : partner_records , } )
)
datas . append ( new_data )
if method == " DELETE " :
return request . make_response ( data = datas )
if not option . is_delete :
except :
raise NotImplementedError ( )
return ( " <html><body><h2>Invalid JSON Data "
" </h2></body></html> " )
if " id " not in query :
if method == ' PUT ' :
raise ValueError ( " No ID Provided " )
if not option . is_put :
return ( " <html><body><h2>Method Not Allowed "
resource_id = str ( query . get ( " id " ) )
" </h2></body></html> " )
resource = request . env [ str ( model_name ) ] . browse ( int ( resource_id ) )
else :
if not resource . exists ( ) :
if rec_id == 0 :
raise ValueError ( " Resource not found " )
return ( " <html><body><h2>No ID Provided "
" </h2></body></html> " )
partner_records = request . env [ str ( model_name ) ] . search_read (
else :
[ ( " id " , " = " , resource . id ) ] , fields
resource = request . env [ str ( model_name ) ] . browse (
)
int ( rec_id ) )
resource . unlink ( )
if not resource . exists ( ) :
return Response (
return ( " <html><body><h2>Resource not found "
json . dumps (
" </h2></body></html> " )
{
else :
" message " : " Resource deleted " ,
try :
" data " : self . sanitize_records ( partner_records ) ,
datas = [ ]
}
data = json . loads ( request . httprequest . data )
) ,
resource . write ( data [ ' values ' ] )
status = 202 ,
partner_records = request . env [
)
str ( model_name ) ] . search_read (
domain = [ ( ' id ' , ' = ' , resource . id ) ] ,
# If not using any method above, simply return an error
fields = fields
raise NotImplementedError ( )
)
except ValueError as e :
new_data = json . dumps (
return Response ( json . dumps ( { " message " : e . args [ 0 ] } ) , status = 403 )
{ ' Updated resource ' : partner_records ,
except NotImplementedError as e :
} )
return Response (
datas . append ( new_data )
json . dumps (
return request . make_response ( data = datas )
{
" message " : f " Method not allowed. { e . args [ 0 ] } Please contact your admininstrator to enable { method } method for { model_display_name or ' this ' } record. "
except :
}
return ( " <html><body><h2>Invalid JSON Data "
) ,
" !</h2></body></html> " )
status = 405 ,
if method == ' DELETE ' :
)
if not option . is_delete :
except Exception :
return ( " <html><body><h2>Method Not Allowed "
return Response (
" </h2></body></html> " )
json . dumps ( { " message " : " Internal server error " } ) , status = 500
else :
)
if rec_id == 0 :
return ( " <html><body><h2>No ID Provided "
@http . route (
" </h2></body></html> " )
[ " /send_request " ] ,
else :
type = " http " ,
resource = request . env [ str ( model_name ) ] . browse (
auth = " none " ,
int ( rec_id ) )
methods = [ " GET " , " POST " , " PUT " , " DELETE " ] ,
if not resource . exists ( ) :
csrf = False ,
return ( " <html><body><h2>Resource not found "
)
" </h2></body></html> " )
else :
records = request . env [
str ( model_name ) ] . search_read (
domain = [ ( ' id ' , ' = ' , resource . id ) ] ,
fields = [ ' id ' , ' display_name ' ]
)
remove = json . dumps (
{ " Resource deleted " : records ,
} )
resource . unlink ( )
return request . make_response ( data = remove )
@http . route ( [ ' /send_request ' ] , type = ' http ' ,
auth = ' none ' ,
methods = [ ' GET ' , ' POST ' , ' PUT ' , ' DELETE ' ] , csrf = False )
def fetch_data ( self , * * kw ) :
def fetch_data ( self , * * kw ) :
""" This controller will be called when sending a request to the
""" This controller will be called when sending a request to the
specified url , and it will authenticate the api - key and then will
specified url , and it will authenticate the api - key and then will
generate the result """
generate the result """
http_method = request . httprequest . method
http_method = request . httprequest . method
api_key = request . httprequest . headers . get ( ' api-key ' )
api_key = request . httprequest . headers . get ( " api-key " )
auth_api = self . auth_api_key ( api_key )
auth_api = self . auth_api_key ( api_key )
model = kw . get ( ' model ' )
model = kw . pop ( " model " )
username = request . httprequest . headers . get ( ' login ' )
username = request . httprequest . headers . get ( " login " )
password = request . httprequest . headers . get ( ' password ' )
password = request . httprequest . headers . get ( " password " )
request . session . authenticate ( request . session . db , username ,
request . session . authenticate ( request . session . db , username , password )
password )
model_id = request . env [ " ir.model " ] . search ( [ ( " model " , " = " , model ) ] )
model_id = request . env [ ' ir.model ' ] . search (
[ ( ' model ' , ' = ' , model ) ] )
if not model_id :
if not model_id :
return ( " <html><body><h3>Invalid model, check spelling or maybe "
return Response (
" the related "
json . dumps (
" module is not installed "
{
" </h3></body></html> " )
" message " : " Invalid model, check spelling or maybe the related module is not installed "
}
if auth_api == True :
) ,
if not kw . get ( ' Id ' ) :
status = 403 ,
rec_id = 0
)
else :
rec_id = int ( kw . get ( ' Id ' ) )
if auth_api . status_code == 200 :
result = self . generate_response ( http_method , model_id . id , rec_id )
result = self . generate_response ( http_method , model = model _id. id , * * kw )
return result
return result
else :
else :
return auth_api
return auth_api
@http . route ( [ ' /odoo_connect ' ] , type = " http " , auth = " none " , csrf = False ,
@http . route (
methods = [ ' GET ' ] )
[ " /odoo_connect " ] , type = " http " , auth = " none " , csrf = False , methods = [ " GET " ]
)
def odoo_connect ( self , * * kw ) :
def odoo_connect ( self , * * kw ) :
""" This is the controller which initializes the api transaction by
""" This is the controller which initializes the api transaction by
generating the api - key for specific user and database """
generating the api - key for specific user and database """
username = request . httprequest . headers . get ( ' login ' )
username = request . httprequest . headers . get ( " login " )
password = request . httprequest . headers . get ( ' password ' )
password = request . httprequest . headers . get ( " password " )
db = request . httprequest . headers . get ( ' db ' )
db = request . httprequest . headers . get ( " db " )
try :
try :
request . session . update ( http . get_default_session ( ) , db = db )
request . session . update ( http . get_default_session ( ) , db = db )
auth = request . session . authenticate ( request . session . db , username ,
auth = request . session . authenticate ( request . session . db , username , password )
password )
user = request . env [ " res.users " ] . browse ( auth )
user = request . env [ ' res.users ' ] . browse ( auth )
api_key = request . env . user . generate_api ( username )
api_key = request . env . user . generate_api ( username )
datas = json . dumps ( { " Status " : " auth successful " ,
datas = json . dumps (
" User " : user . name ,
{ " Status " : " auth successful " , " User " : user . name , " api-key " : api_key }
" api-key " : api_key } )
)
return request . make_response ( data = datas )
return Response ( datas )
except :
except Exception :
return ( " <html><body><h2>wrong login credentials "
return Response (
" </h2></body></html> " )
json . dumps ( { " message " : " wrong login credentials " } ) , status = 401
)