def setUp(self): delete_sample_user_states() self.user_state_schema = create_user_state_config( default_class_config()) self.user_states = create_sample_user_states( UserState, get_region_model(), get_project_model(), get_location_schema()['model_class'], get_search_location_schema()['model_class']) # Gather all unique sample users self.users = list( set(R.map(lambda user_state: user_state.user, self.user_states))) self.client = client_for_testing(schema(), self.users[0]) # Gather all unique sample regions self.regions = R.compose( # Forth Resolve persisted Regions R.map(lambda id: get_region_model().objects.get(id=id)), # Third make ids unique lambda ids: list(set(ids)), # Second map each to the region id R.map(R.item_str_path('region.id')), # First flat map the user regions of all user_states R.chain(lambda user_state: R.item_str_path( 'data.userRegions', user_state.__dict__)))(self.user_states) # Gather all unique sample projects self.projects = R.compose( # Forth Resolve persisted Projects R.map(lambda id: get_project_model().objects.get(id=id)), # Third make ids unique lambda ids: list(set(ids)), # Second map each to the project id R.map(R.item_str_path('project.id')), # First flat map the user regions of all user_states R.chain(lambda user_state: R.item_str_path( 'data.userProjects', user_state.__dict__)))(self.user_states) self.locations = create_local_sample_locations( get_location_schema()['model_class']) def extract_search_location_ids(user_regions): return R.map( R.item_str_path('searchLocation.id'), R.chain(R.item_str_path('userSearch.userSearchLocations'), user_regions)) # Gather all unique searches locations from userRegions. # user searches could also be in userProjects, but we'll ignore that self.search_locations = R.compose( # Forth Resolve persisted UserSearches lambda ids: R.map( lambda id: get_search_location_schema()['model_class'].objects. get(id=id), ids), # Third make ids unique lambda ids: list(set(ids)), # Chain to a flat list of user search location ids lambda user_regions: extract_search_location_ids(user_regions), # First flat map the user regions of all user_states R.chain(lambda user_state: R.item_str_path( 'data.userRegions', user_state.__dict__)))(self.user_states) # Can be set by inheritors self.additional_user_scope_data = {}
def test_create(self): values = dict(username="******", firstName='T', lastName='Rex', password=make_password("rrrrhhh", salt='not_random')) result = graphql_update_or_create_user(self.client, values) assert_no_errors(result) # look at the users added and omit the non-determinant dateJoined assert R.item_str_path('data.createUser.user', result) versions = Version.objects.get_for_object(get_user_model().objects.get( id=R.item_str_path('data.createUser.user.id', result) )) assert len(versions) == 1
def test_create(self): result, new_result = quiz_model_mutation_create( self.client, graphql_update_or_create_project, 'createProject.project', dict( name='Carre', key='carre', geojson={ 'type': 'FeatureCollection', 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] }, data=dict(), locations=R.map(R.compose(R.pick(['id']), lambda l: l.__dict__), self.locations), user=R.pick(['id'], R.head(self.users).__dict__), ), dict(key=r'carre.+')) versions = Version.objects.get_for_object(get_project_model().objects.get( id=R.item_str_path('data.createProject.project.id', result) )) assert len(versions) == 1
def test_create(self): result, new_result = quiz_model_mutation_create( self.client, graphql_update_or_create_location, 'createLocation.location', dict( name='Grote Markt', key='groteMarkt', geojson={ 'type': 'FeatureCollection', 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] }, data=dict() ), # Second create should create a new record that matches this regex dict(key=r'groteMarkt.+') ) versions = Version.objects.get_for_object(Location.objects.get( id=R.item_str_path('data.createLocation.location.id', result) )) assert len(versions) == 1
def test_update(self): result, update_result = quiz_model_mutation_update( self.client, graphql_update_or_create_resource, 'createResource.resource', 'updateResource.resource', dict( key='candy', name='Candy', region=dict(id=self.region.id), data=R.merge( sample_settings, dict( material='Candy', rawData=[ 'Other Global Imports;Shipments, location generalized;51.309933, 3.055030;Source;22,469,843', 'Knauf (Danilith) BE;Waregemseweg 156-142 9790 Wortegem-Petegem, Belgium;50.864762, 3.479308;Conversion;657,245', "MPRO Bruxelles;Avenue du Port 67 1000 Bruxelles, Belgium;50.867486, 4.352543;Distribution;18,632", 'Residential Buildings (all typologies);Everywhere in Brussels;NA;Demand;3,882,735', 'Duplex House Typology;Everywhere in Brussels;NA;Demand;13,544', 'Apartment Building Typology;Everywhere in Brussels;NA;Demand;34,643', 'New West Gypsum Recycling;9130 Beveren, Sint-Jansweg 9 Haven 1602, Kallo, Belgium;51.270229, 4.261048;Reconversion;87,565', 'Residential Buildings (all typologies);Everywhere in Brussels;NA;Sink;120,000', 'RecyPark South;1190 Forest, Belgium;50.810799, 4.314789;Sink;3,130', 'RecyPark Nord;Rue du Rupel, 1000 Bruxelles, Belgium;50.880181, 4.377136;Sink;1,162' ]))), dict(key='popcorn', name='Popcorn')) versions = Version.objects.get_for_object( Resource.objects.get(id=R.item_str_path( 'data.updateResource.resource.id', update_result))) assert len(versions) == 2
def test_update(self): (result, update_result) = quiz_model_mutation_update( self.client, graphql_update_or_create_foo, 'createFoo.foo', 'updateFoo.foo', dict(name='Luxembourg', key='luxembourg', user=R.pick(['id'], self.admin), geojson=geojson, data=dict(example=1.1, friend=R.pick(['id'], self.user))), # Update the coords dict( geojson={ 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] })) versions = Version.objects.get_for_object( Foo.objects.get( id=R.item_str_path('data.updateFoo.foo.id', update_result))) assert len(versions) == 2
def test_update(self): # First add a new User margay = dict(username="******", first_name='Upa', last_name='Tree', password=make_password("merowgir", salt='not_random')) user = create_sample_user(margay) # Now assign regions and persist the UserState sample_user_state_data = dict( user=dict(id=user.id), data=form_sample_user_state_data( self.regions, self.projects, dict( userRegions=[ dict( # Assign the first region region=dict( key=R.prop('key', R.head(self.regions))), mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7))) ], userProjects=[ dict( # Assign the first prjoect project=dict( key=R.prop('key', R.head(self.projects))), mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7))) ]))) # Update the zoom of the first userRegion update_data = deepcopy(R.pick(['data'], sample_user_state_data)) R.item_str_path( 'mapbox.viewport', R.head(R.item_str_path('data.userRegions', (update_data))))['zoom'] = 15 result, update_result = quiz_model_mutation_update( self.client, R.prop('graphql_mutation', self.user_state_schema), 'createUserState.userState', 'updateUserState.userState', sample_user_state_data, update_data) versions = Version.objects.get_for_object( UserState.objects.get(id=R.item_str_path( 'data.updateUserState.userState.id', update_result))) assert len(versions) == 2
def test_update(self): values = dict(username="******", firstName='T', lastName='Rex', password=make_password("rrrrhhh", salt='not_random')) # Here is our create create_result = graphql_update_or_create_user(self.client, values) # Unfortunately Graphene returns the ID as a string, even when its an int id = R.prop('id', R.item_str_path('data.createUser.user', create_result)) # Here is our update result = graphql_update_or_create_user( self.client, dict(id=id, firstName='Al', lastName="Lissaurus") ) assert_no_errors(result) assert R.item_str_path('data.updateUser.user', result) versions = Version.objects.get_for_object(get_user_model().objects.get( id=R.item_str_path('data.updateUser.user.id', result) )) assert len(versions) == 2
def test_create(self): (result, new_result) = quiz_model_mutation_create( self.client, graphql_update_or_create_region, 'createRegion.region', dict( name='Luxembourg', key='luxembourg', geojson={ 'type': 'FeatureCollection', 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] }, data=dict( locations=dict( params=dict( city='Luxembourg City' ) ), # Optional default mapbox settings for the region mapbox=dict( viewport=dict( # Extent can replace latitude, longitude, zoom for complex cases extent=dict( type='FeatureCollection', features=[dict( type='Feature', geometry=dict( type='Polygon', coordinates=[ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] ) )] ), ) ) ) ), dict(key=r'luxembourg.+') ) versions = Version.objects.get_for_object(Region.objects.get( id=R.item_str_path('data.createRegion.region.id', result) )) assert len(versions) == 1
def test_create(self): (result, new_result) = quiz_model_mutation_create( self.client, graphql_update_or_create_foo, 'createFoo.foo', dict(name='Luxembourg', key='luxembourg', user=R.pick(['id'], self.admin), geojson=geojson, data=dict(example=1.1, friend=R.pick(['id'], self.user))), dict(key=r'luxembourg.+')) versions = Version.objects.get_for_object( Foo.objects.get( id=R.item_str_path('data.createFoo.foo.id', result))) assert len(versions) == 1
def quiz_model_mutation_update(client, graphql_update_or_create_function, create_path, update_path, values, update_values): """ Tests an update mutation for a model by calling a create with the given values then an update with the given update_values (plus the create id) :param client: The Apollo Client :param graphql_update_or_create_function: The update or create mutation function for the model. Expects client and input values :param create_path: The path to the result of the create in the data object (e.g. createRegion.region) :param update_path: The path to the result of the update in the data object (e.g. updateRegion.region) :param values: The input values to use for the create :param update_values: The input values to use for the update. This can be as little as one key value :return: """ result = graphql_update_or_create_function(client, values=values) assert not R.has('errors', result), R.dump_json( R.map(lambda e: format_error(e), R.prop('errors', result))) # Extract the result and map the graphql keys to match the python keys created = R.compose( lambda r: R.map_keys(lambda key: underscore(key), r), lambda r: R.item_str_path(f'data.{create_path}', r))(result) # look at the users added and omit the non-determinant dateJoined assert values == pick_deep(created, values) # Update with the id and optionally key if there is one + update_values update_result = graphql_update_or_create_function( client, R.merge_all([ dict(id=created['id']), dict(key=created['key']) if R.prop_or(False, 'key', created) else {}, update_values ])) assert not R.has('errors', update_result), R.dump_json( R.map(lambda e: format_error(e), R.prop('errors', update_result))) updated = R.item_str_path(f'data.{update_path}', update_result) assert created['id'] == updated['id'] assert update_values == pick_deep(update_values, updated) return result, update_result
def test_authenticate(self): values = dict(username=self.user.username, password='******') result = graphql_token_auth_mutation(self.client, values) assert_no_errors(result) auth_token = R.item_str_path('data.tokenAuth.token', result) assert auth_token verify_result = graphql_verify_token_mutation(self.client, dict(token=auth_token)) assert_no_errors(verify_result) refresh_result = graphql_refresh_token_mutation(self.client, dict(token=auth_token)) assert_no_errors(refresh_result) delete_token_cookie_result = graphql_delete_token_cookie_mutation(self.client, {}) assert_no_errors(delete_token_cookie_result) delete_token_cookie_result = graphql_delete_token_cookie_mutation(self.client, {}) assert_no_errors(delete_token_cookie_result) delete_refresh_token_cookie_result = graphql_delete_refresh_token_cookie_mutation(self.client, {}) assert_no_errors(delete_refresh_token_cookie_result)
def test_create(self): result, _ = quiz_model_mutation_create( self.client, graphql_update_or_create_settings, 'createSettings.settings', dict(key='mars', data=dict(domain='localhost', api=dict(protocol='http', host='localhost', port='8008', path='/graphql/'), overpass=dict(cellSize=100, sleepBetweenCalls=1000), mapbox=dict(viewport={}, )))) versions = Version.objects.get_for_object( Settings.objects.get( id=R.item_str_path('data.createSettings.settings.id', result))) assert len(versions) == 1
def test_update(self): result, update_result = quiz_model_mutation_update( self.client, graphql_update_or_create_project, 'createProject.project', 'updateProject.project', dict( name='Carre', key='carre', geojson={ 'type': 'FeatureCollection', 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.4426671413, 5.67405195478], [50.1280516628, 5.67405195478], [50.1280516628, 6.24275109216], [49.4426671413, 6.24275109216], [49.4426671413, 5.67405195478]]] } }] }, data=dict(), locations=R.map(R.compose(R.pick(['id']), lambda l: l.__dict__), self.locations), user=R.pick(['id'], R.head(self.users).__dict__), ), # Update the coords and limit to one location dict( geojson={ 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] }, locations=R.map(R.compose(R.pick(['id']), lambda l: l.__dict__), [R.head(self.locations)]) ) ) versions = Version.objects.get_for_object(get_project_model().objects.get( id=R.item_str_path('data.updateProject.project.id', update_result) )) assert len(versions) == 2
def quiz_model_mutation_create(client, graphql_update_or_create_function, result_path, values, second_create_results=None, second_create_does_update=False): """ Tests a create mutation for a model :param client: The Apollo Client :param graphql_update_or_create_function: The update or create mutation function for the model. Expects client and input values :param result_path: The path to the result of the create in the data object (e.g. createRegion.region) :param values: The input values to use for the create :param second_create_results: Object, tests a second create if specified. Use to make sure that create with the same values creates a new instance or updates, depending on what you expect it to do. The values of this should be regexes that match the created instance :param second_create_does_update: Default False. If True expects a second create with the same value to update rather than create a new instance :return: Tuple with two return values. The second is null if second_create_results is False """ result = graphql_update_or_create_function(client, values=values) result_path_partial = R.item_str_path(f'data.{result_path}') assert not R.has('errors', result), R.dump_json( R.map(lambda e: format_error(e), R.prop('errors', result))) # Get the created value, using underscore to make the camelcase keys match python keys created = R.map_keys(lambda key: underscore(key), result_path_partial(result)) # get all the keys in values that are in created. This should match values if created has everything we expect assert values == pick_deep(created, values) # Try creating with the same values again, unique constraints will apply to force a create or an update will occur if second_create_results: new_result = graphql_update_or_create_function(client, values) assert not R.has('errors', new_result), R.dump_json( R.map(lambda e: format_error(e), R.prop('errors', new_result))) created_too = result_path_partial(new_result) if second_create_does_update: assert created['id'] == created_too['id'] if not second_create_does_update: assert created['id'] != created_too['id'] for path, value in R.flatten_dct(second_create_results, '.').items(): assert re.match(value, R.item_str_path_or(None, path, created_too)) else: new_result = None return result, new_result
def test_update(self): result, update_result = quiz_model_mutation_update( self.client, graphql_update_or_create_location, 'createLocation.location', 'updateLocation.location', dict( name='Grote Markt', key='groteMarkt', geojson={ 'type': 'FeatureCollection', 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.4426671413, 5.67405195478], [50.1280516628, 5.67405195478], [50.1280516628, 6.24275109216], [49.4426671413, 6.24275109216], [49.4426671413, 5.67405195478]]] } }] }, data=dict() ), # Update the coords dict( geojson={ 'features': [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [[49.5294835476, 2.51357303225], [51.4750237087, 2.51357303225], [51.4750237087, 6.15665815596], [49.5294835476, 6.15665815596], [49.5294835476, 2.51357303225]]] } }] } ) ) versions = Version.objects.get_for_object(Location.objects.get( id=R.item_str_path('data.updateLocation.location.id', update_result) )) assert len(versions) == 2
def resolve_scope_instance(scope_key, user_scope_instance): # Replace key with id id = R.compose( # third get the id if it exists R.prop_or(None, 'id'), # second resolve the scope instance if it exists lambda k: R.prop_or(None, k, scope_instances_by_key), # first get the key R.item_str_path(f'{scope_key}.key'))(user_scope_instance) return { scope_key: R.compact_dict( dict( # Resolve the persisted Scope instance by key id=id ) if id else dict( # Otherwise pass everything so the server can create the instance # (Currently only supported for projects) user_scope_instance[scope_key])) }
def sample_user_state_with_search_locations_and_additional_scope_instances( user_scope_name, sample_user_state): return R.fake_lens_path_set( f'data.{user_scope_name}'.split('.'), R.map( lambda user_scope: R.compose( # Gives applications a chance to add the needed additional scope instances, # e.g. userDesignFeatures lambda user_scope: create_additional_scope_instance_properties(user_scope), lambda user_scope: R.merge( user_scope, dict(userSearch=dict(userSearchLocations=R.map( lambda i_search_location: dict( # Just return with the id since the full data is in the database searchLocation=R.pick(['id'], i_search_location[1]), # Set the first search_location to active activity=dict(isActive=i_search_location[0] == 0)), enumerate(search_locations))))))(user_scope), R.item_str_path(f'data.{user_scope_name}', sample_user_state)), sample_user_state)
def test_create(self): # First add a new User margay = dict(username="******", first_name='Upa', last_name='Tree', password=make_password("merowgir", salt='not_random')) user = create_sample_user(margay) # Now assign regions and persist the UserState sample_user_state_data = dict( user=dict(id=user.id), data=form_sample_user_state_data( self.regions, self.projects, dict( userGlobal=dict(mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7))), userRegions=[ dict( # Assign the first region region=dict( key=R.prop('key', R.head(self.regions))), mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7)), userSearch=dict(userSearchLocations=R.concat( R.map( lambda search_location: dict( searchLocation=R.pick(['id'], search_location), activity=dict(isActive=True)), self.search_locations), # Search locations can be created on the fly [ dict(searchLocation=dict( name="I am a new search"), activity=dict(isActive=True)) ])), **self.additional_user_scope_data) ], userProjects=[ dict( # Assign the first project project=dict( key=R.prop('key', R.head(self.projects))), mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7)), userSearch=dict(userSearchLocations=R.map( lambda search_location: dict( searchLocation=R.pick(['id'], search_location), activity=dict(isActive=True)), self.search_locations)), **self.additional_user_scope_data), dict( # Create a new project when creating the userProject project=dict(user=R.pick(['id'], user), region=R.pick(['id'], R.head(self.regions)), key='newProject', name='New Project', locations=R.map( R.pick(['id']), self.locations)), mapbox=dict(viewport=dict( latitude=50.5915, longitude=2.0165, zoom=7)), userSearch=dict(userSearchLocations=R.map( lambda search_location: dict( searchLocation=R.pick(['id'], search_location), activity=dict(isActive=True)), self.search_locations)), **self.additional_user_scope_data) ]))) result, _ = quiz_model_mutation_create( self.client, R.prop('graphql_mutation', self.user_state_schema), 'createUserState.userState', sample_user_state_data, # The second create should update, since we can only have one userState per user dict(), True) versions = Version.objects.get_for_object( UserState.objects.get(id=R.item_str_path( 'data.createUserState.userState.id', result))) assert len(versions) == 1
def extract_search_location_ids(user_regions): return R.map( R.item_str_path('searchLocation.id'), R.chain(R.item_str_path('userSearch.userSearchLocations'), user_regions))