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 create_sample_foos(user, friend):
    bar = Bar(key='bar')
    with reversion.create_revision():
        bar.save()
    bar_barr = Bar(key='bar_barr')
    with reversion.create_revision():
        bar_barr.save()

    foo = Foo(key='foo',
              name="Foo",
              user=user,
              data=dict(example=2.2, friend=R.pick(['id'], friend)),
              geojson=geojson,
              geo_collection=ewkt_from_feature_collection(geojson))
    with reversion.create_revision():
        foo.save()
    foo.bars.add(bar)
    foo.bars.add(bar_barr)
    boo = Foo(key='boo',
              name="Boo",
              user=user,
              data=dict(example=2.2, friend=R.pick(['id'], friend)),
              geojson=geojson,
              geo_collection=ewkt_from_feature_collection(geojson))
    with reversion.create_revision():
        boo.save()
    boo.bars.add(bar)
    return [foo, boo]
示例#3
0
 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_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
示例#5
0
 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 pick_selections(selections, data):
    """
        Pick the selections from the current data
    :param {[Sting]} selections: The field names to that are in the query
    :param {dict} data: Data to pick from
    :return: {DataTuple} data with limited to selections
    """
    dct = R.pick(selections, data)
    return namedtuple('DataTuple', R.keys(dct))(*R.values(dct))
示例#7
0
 def test_query_with_project_reference(self):
     quiz_model_query(
         self.client,
         graphql_query_locations,
         'locations',
         dict(
             name='Grand Place',
             projects=[R.pick(['id'], self.projects[0])]
         )
     )
示例#8
0
            def fetch_and_merge(modified_user_state_data, props):
                existing = UserState.objects.filter(**props)
                # If the user doesn't have a user state yet
                if not R.length(existing):
                    return modified_user_state_data

                return merge_data_fields_on_update(
                    ['data'],
                    R.head(existing),
                    # Merge existing's id in case it wasn't in user_state_data
                    R.merge(modified_user_state_data, R.pick(['id'], existing))
                )
示例#9
0
    def execute_graphql_request(self, *args, **kwargs):
        result = super().execute_graphql_request(*args, **kwargs)
        if result.errors:
            log.error(
                json.dumps(R.pick(['operationName', 'variables'], args[1]),
                           indent=4))
            for error in result.errors:
                if hasattr(error, 'source'):
                    log.error(error.source.body)
                # NO way to get stack trace of the original error grrrr
                log.exception(error)

        return result
    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
示例#11
0
    def test_query(self):
        user_results = graphql_query_users(self.client)
        format_error(R.prop('errors', user_results)[0])
        assert not R.prop('errors', user_results), R.dump_json(R.map(lambda e: format_error(e), R.prop('errors', user_results)))
        assert 2 == R.length(R.map(R.omit_deep(omit_props), R.item_path(['data', 'users'], user_results)))

        # Query using for foos based on the related User
        foo_results = graphql_query_foos(
            self.client,
            variables=dict(
                user=R.pick(['id'], self.lion.__dict__),
                # Test filters
                name_contains='oo',
                name_contains_not='jaberwaki'
            )
        )
        assert not R.prop('errors', foo_results), R.dump_json(R.map(lambda e: format_error(e), R.prop('errors', foo_results)))
        assert 1 == R.length(R.map(R.omit_deep(omit_props), R.item_path(['data', 'foos'], foo_results)))
        # Make sure the Django instance in the json blob was resolved
        assert self.cat.id == R.item_path(['data', 'foos', 0, 'data', 'friend', 'id'], foo_results)
def resolver_for_feature_collection(resource, context, **kwargs):
    """
        Like resolver but takes care of converting the geos value stored in the field to a dict that
        has the values we want to resolve, namely type and features.
    :param {string} resource: The instance whose json field data is being resolved
    :param {ResolveInfo} context: Graphene context which contains the fields queried in field_asts
    :return: {DataTuple} Standard resolver return value
    """

    # Take the camelized keys. We don't store data fields slugified. We leave them camelized
    selections = R.map(lambda sel: sel.name.value, context.field_asts[0].selection_set.selections)
    # Recover the json by parsing the string provided by GeometryCollection and mapping the geometries property to features
    json = R.compose(
        # Map the value GeometryCollection to FeatureCollection for the type property
        R.map_with_obj(lambda k, v: R.if_else(
            R.equals('type'),
            R.always('FeatureCollection'),
            R.always(v)
        )(k)),
        # Map geometries to features: [{type: Feature, geometry: geometry}]
        lambda dct: R.merge(
            # Remove geometries
            R.omit(['geometries'], dct),
            # Add features containing the geometries
            dict(features=R.map(
                lambda geometry: dict(type='Feature', geometry=geometry),
                R.prop_or([], 'geometries', dct))
            )
        ),
    )(ast.literal_eval(R.prop(context.field_name, resource).json))
    # Identify the keys that are actually in resource[json_field_name]
    all_selections = R.filter(
        lambda key: key in json,
        selections
    )
    # Pick out the values that we want
    result = R.pick(all_selections, json)

    # Return in the standard Graphene DataTuple
    return namedtuple('DataTuple', R.keys(result))(*R.values(result))
 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 handle_can_mutate_related(model, related_model_scope_config, data,
                              validated_scope_objs_instances_and_ids):
    """
        Mutates the given related models of an instance if permitted
        See rescape-region's UserState for a working usage
    :param model: The related model
    :param related_model_scope_config: Configuration of the related model relative to the referencing instance
    :param data: The data containing thphee related models dicts to possibly mutate with
    :param validated_scope_objs_instances_and_ids: Config of the related objects that have been validated as
    existing in the database for objects not being created
    :return: Possibly mutates instances, returns data with newly created ids set
    """
    def make_fields_unique_if_needed(scope_obj):
        # If a field needs to be unique, like a key, call it's unique_with method
        return R.map_with_obj(
            lambda key, value: R.item_str_path_or(
                R.identity, f'field_config.{key}.unique_with',
                related_model_scope_config)(scope_obj), scope_obj)

    def convert_foreign_key_to_id(scope_obj):
        # Find ForeignKey attributes and map the class field name to the foreign key id field
        # E.g. region to region_id, user to user_id, etc
        converters = R.compose(
            R.from_pairs, R.map(lambda field: [field.name, field.attname]),
            R.filter(lambda field: R.isinstance(ForeignKey, field)))(
                model._meta.fields)
        # Convert scopo_obj[related_field] = {id: x} to scope_obj[related_field_id] = x
        return R.from_pairs(
            R.map_with_obj_to_values(
                lambda key, value: [converters[key],
                                    R.prop('id', value)]
                if R.has(key, converters) else [key, value], scope_obj))

    def omit_to_many(scope_obj):
        return R.omit(R.map(R.prop('attname'), model._meta.many_to_many),
                      scope_obj)

    # This indicates that scope_objs were submitted that didn't have ids
    # This is allowed if those scope_objs can be created/updated when the userState is mutated
    if R.prop_or(False, 'can_mutate_related', related_model_scope_config):
        for scope_obj_key_value in validated_scope_objs_instances_and_ids[
                'scope_objs']:

            scope_obj = scope_obj_key_value['value']
            scope_obj_path = scope_obj_key_value['key']
            if R.length(R.keys(R.omit(['id'], scope_obj))):
                modified_scope_obj = R.compose(
                    convert_foreign_key_to_id, omit_to_many,
                    make_fields_unique_if_needed)(scope_obj)
                if R.prop_or(False, 'id', scope_obj):
                    # Update, we don't need the result since it's already in user_state.data
                    instance, created = model.objects.update_or_create(
                        defaults=R.omit(['id'], modified_scope_obj),
                        **R.pick(['id'], scope_obj))
                else:
                    # Create
                    instance = model(**modified_scope_obj)
                    instance.save()
                    # We need to replace the object
                    # passed in with an object containing the id of the instance
                    data = R.fake_lens_path_set(scope_obj_path.split('.'),
                                                R.pick(['id'], instance), data)

                for to_many in model._meta.many_to_many:
                    if to_many.attname in R.keys(scope_obj):
                        # Set existing related values to the created/updated instances
                        getattr(instance, to_many.attname).set(
                            R.map(R.prop('id'), scope_obj[to_many.attname]))
    return data
示例#15
0

class ResourceType(DjangoObjectType, DjangoObjectTypeRevisionedMixin):
    id = graphene.Int(source='pk')

    class Meta:
        model = Resource


raw_resource_fields = merge_with_django_properties(
    ResourceType,
    dict(
        id=dict(create=DENY, update=REQUIRE),
        key=dict(create=REQUIRE,
                 unique_with=increment_prop_until_unique(
                     Resource, None, 'key', R.pick(['deleted']))),
        name=dict(create=REQUIRE),
        # This refers to the Resource, which is a representation of all the json fields of Resource.data
        data=dict(graphene_type=ResourceDataType,
                  fields=resource_data_fields,
                  default=lambda: dict()),
        # This is a Foreign Key. Graphene generates these relationships for us, but we need it here to
        # support our Mutation subclasses and query_argument generation
        # For simplicity we limit fields to id. Mutations can only us id, and a query doesn't need other
        # details of the resource--it can query separately for that
        region=dict(graphene_type=RegionType,
                    fields=merge_with_django_properties(
                        RegionType, dict(id=dict(create=REQUIRE)))),
        **reversion_and_safe_delete_types))

# Modify data field to use the resolver.
    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
示例#17
0
from rescape_graphene.graphql_helpers.schema_helpers import process_filter_kwargs, delete_if_marked_for_delete, \
    update_or_create_with_revision, top_level_allowed_filter_arguments, allowed_filter_arguments
from rescape_graphene.schema_models.django_object_type_revisioned_mixin import reversion_and_safe_delete_types, \
    DjangoObjectTypeRevisionedMixin
from rescape_graphene.schema_models.geojson.types.feature_collection import feature_collection_data_type_fields
from rescape_python_helpers import ramda as R

from rescape_region.model_helpers import get_region_model
from rescape_region.models.region import Region
from .region_data_schema import RegionDataType, region_data_fields

raw_region_fields = dict(
    id=dict(create=DENY, update=REQUIRE),
    key=dict(create=REQUIRE,
             unique_with=increment_prop_until_unique(Region, None, 'key',
                                                     R.pick(['deleted']))),
    name=dict(create=REQUIRE),
    # This refers to the RegionDataType, which is a representation of all the json fields of Region.data
    data=dict(graphene_type=RegionDataType,
              fields=region_data_fields,
              default=lambda: dict()),
    # This is the OSM geojson
    geojson=dict(graphene_type=FeatureCollectionDataType,
                 fields=feature_collection_data_type_fields),
    **reversion_and_safe_delete_types)


class RegionType(DjangoObjectType, DjangoObjectTypeRevisionedMixin):
    id = graphene.Int(source='pk')

    class Meta: