async def test_listens_for_related_deletes(self): # the model connecting the two connection_model = self.model() setattr(connection_model, model_service_name(self.service1), 1) setattr(connection_model, model_service_name(self.service2), 1) connection_model.save() # make sure the connection model can be found assert self.model.get(self.service1_value == 1), ( "Test record could not be created before deleting." ) # the action data for a related delete action_type = conventions.get_crud_action( 'delete', self.service1, status='success' ) payload = dict(id=1) # instantiate an action handler to test handler = self.service.action_handler() # fire the action await handler.handle_action( action_type=action_type, payload=payload, props={}, notify=False ) # make sure the model can't be found self.assertRaises(Exception, self.model.get, self.service1_value == 1)
def test_connection_model(self): # the fields of the underlying service model model_fields = {field.name for field in self.service.model.fields()} # the target field names target_fields = { 'id', model_service_name(self.service1), model_service_name(self.service2) } # for each model managed by this service assert model_fields == target_fields, ( "Connection model did not have the correct fields" )
def setUp(self): # point the database to a in-memory sqlite database nautilus.database.init_db('sqlite:///test.db') # create a spy we can check for later self.spy = Mock() # save the names of the connected services to the suite self.service1 = 'TestService1' self.service2 = 'TestService2' class TestConnectionService(nautilus.ConnectionService): from_service = (self.service1,) to_service = (self.service2,) self.services = [self.service1, self.service2] self.service = TestConnectionService() self.model = self.service.model # create the test table self.model.create_table(True) # save the attribute we'll use to test against self.service1_value = getattr(self.model, model_service_name(self.service1))
def __init__(self, model=None, **kwargs): # avoid circular depdencies from ..api.util import create_model_schema # if we were given a model for the service if model: # use the given model self.model = model # if there is no model associated with this service if not self.model: # yell loudly raise ValueError("Please provide a model for the model service.") # pull the name of the service from kwargs if it was given name = kwargs.pop('name', None) or model_service_name(self.model) # create the service super().__init__( schema=create_model_schema(self.model), name=name, **kwargs ) # initialize the database self.init_db()
def __init__(self, model, additonal_action_handler = noop_handler, **kwargs): # save a reference to the model this service is managing self.model = model # the schema to add to the service schema = create_model_schema(model) # the action handler is a combination action_handler = combine_action_handlers( # of the given one additonal_action_handler, # and a crud handler CRUDHandler(model) ) # pull the name of the service from kwargs if it was given name = kwargs.pop('name', None) or model_service_name(model) # create the service super().__init__( schema=schema, action_handler=action_handler, name=name, **kwargs )
def summarize(self, **extra_fields): # start with the default summary try: return { **super().summarize(), 'connection': { 'from': { 'service': model_service_name(self.from_service[0]), }, 'to': { 'service': model_service_name(self.to_service[0]), } }, **extra_fields } except Exception as e: print(e)
def summarize(self, **extra_fields): # start with the default summary try: return { **super().summarize(), 'connection': { 'from': { 'service': model_service_name(self.from_service[0]), }, 'to': { 'service': model_service_name(self.to_service[0]), } }, **extra_fields } except Exception as e: print(e)
async def _verify_action_handler_create(self): action_type = conventions.get_crud_action('create', self.service.model) payload = { model_service_name(self.service1): 'foo', model_service_name(self.service2): 'bar' } # create an instance of the action handler handler = self.service.action_handler() # fire a create action await handler.handle_action( action_type=action_type, payload=payload, props={}, notify=False ) # the query to find a matching model matching_model = self.service1_value == 'foo' # make sure the created record was found and save the id self.model_id = self.model_id = self.model.get(matching_model).id
def test_model_service_name(self): # a model to test with class TestModel(nautilus.models.BaseModel): name = nautilus.models.fields.CharField() TestModel = MockModel() # generate a service from the model class TestService(nautilus.ModelService): model = TestModel # make sure we could generate a name for it assert isinstance(conventions.model_service_name(TestService), str), ( "Could not generate name for model service" )
def create_connection_model(service): """ Create an SQL Alchemy table that connects the provides services """ # the services connected services = service._services # the mixins / base for the model bases = (BaseModel,) # the fields of the derived attributes = {model_service_name(service): fields.CharField() for service in services} # create an instance of base model with the right attributes return type(BaseModel)(connection_service_name(service), bases, attributes)
async def _verify_action_handler_update(self): payload = {'id':self.model_id, model_service_name(self.service1): 'bars'} # create an instance of the action handler handler = self.service.action_handler() # call the service action handler await handler.handle_action( action_type=conventions.get_crud_action('update', self.model), props={}, payload=payload, notify=False ) # check that a model matches self.model.get(self.service1_value == 'bars')
def test_model_service_name(self): # a model to test with class TestModel(nautilus.models.BaseModel): name = nautilus.models.fields.CharField() TestModel = MockModel() # generate a service from the model class TestService(nautilus.ModelService): model = TestModel # make sure we could generate a name for it assert isinstance(conventions.model_service_name(TestService), str), ("Could not generate name for model service")
def test_can_create_connection_model(self): import nautilus # models to test Model1 = model_service_name('Model1') Model2 = model_service_name('Model2') class TestConnectionService(nautilus.ConnectionService): to_service = (Model1, ) from_service = (Model2, ) # create the connection model connection_model = nautilus.models.create_connection_model( TestConnectionService()) assert issubclass(connection_model, models.BaseModel), ( "Generated connection model is not an instance of BaseModel") # grab the name of the fields connect_fields = {field.name for field in connection_model.fields()} assert connect_fields == { Model1, Model2, 'id' }, ("Connection model did not have the correct fields.")
def test_model_service_name_accepts_numbers(self): # a model to test with class TestModel(nautilus.models.BaseModel): name = nautilus.models.fields.CharField() # generate a service from the model class TestService1(nautilus.ModelService): model = TestModel # figure out the conventional name for the service service_name = conventions.model_service_name(TestService1) # make sure we could generate a name for it assert (isinstance(service_name, str) and '1' in service_name), ( "Could not generate name for model service when it has a number.")
def test_can_create_connection_model(self): import nautilus # models to test Model1 = model_service_name('Model1') Model2 = model_service_name('Model2') class TestConnectionService(nautilus.ConnectionService): to_service = (Model1,) from_service = (Model2,) # create the connection model connection_model = nautilus.models.create_connection_model(TestConnectionService()) assert issubclass(connection_model, models.BaseModel), ( "Generated connection model is not an instance of BaseModel" ) # grab the name of the fields connect_fields = {field.name for field in connection_model.fields()} assert connect_fields == {Model1, Model2, 'id'}, ( "Connection model did not have the correct fields." )
def test_model_service_name_accepts_numbers(self): # a model to test with class TestModel(nautilus.models.BaseModel): name = nautilus.models.fields.CharField() # generate a service from the model class TestService1(nautilus.ModelService): model = TestModel # figure out the conventional name for the service service_name = conventions.model_service_name(TestService1) # make sure we could generate a name for it assert ( isinstance(service_name, str) and '1' in service_name ), ( "Could not generate name for model service when it has a number." )
async def action_handler(action_type, payload, notify=True, **kwds): """ an action handler to remove related entries in the connection db. """ # if the action designates a successful delete of the model if action_type == related_action_type: # the id of the deleted model related_id = payload['id'] # the query for matching fields matching_records = getattr(self.model, model_service_name(model)) == related_id ids = [model.id for model in self.model.filter(matching_records)] # find the matching records self.model.delete().where(matching_records).execute() # if we are supposed to notify if notify: # notify of the related delete await self.event_broker.send( action_type=get_crud_action('delete', self.model, status=success_status()), payload=ids )
def test_has_valid_schema(self): assert hasattr(self.service, 'schema') and self.service.schema, ( "Model Service did not have a schema." ) # the field to query field_name = service_conventions.model_service_name(self.service1) # the query to test the schema query = """ query { all_models { %s } } """ % field_name parsed_query = self.service.schema.execute(query) # make sure there are no errors assert parsed_query.errors == [], ( "Model service schema is invalid: " + str(parsed_query.errors) ) assert len(parsed_query.data) > 0, ( "Model could not be retrieved with schema." )
async def action_handler(action_type, payload, notify=True, **kwds): """ an action handler to remove related entries in the connection db. """ # if the action designates a successful delete of the model if action_type == related_action_type: # the id of the deleted model related_id = payload['id'] # the query for matching fields matching_records = getattr( self.model, model_service_name(model)) == related_id ids = [ model.id for model in self.model.filter(matching_records) ] # find the matching records self.model.delete().where(matching_records).execute() # if we are supposed to notify if notify: # notify of the related delete await self.event_broker.send(action_type=get_crud_action( 'delete', self.model, status=success_status()), payload=ids)
def __new__(cls, *args, **kwds): # make sure the service has the right name cls.name = model_service_name(cls.model) if cls.model else '' # bubble up return super().__new__(cls, *args)
def test_has_conventional_name(self): assert self.service_record.name == \ service_conventions.model_service_name(self.model), ( "Model service did not have the correct name." )