@ -22,6 +22,8 @@
import json
import json
import logging
import logging
from datetime import datetime
from odoo import http
from odoo import http
from odoo . http import request , Response
from odoo . http import request , Response
from ast import literal_eval
from ast import literal_eval
@ -37,195 +39,259 @@ class RestApi(http.Controller):
""" 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 ' ] . 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 :
return True
return Response ( json . dumps ( { " message " : " Authorized " } ) , status = 200 )
elif not user_id :
elif not user_id :
return Response ( json . dumps ( { ' message ' : ' Invalid API Key ' } ) , status = 401 )
return Response ( json . dumps ( { " message " : " Invalid API Key " } ) , status = 401 )
return Response ( json . dumps ( { ' message ' : ' No API Key Provided ' } ) , status = 400 )
return Response ( json . dumps ( { " message " : " No API Key Provided " } ) , status = 400 )
def simplest_type ( self , input ) :
def simplest_type ( self , input ) :
""" Try cast input into native Python class, otherwise return as string """
""" Try cast input into native Python class, otherwise return as string """
try :
try :
return literal_eval ( input )
return literal_eval ( input )
except :
except Exception :
if input == ' true ' :
# Handle lowercase booleans
if input == " true " :
return True
return True
if input == ' false ' :
if input == " false " :
return False
return False
return input
return input
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 ) :
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 """
model = query . pop ( ' model ' )
model = query . pop ( " model " )
option = request . env [ ' connection.api ' ] . search (
option = request . env [ " connection.api " ] . search (
[ ( ' model_id ' , ' = ' , model ) ] , limit = 1 )
[ ( " model_id " , " = " , model ) ] , limit = 1
)
model_name = option . model_id . model
model_name = option . model_id . model
model_display_name = option . model_id . name
model_display_name = option . model_id . name
data = { }
try :
try :
data = json . loads ( request . httprequest . data )
data = json . loads ( request . httprequest . data )
except :
except Exception :
pass
data = { }
fields = [ ]
fields = [ ]
if data :
if data :
for field in data [ ' fields ' ] :
for field in data [ " fields " ] :
fields . append ( field )
fields . append ( field )
if ' * ' in fields :
# 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 = [ ]
fields = [ ]
record_fields = request . env [
record_fields = request . env [ str ( model_name ) ] . fields_get (
str ( model_name ) ] . fields_get ( [ ] , attributes = [ ' type ' ] )
[ ] , attributes = [ " type " ]
)
for field , value in record_fields . items ( ) :
for field , value in record_fields . items ( ) :
value_type = value . get ( ' type ' )
value_type = value . get ( " type " )
if not ( value_type == ' binary ' or value_type == ' datetime ' ) :
if not ( value_type == " binary " ) :
fields . append ( field )
fields . append ( field )
if not option :
if not option :
return Response ( json . dumps ( { ' message ' : f ' No Record Created for the model. Please contact your admininstrator to enable { method } method for { model_display_name } record. ' } ) , status = 403 )
return Response (
try :
json . dumps (
if method == ' GET ' :
{
if not fields :
" message " : f " No Record Created for the model. Please contact your admininstrator to enable { method } method for { model_display_name } record. "
fields . append ( ' id ' )
}
if not option . is_get :
) ,
return Response (
status = 403 ,
json . dumps ( { ' message ' : f ' Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. ' } ) ,
)
status = 405 )
if method == " GET " :
else :
if not option . is_get :
domains = [ ]
return Response (
for key , value in query . items ( ) :
json . dumps (
domains . append ( ( key , ' = ' , self . simplest_type ( value ) ) )
{
partner_records = request . env [
" message " : f " Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. "
str ( model_name ) ] . search_read (
}
domain = domains ,
) ,
fields = fields
status = 405 ,
)
)
return Response (
json . dumps ( {
domains = [ ]
' records ' : partner_records
for key , value in query . items ( ) :
} )
domains . append ( ( key , " = " , self . simplest_type ( value ) ) )
)
partner_records = request . env [ str ( model_name ) ] . search_read (
if method == ' POST ' :
domain = domains , fields = fields
if not option . is_post :
)
return Response (
json . dumps ( { ' message ' : f ' Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. ' } ) ,
return Response (
status = 405 )
json . dumps ( { " records " : self . sanitize_records ( partner_records ) } )
if not data or ' values ' not in data :
)
return Response ( json . dumps ( { ' message ' : ' No Data Provided ' } ) , status = 403 )
if method == " POST " :
if not option . is_post :
try :
return Response (
data = json . loads ( request . httprequest . data )
json . dumps (
new_resource = request . env [ str ( model_name ) ] . create (
{
data [ ' values ' ] )
" message " : f " Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. "
partner_records = request . env [
}
str ( model_name ) ] . search_read (
) ,
domain = [ ( ' id ' , ' = ' , new_resource . id ) ] ,
status = 405 ,
fields = fields
)
)
if not data or " values " not in data :
return Response ( json . dumps ( { ' new_record ' : partner_records } ) , status = 201 )
return Response ( json . dumps ( { " message " : " No Data Provided " } ) , status = 403 )
except :
return Response ( json . dumps ( { ' message ' : ' Invalid JSON Data ' } ) , status = 403 )
try :
if method == ' PUT ' :
data = json . loads ( request . httprequest . data )
if not option . is_put :
new_resource = request . env [ str ( model_name ) ] . create ( data [ " values " ] )
return Response (
partner_records = request . env [ str ( model_name ) ] . search_read (
json . dumps ( { ' message ' : f ' Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. ' } ) ,
domain = [ ( " id " , " = " , new_resource . id ) ] , fields = fields
status = 405 )
)
return Response (
if not ' id ' in query :
json . dumps ( { " new_record " : self . sanitize_records ( partner_records ) } ) ,
return Response ( json . dumps ( { ' message ' : ' No ID Provided ' } ) , status = 403 )
status = 201 ,
if not data or ' values ' not in data :
)
return Response ( json . dumps ( { ' message ' : ' No Data Provided ' } ) , status = 403 )
except Exception :
return Response (
resource = request . env [ str ( model_name ) ] . browse (
json . dumps ( { " message " : " Invalid JSON Data " } ) , status = 403
int ( query . get ( ' id ' ) ) )
)
if not resource . exists ( ) :
if method == " PUT " :
return Response ( json . dumps ( { ' message ' : ' Resource not found ' } ) , status = 404 )
if not option . is_put :
return Response (
try :
json . dumps (
data = json . loads ( request . httprequest . data )
{
resource . write ( data [ ' values ' ] )
" message " : f " Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. "
partner_records = request . env [
}
str ( model_name ) ] . search_read (
) ,
domain = [ ( ' id ' , ' = ' , resource . id ) ] ,
status = 405 ,
fields = fields
)
)
return Response ( json . dumps ( { ' updated_record ' : partner_records } ) )
if " id " not in query :
return Response ( json . dumps ( { " message " : " No ID Provided " } ) , status = 403 )
except :
if not data or " values " not in data :
return Response ( json . dumps ( { ' message ' : ' Invalid JSON value(s) passed ' } ) , status = 403 )
return Response ( json . dumps ( { " message " : " No Data Provided " } ) , status = 403 )
if method == ' DELETE ' :
if not option . is_delete :
resource_id = str ( query . get ( " id " ) )
return Response (
resource = request . env [ str ( model_name ) ] . browse ( int ( resource_id ) )
json . dumps ( { ' message ' : f ' Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. ' } ) ,
if not resource . exists ( ) :
status = 405 )
return Response (
json . dumps ( { " message " : " Resource not found " } ) , status = 404
if not ' id ' in query :
)
return Response ( json . dumps ( { ' message ' : ' No ID Provided ' } ) , status = 403 )
try :
resource = request . env [ str ( model_name ) ] . browse (
data = json . loads ( request . httprequest . data )
int ( query . get ( ' id ' ) ) )
resource . write ( data [ " values " ] )
if not resource . exists ( ) :
partner_records = request . env [ str ( model_name ) ] . search_read (
return Response ( json . dumps ( { ' message ' : ' Resource not found ' } ) , status = 404 )
domain = [ ( " id " , " = " , resource . id ) ] , fields = fields
else :
)
return Response (
records = request . env [
json . dumps (
str ( model_name ) ] . search_read (
{ " updated_record " : self . sanitize_records ( partner_records ) }
domain = [ ( ' id ' , ' = ' , resource . id ) ] ,
fields = fields
)
)
resource . unlink ( )
)
return Response ( json . dumps ( { ' message ' : ' Resource deleted ' , ' data ' : records } ) , status = 202 )
except :
except Exception :
return Response ( json . dumps ( { ' message ' : ' Internal Server Error ' } ) , status = 500 )
return Response (
json . dumps ( { " message " : " Invalid JSON value(s) passed " } ) ,
@http . route ( [ ' /send_request ' ] , type = ' http ' ,
status = 403 ,
auth = ' none ' ,
)
methods = [ ' GET ' , ' POST ' , ' PUT ' , ' DELETE ' ] , csrf = False )
if method == " DELETE " :
if not option . is_delete :
return Response (
json . dumps (
{
" message " : f " Method not allowed. Please contact your admininstrator to enable { method } method for { model_display_name } record. "
}
) ,
status = 405 ,
)
if " id " not in query :
return Response ( json . dumps ( { " message " : " No ID Provided " } ) , status = 403 )
resource_id = str ( query . get ( " id " ) )
resource = request . env [ str ( model_name ) ] . browse ( int ( resource_id ) )
if not resource . exists ( ) :
return Response (
json . dumps ( { " message " : " Resource not found " } ) , status = 404
)
partner_records = request . env [ str ( model_name ) ] . search_read (
domain = [ ( " id " , " = " , resource . id ) ] , fields = fields
)
resource . unlink ( )
return Response (
json . dumps (
{
" message " : " Resource deleted " ,
" data " : self . sanitize_records ( partner_records ) ,
}
) ,
status = 202 ,
)
# If not using any method above, simply return an error
return Response ( json . dumps ( { " message " : " Method not allowed " } ) , status = 405 )
@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 . pop ( ' 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 Response ( json . dumps (
return Response (
{ ' message ' : ' Invalid model, check spelling or maybe the related module is not installed ' } ) ,
json . dumps (
status = 403 )
{
" message " : " Invalid model, check spelling or maybe the related module is not installed "
}
) ,
status = 403 ,
)
if auth_api == True :
if auth_api . status_code == 200 :
result = self . generate_response ( http_method , model = model_id . id , * * kw )
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 , status = 200 )
except :
except Exception :
return Response ( json . dumps ( { ' message ' : ' wrong login credentials ' } ) , status = 401 )
return Response (
json . dumps ( { " message " : " wrong login credentials " } ) , status = 401
)