<s> import copy <EOL> import logging <EOL> import sqlalchemy <EOL> from opencenter . db . database import session <EOL> from opencenter . db import exceptions <EOL> from opencenter . db import inmemory <EOL> import opencenter . webapp . ast <EOL> LOG = logging . getLogger ( __name__ ) <EOL> class DbAbstraction ( object ) : <EOL> def __init__ ( self , api , model , name ) : <EOL> classname = self . __class__ . __name__ . lower ( ) <EOL> self . logger = logging . getLogger ( '<STR_LIT>' % ( __name__ , classname ) ) <EOL> self . api = api <EOL> self . name = name <EOL> self . model = model <EOL> def destroy_cache ( self ) : <EOL> pass <EOL> def get_columns ( self ) : <EOL> raise NotImplementedError <EOL> def get_all ( self ) : <EOL> raise NotImplementedError <EOL> def get_schema ( self ) : <EOL> raise NotImplementedError <EOL> def create ( self , data ) : <EOL> raise NotImplementedError <EOL> def delete ( self , id ) : <EOL> raise NotImplementedError <EOL> def get ( self , id ) : <EOL> raise NotImplementedError <EOL> def query ( self , query ) : <EOL> """<STR_LIT>""" <EOL> full_query = '<STR_LIT>' % ( self . name , query ) <EOL> builder = opencenter . webapp . ast . FilterBuilder ( <EOL> opencenter . webapp . ast . FilterTokenizer ( ) , <EOL> full_query , api = self . api ) <EOL> root = builder . build ( ) <EOL> if root . op == '<STR_LIT:=>' : <EOL> if root . lhs . op == '<STR_LIT>' and root . lhs . lhs == '<STR_LIT:id>' : <EOL> if root . rhs . op == '<STR_LIT>' or root . rhs . op == '<STR_LIT>' : <EOL> return [ self . get ( int ( root . rhs . lhs ) ) ] <EOL> result = builder . filter ( ) <EOL> return result <EOL> def update ( self , id , data ) : <EOL> raise NotImplementedError <EOL> def first_by_query ( self , query ) : <EOL> result = self . query ( query ) <EOL> if len ( result ) : <EOL> return result [ <NUM_LIT:0> ] <EOL> return None <EOL> def _sanitize_for_update ( self , data ) : <EOL> retval = copy . deepcopy ( data ) <EOL> schema = self . get_schema ( ) <EOL> ro_fields = [ x for x in schema if schema [ x ] [ '<STR_LIT>' ] is False ] <EOL> for field in ro_fields : <EOL> if field in retval : <EOL> retval . pop ( field ) <EOL> for field in data : <EOL> if not field in schema . keys ( ) : <EOL> retval . pop ( field ) <EOL> return retval <EOL> def _sanitize_for_create ( self , data ) : <EOL> retval = copy . deepcopy ( data ) <EOL> schema = self . get_schema ( ) <EOL> required_fields = [ x for x in schema if schema [ x ] [ '<STR_LIT>' ] is True ] <EOL> ro_fields = [ x for x in schema if schema [ x ] [ '<STR_LIT>' ] is True ] <EOL> if '<STR_LIT:id>' in required_fields : <EOL> required_fields . remove ( '<STR_LIT:id>' ) <EOL> if '<STR_LIT:id>' in retval : <EOL> retval . pop ( '<STR_LIT:id>' ) <EOL> for field in required_fields : <EOL> if not field in retval : <EOL> raise KeyError ( '<STR_LIT>' % field ) <EOL> for field in ro_fields : <EOL> if field in retval : <EOL> retval . pop ( field ) <EOL> for field in data : <EOL> if not field in schema . keys ( ) : <EOL> retval . pop ( field ) <EOL> return retval <EOL> def _validate_id_format ( self , id_to_check ) : <EOL> try : <EOL> return int ( id_to_check ) <EOL> except ( TypeError , ValueError ) : <EOL> msg = '<STR_LIT>' <EOL> raise exceptions . IdInvalid ( message = msg ) <EOL> class SqlAlchemyAbstraction ( DbAbstraction ) : <EOL> def __init__ ( self , api , model , name ) : <EOL> super ( SqlAlchemyAbstraction , self ) . __init__ ( api , model , name ) <EOL> def get_columns ( self ) : <EOL> field_list = [ c for c in self . model . __table__ . columns . keys ( ) ] <EOL> if hasattr ( self . model , '<STR_LIT>' ) : <EOL> field_list += self . model . _synthesized_fields <EOL> return field_list <EOL> def get_all ( self ) : <EOL> return [ x . jsonify ( api = self . api ) for x in self . model . query . all ( ) ] <EOL> def get_schema ( self ) : <EOL> obj = self . model <EOL> cols = obj . __table__ . columns <EOL> fields = { } <EOL> for k in cols . keys ( ) : <EOL> fields [ k ] = { } <EOL> fields [ k ] [ '<STR_LIT:type>' ] = str ( cols [ k ] . type ) <EOL> if repr ( cols [ k ] . type ) == '<STR_LIT>' : <EOL> fields [ k ] [ '<STR_LIT:type>' ] = '<STR_LIT>' <EOL> if repr ( cols [ k ] . type ) == '<STR_LIT>' : <EOL> fields [ k ] [ '<STR_LIT:type>' ] = '<STR_LIT>' <EOL> fields [ k ] [ '<STR_LIT:primary_key>' ] = cols [ k ] . primary_key <EOL> fields [ k ] [ '<STR_LIT>' ] = cols [ k ] . unique or cols [ k ] . primary_key <EOL> fields [ k ] [ '<STR_LIT>' ] = True <EOL> fields [ k ] [ '<STR_LIT>' ] = not cols [ k ] . nullable <EOL> fields [ k ] [ '<STR_LIT>' ] = False <EOL> if hasattr ( obj , '<STR_LIT>' ) : <EOL> if k in obj . _non_updatable_fields : <EOL> fields [ k ] [ '<STR_LIT>' ] = False <EOL> if len ( cols [ k ] . foreign_keys ) > <NUM_LIT:0> : <EOL> fields [ k ] [ '<STR_LIT>' ] = list ( cols [ k ] . foreign_keys ) [ <NUM_LIT:0> ] . target_fullname <EOL> if hasattr ( obj , '<STR_LIT>' ) : <EOL> for syn in obj . _synthesized_fields : <EOL> fields [ syn ] = { '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : True , <EOL> '<STR_LIT:primary_key>' : False } <EOL> return fields <EOL> def create ( self , data ) : <EOL> """<STR_LIT>""" <EOL> new_data = self . _sanitize_for_create ( data ) <EOL> if self . name in ( '<STR_LIT>' , '<STR_LIT>' ) : <EOL> node = self . api . _model_get_by_id ( '<STR_LIT>' , new_data [ '<STR_LIT>' ] ) <EOL> query = '<STR_LIT>' % ( node [ '<STR_LIT:id>' ] , new_data [ '<STR_LIT:key>' ] ) <EOL> existing = self . api . _model_query ( self . name , query ) <EOL> if len ( existing ) != <NUM_LIT:0> : <EOL> return self . update ( existing [ <NUM_LIT:0> ] [ '<STR_LIT:id>' ] , data ) <EOL> r = self . model ( ** new_data ) <EOL> session . add ( r ) <EOL> try : <EOL> session . commit ( ) <EOL> return r . jsonify ( api = self . api ) <EOL> except sqlalchemy . exc . IntegrityError as e : <EOL> session . rollback ( ) <EOL> msg = "<STR_LIT>" % ( self . name . title ( ) ) <EOL> raise exceptions . CreateError ( msg ) <EOL> except sqlalchemy . exc . StatementError as e : <EOL> session . rollback ( ) <EOL> msg = "<STR_LIT>" "<STR_LIT>" % str ( e ) <EOL> raise exceptions . CreateError ( msg ) <EOL> def delete ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> r = self . model . query . filter_by ( id = id ) . first ( ) <EOL> try : <EOL> session . delete ( r ) <EOL> session . commit ( ) <EOL> return True <EOL> except sqlalchemy . orm . exc . UnmappedInstanceError as e : <EOL> session . rollback ( ) <EOL> msg = "<STR_LIT>" % ( self . name . title ( ) ) <EOL> raise exceptions . IdNotFound ( message = msg ) <EOL> except sqlalchemy . exc . InvalidRequestError as e : <EOL> session . rollback ( ) <EOL> msg = e . msg <EOL> raise RuntimeError ( msg ) <EOL> def get ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> r = self . model . query . filter_by ( id = id ) . first ( ) <EOL> if not r : <EOL> msg = '<STR_LIT>' % ( self . name . title ( ) , id ) <EOL> raise exceptions . IdNotFound ( message = msg ) <EOL> return r . jsonify ( api = self . api ) <EOL> def update ( self , id , data ) : <EOL> id = self . _validate_id_format ( id ) <EOL> new_data = self . _sanitize_for_update ( data ) <EOL> r = self . model . query . filter_by ( id = id ) . first ( ) <EOL> for field in new_data : <EOL> r . __setattr__ ( field , data [ field ] ) <EOL> try : <EOL> ret = r . jsonify ( api = self . api ) <EOL> session . commit ( ) <EOL> return ret <EOL> except sqlalchemy . exc . InvalidRequestError as e : <EOL> session . rollback ( ) <EOL> msg = e . msg <EOL> raise RuntimeError ( msg ) <EOL> except : <EOL> session . rollback ( ) <EOL> raise <EOL> def _ast_to_sqlalchemy ( self , ast ) : <EOL> if ast . op == '<STR_LIT>' : <EOL> return sqlalchemy . and_ ( self . _ast_to_sqlalchemy ( ast . lhs ) , <EOL> self . _ast_to_sqlalchemy ( ast . rhs ) ) <EOL> if ast . op == '<STR_LIT>' : <EOL> return sqlalchemy . or_ ( self . _ast_to_sqlalchemy ( ast . lhs ) , <EOL> self . _ast_to_sqlalchemy ( ast . rhs ) ) <EOL> if ast . op == '<STR_LIT:=>' and ast . lhs . op == '<STR_LIT>' and ast . rhs . op in [ '<STR_LIT>' , '<STR_LIT>' ] : <EOL> return self . model . __table__ . columns [ ast . lhs . lhs ] == ast . rhs . lhs <EOL> raise ValueError ( '<STR_LIT>' ) <EOL> def query ( self , query ) : <EOL> """<STR_LIT>""" <EOL> full_query = '<STR_LIT>' % ( self . name , query ) <EOL> builder = opencenter . webapp . ast . FilterBuilder ( <EOL> opencenter . webapp . ast . FilterTokenizer ( ) , <EOL> full_query , api = self . api ) <EOL> root = builder . build ( ) <EOL> sql_filter = None <EOL> try : <EOL> sql_filter = self . _ast_to_sqlalchemy ( root ) <EOL> except ValueError : <EOL> self . logger . error ( '<STR_LIT>' % ( query ) ) <EOL> if sql_filter is not None : <EOL> r = self . model . query . filter ( sql_filter ) <EOL> if not r : <EOL> return None <EOL> else : <EOL> return [ x . jsonify ( api = self . api ) for x in r ] <EOL> return builder . filter ( ) <EOL> class APIAbstraction ( DbAbstraction ) : <EOL> def __init__ ( self , api , model , name , endpoint ) : <EOL> self . endpoint = endpoint <EOL> self . schema = None <EOL> self . objects = endpoint [ name ] <EOL> super ( APIAbstraction , self ) . __init__ ( api , model , name ) <EOL> def get_columns ( self ) : <EOL> if self . schema is None : <EOL> self . schema = self . get_schema ( ) <EOL> return self . schema . keys ( ) <EOL> def get_all ( self ) : <EOL> self . objects . _refresh ( True ) <EOL> for obj in self . objects : <EOL> yield obj . to_hash ( ) <EOL> def get_schema ( self ) : <EOL> self . objects . _maybe_refresh_schema ( ) <EOL> return self . objects . schema . field_schema <EOL> def create ( self , data ) : <EOL> new_data = self . _sanitize_for_create ( data ) <EOL> new_node = self . objects . new ( ** new_data ) <EOL> new_node . save ( ) <EOL> return new_node . to_hash ( ) <EOL> def delete ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> try : <EOL> obj = self . objects [ id ] <EOL> obj . delete ( ) <EOL> except KeyError : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> except ValueError : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> def get ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> try : <EOL> obj = self . objects [ id ] <EOL> if obj is None : <EOL> self . objects . _refresh ( True ) <EOL> obj = self . objects [ id ] <EOL> if obj is not None : <EOL> obj . _request ( '<STR_LIT>' ) <EOL> json_object = self . objects [ id ] . to_hash ( ) <EOL> except KeyError : <EOL> return None <EOL> return json_object <EOL> def update ( self , id , data ) : <EOL> id = self . _validate_id_format ( id ) <EOL> new_data = self . _sanitize_for_update ( data ) <EOL> try : <EOL> obj = self . objects [ id ] <EOL> except KeyError : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> obj = self . objects . new ( id = id , ** ( self . _sanitize_for_update ( new_data ) ) ) <EOL> obj . save ( ) <EOL> return obj . to_hash ( ) <EOL> class InMemoryAbstraction ( DbAbstraction ) : <EOL> def __init__ ( self , api , model , name , dictionary ) : <EOL> self . dictionary = dictionary <EOL> self . model = model <EOL> super ( InMemoryAbstraction , self ) . __init__ ( api , model , name ) <EOL> def get_columns ( self ) : <EOL> cols = [ ] <EOL> for attr in dir ( self . model ) : <EOL> if isinstance ( getattr ( self . model , attr ) , inmemory . Column ) : <EOL> cols . append ( attr ) <EOL> if hasattr ( self . model , '<STR_LIT>' ) : <EOL> cols += self . model . _synthesized_fields <EOL> return cols <EOL> def get_all ( self ) : <EOL> return self . dictionary . values ( ) <EOL> def get_schema ( self ) : <EOL> fields = { } <EOL> for attr in dir ( self . model ) : <EOL> col = getattr ( self . model , attr ) <EOL> if isinstance ( getattr ( self . model , attr ) , inmemory . Column ) : <EOL> fields [ attr ] = col . schema <EOL> if hasattr ( self . model , '<STR_LIT>' ) : <EOL> for syn in self . model . _synthesized_fields : <EOL> fields [ syn ] = { '<STR_LIT:type>' : '<STR_LIT>' , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : False , <EOL> '<STR_LIT>' : True , <EOL> '<STR_LIT:primary_key>' : False } <EOL> return fields <EOL> def create ( self , data ) : <EOL> new_data = self . _sanitize_for_create ( data ) <EOL> try : <EOL> new_thing = self . model ( ** new_data ) <EOL> except TypeError : <EOL> raise exceptions . CreateError ( '<STR_LIT>' ) <EOL> retval = new_thing . jsonify ( api = self . api ) <EOL> retval [ '<STR_LIT:id>' ] = self . _get_new_id ( ) <EOL> self . dictionary [ retval [ '<STR_LIT:id>' ] ] = retval <EOL> return retval <EOL> def delete ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> if not id in self . dictionary : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> self . dictionary . pop ( id ) <EOL> return True <EOL> def get ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> if id in self . dictionary : <EOL> return self . dictionary [ id ] <EOL> return None <EOL> def update ( self , id , data ) : <EOL> id = self . _validate_id_format ( id ) <EOL> new_data = self . _sanitize_for_update ( data ) <EOL> self . dictionary [ id ] . update ( new_data ) <EOL> return self . dictionary [ id ] <EOL> def _get_new_id ( self ) : <EOL> if len ( self . dictionary ) == <NUM_LIT:0> : <EOL> return <NUM_LIT:1> <EOL> return max ( self . dictionary . keys ( ) ) + <NUM_LIT:1> <EOL> class CachedAbstraction ( DbAbstraction ) : <EOL> def __init__ ( self , api , model , name , base_abstraction ) : <EOL> self . cache = None <EOL> self . base = base_abstraction <EOL> super ( CachedAbstraction , self ) . __init__ ( api , model , name ) <EOL> def destroy_cache ( self ) : <EOL> self . base . destroy_cache ( ) <EOL> self . cache = None <EOL> def get_columns ( self ) : <EOL> return self . base . get_columns ( ) <EOL> def get_all ( self ) : <EOL> if self . cache is None : <EOL> self . cache = { } <EOL> for obj in self . base . get_all ( ) : <EOL> self . cache [ int ( obj [ '<STR_LIT:id>' ] ) ] = obj <EOL> return self . cache . values ( ) <EOL> def get_schema ( self ) : <EOL> return self . base . get_schema ( ) <EOL> def create ( self , data ) : <EOL> result = self . base . create ( data ) <EOL> self . api . destroy_cache ( ) <EOL> return result <EOL> def delete ( self , data ) : <EOL> result = self . base . delete ( data ) <EOL> self . api . destroy_cache ( ) <EOL> return result <EOL> def get ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> if self . cache is None : <EOL> return self . base . get ( id ) <EOL> else : <EOL> if not id in self . cache : <EOL> raise exceptions . IdNotFound ( <EOL> message = '<STR_LIT>' % ( self . model , int ( id ) ) ) <EOL> else : <EOL> return self . cache [ int ( id ) ] <EOL> def update ( self , id , data ) : <EOL> id = self . _validate_id_format ( id ) <EOL> result = self . base . update ( id , data ) <EOL> self . api . destroy_cache ( ) <EOL> return result <EOL> class EphemeralAbstraction ( DbAbstraction ) : <EOL> def __init__ ( self , api , model , name , base_abstraction ) : <EOL> self . del_obj = [ ] <EOL> self . new_obj = { } <EOL> self . upd_obj = { } <EOL> self . current_max = <NUM_LIT> <EOL> self . base = base_abstraction <EOL> super ( EphemeralAbstraction , self ) . __init__ ( api , model , name ) <EOL> def transactions ( self ) : <EOL> result = { } <EOL> if len ( self . del_obj ) > <NUM_LIT:0> : <EOL> result [ '<STR_LIT>' ] = self . del_obj <EOL> if len ( self . new_obj ) > <NUM_LIT:0> : <EOL> result [ '<STR_LIT>' ] = self . new_obj <EOL> if len ( self . upd_obj ) > <NUM_LIT:0> : <EOL> result [ '<STR_LIT>' ] = self . upd_obj <EOL> if result == { } : <EOL> return None <EOL> return result <EOL> def _update_object ( self , underlying ) : <EOL> if underlying [ '<STR_LIT:id>' ] in self . del_obj : <EOL> return None <EOL> if not underlying [ '<STR_LIT:id>' ] in self . upd_obj : <EOL> return underlying <EOL> updated_object = copy . deepcopy ( underlying ) <EOL> updated_object . update ( self . upd_obj [ underlying [ '<STR_LIT:id>' ] ] ) <EOL> return updated_object <EOL> def get_columns ( self ) : <EOL> return self . base . get_columns ( ) <EOL> def get_all ( self ) : <EOL> result = [ ] <EOL> for obj in self . base . get_all ( ) : <EOL> new_obj = self . _update_object ( obj ) <EOL> if new_obj is not None : <EOL> r = self . model ( ** ( self . _sanitize_for_create ( new_obj ) ) ) <EOL> r . id = new_obj [ '<STR_LIT:id>' ] <EOL> new_obj = r . jsonify ( api = self . api ) <EOL> result . append ( new_obj ) <EOL> for id , obj in self . new_obj . items ( ) : <EOL> new_obj = self . _update_object ( obj ) <EOL> if new_obj is not None : <EOL> result . append ( new_obj ) <EOL> return result <EOL> def get_schema ( self ) : <EOL> return self . base . get_schema ( ) <EOL> def create ( self , data ) : <EOL> new_data = self . _sanitize_for_create ( data ) <EOL> if self . name == '<STR_LIT>' : <EOL> existing = self . api . _model_query ( <EOL> '<STR_LIT>' , '<STR_LIT>' % ( <EOL> int ( new_data [ '<STR_LIT>' ] ) , new_data [ '<STR_LIT:key>' ] ) ) <EOL> if len ( existing ) != <NUM_LIT:0> : <EOL> return self . update ( existing [ <NUM_LIT:0> ] [ '<STR_LIT:id>' ] , data ) <EOL> new_data [ '<STR_LIT:id>' ] = self . _get_new_id ( ) <EOL> self . new_obj [ new_data [ '<STR_LIT:id>' ] ] = new_data <EOL> return new_data <EOL> def delete ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> if id in self . del_obj : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> obj = self . base . get ( id ) <EOL> if obj is None : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> self . del_obj . append ( id ) <EOL> return True <EOL> def get ( self , id ) : <EOL> id = self . _validate_id_format ( id ) <EOL> new_obj = None <EOL> obj = self . base . get ( id ) <EOL> if obj is not None : <EOL> new_obj = self . _update_object ( obj ) <EOL> else : <EOL> if id in self . new_obj : <EOL> new_obj = self . new_obj [ id ] <EOL> if new_obj is None : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % id ) <EOL> r = self . model ( ** ( self . _sanitize_for_create ( new_obj ) ) ) <EOL> r . id = id <EOL> result = r . jsonify ( api = self . api ) <EOL> return result <EOL> def update ( self , id , data ) : <EOL> id = self . _validate_id_format ( id ) <EOL> new_data = self . _sanitize_for_update ( data ) <EOL> if id in self . del_obj : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % <EOL> ( self . model , id ) ) <EOL> obj = None <EOL> try : <EOL> obj = self . base . get ( id ) <EOL> except exceptions . IdNotFound : <EOL> if id in self . new_obj : <EOL> obj = self . new_obj [ id ] <EOL> if not obj : <EOL> raise exceptions . IdNotFound ( message = '<STR_LIT>' % <EOL> ( self . model , id ) ) <EOL> existing_obj = self . _update_object ( obj ) <EOL> if not id in self . upd_obj : <EOL> self . upd_obj [ id ] = { } <EOL> for field in new_data : <EOL> if existing_obj [ field ] != new_data [ field ] : <EOL> self . upd_obj [ id ] [ field ] = new_data [ field ] <EOL> new_obj = self . _update_object ( existing_obj ) <EOL> return new_obj <EOL> def _get_new_id ( self ) : <EOL> self . current_max += <NUM_LIT:1> <EOL> return self . current_max </s>
