Ejemplo n.º 1
0
def dump_graphql_data_object(dct):
    """
        Stringify a dict to a graphql input parameter key values in the form
        Also camelizes keys if the are slugs
        {"key1": "string value1", "key2": "number2", ...}
    :param dct:
    :return:
    """

    if dct == None:
        return 'null'
    elif isinstance(dct, dict):
        return '{%s}' % R.join(
            ', ',
            R.map(
                lambda key_value: R.join(
                    ': ',
                    [
                        camelize(quote(key_value[0]), False),
                        dump_graphql_data_object(key_value[1])
                    ]
                ),
                dct.items()
            )
        )
    elif isinstance(dct, list):
        return f"[{R.join(', ', R.map(lambda item: dump_graphql_data_object(item), dct))}]"
    else:
        return quote(dct)
Ejemplo n.º 2
0
def log_request_body(info, response_or_error):
    body = info.context._body.decode('utf-8')
    try:
        json_body = json.loads(body)
        (
            logger.error
            if isinstance(response_or_error, ErrorType) else logger.debug
        )(f" User: {info.context.user} \n Action: {json_body['operationName']} \n Variables: {json_body['variables']} \n Body:  {json_body['query']}",
          )
        if hasattr(response_or_error, '_meta') and isinstance(
                response_or_error._meta, MutationOptions):
            # Just log top level types
            if isinstance(response_or_error, (Model)):
                mutation_response = json.dumps(R.omit(
                    ['_state'], response_or_error.__dict__),
                                               sort_keys=True,
                                               indent=1,
                                               cls=MyDjangoJSONEncoder)
                logger.debug(f'Mutation returned {mutation_response}')
            elif isinstance(response_or_error, (BaseType)):
                try:
                    mutation_response = json.dumps(
                        R.omit(['_state'], response_or_error.__dict__),
                        sort_keys=True,
                        indent=1,
                    )
                    logger.debug(f'Mutation returned {mutation_response}')
                except:
                    logger.debug(
                        f'Mutation returned {response_or_error.__class__}')
        else:
            if hasattr(response_or_error, 'objects'):
                count = response_or_error.objects.count()
                # Log up to 100 ids, don't log if it's a larger set because it might be a paging query
                ids = R.join(' ', [
                    '', 'having ids:',
                    R.join(
                        ', ',
                        R.map(R.prop("id"),
                              response_or_error.objects.values('id')))
                ]) if count < 100 else ""
                logger.debug(
                    f'Paginated Query Page {response_or_error.page} of page size {response_or_error.page_size} out of total pages {response_or_error.pages} returned {count} results{ids}'
                )
            elif hasattr(response_or_error, 'count'):
                count = response_or_error.count()
                # Log up to 100 ids, don't log if it's a larger set because it might be a paging query
                ids = R.join(' ', [
                    '', 'having ids:',
                    R.join(', ',
                           R.map(R.prop("id"), response_or_error.values('id')))
                ]) if count < 100 else ""
                logger.debug(f'Query returned {count} results{ids}')
            else:
                id = R.prop('id', response_or_error)
                logger.debug(f'Query returned single result {id}')

    except Exception as e:
        logging.error(body)
Ejemplo n.º 3
0
def dump_graphql_keys(dct):
    """
        Convert a dict to a graphql input parameter keys in the form
        Also camelizes keys if the are slugs and handles complex types. If a value has read=IGNORE it is omitted
        key1
        key2
        key3
        key4 {
            subkey1
            ...
        }
        ...
    :param dct: keyed by field
    :return:
    """
    from rescape_graphene.graphql_helpers.schema_helpers import IGNORE, DENY
    return R.join('\n', R.values(R.map_with_obj(
        dump_graphene_type,
        R.filter_dict(
            lambda key_value: not R.compose(
                lambda v: R.contains(v, [IGNORE, DENY]),
                lambda v: R.prop_or(None, 'read', v)
            )(key_value[1]),
            dct
        )
    )))
def validate_and_mutate_scope_instances(scope_instances_config, data):
    """
        Inspect the data and find all scope instances within data
        For UserState, for instance, this includes userRegions[*].region, userProject[*].project and within
        userRegions and userProjects userSearch.userSearchLocations[*].search_location and whatever the implementing
        libraries define in addition
    :param scope_instances_config: See user_state_schema.user_state_scope_instances_config for an example
    :param data: The instance data field containing the scope instances
    :return: The updated data with scope instances possibly created/updated if allowed. If creates occur
    then the scope instance will now have an id. Otherwise no changes are visible
    """

    validated_scope_objs_instances_and_ids_sets = R.map(
        lambda scope_instance_config: find_scope_instances(
            scope_instance_config, data), scope_instances_config)

    # Some scope instances can be created or modified when embedded in the data. This helps
    # make mutation of the instance, such as UserState,
    # a one step process, so that new Projects, SearchLocations, etc. can
    # be created without having to call mutation for them separately ahead of times, which would create
    # a series of mutations that weren't failure-protected as a single transaction
    for i, validated_scope_objs_instances_and_ids in enumerate(
            validated_scope_objs_instances_and_ids_sets):
        scope = R.merge(
            scope_instances_config[i],
            dict(model=scope_instances_config[i]['model'].__name__))

        # If any scope instances with an id specified in new_data don't exist, throw an error
        if R.length(validated_scope_objs_instances_and_ids['scope_ids']
                    ) != R.length(
                        validated_scope_objs_instances_and_ids['instances']):
            ids = R.join(', ',
                         validated_scope_objs_instances_and_ids['scope_ids'])
            instances_string = R.join(
                ', ',
                R.map(lambda instance: str(instance),
                      validated_scope_objs_instances_and_ids['instances']))
            raise Exception(
                f"For scope {dumps(scope)} Some scope ids among ids:[{ids}] being saved in user state do not exist. Found the following instances in the database: {instances_string or 'None'}. UserState.data is {dumps(data)}"
            )

        # Create/Update any scope instances that permit it
        model = scope_instances_config[i]['model']
        data = handle_can_mutate_related(
            model, scope, data, validated_scope_objs_instances_and_ids)
    return data
Ejemplo n.º 5
0
def get_user_search_data_schema():
    """
    Uses the same technique as get_user_model() to get the current location model from settings
    :return:
    """
    try:
        modules = settings.USER_SEARCH_DATA_SCHEMA_CONFIG.split('.')
        return getattr(importlib.import_module(R.join('.', R.init(modules))),
                       R.last(modules))
    except ValueError:
        raise ImproperlyConfigured(
            '''settings.USER_SEARCH_DATA_SCHEMA_CONFIG must point to the user_search schema config containing
    {
        graphene_class=UserSearchType,
        graphene_fields=user_search_fields,
    }
''')
    except LookupError:
        raise ImproperlyConfigured(
            "settings.USER_SEARCH_DATA_SCHEMA_CONFIG refers to model '%s' that has not been installed"
            % settings.USER_SEARCH_DATA_SCHEMA_CONFIG)
Ejemplo n.º 6
0
def get_location_for_project_schema():
    """

    Like get_location_schema() but without reverse relationships that cause circular dependencies
    :return:
    """
    try:
        modules = settings.LOCATION_SCHEMA_FOR_PROJECT_CONFIG.split('.')
        return getattr(importlib.import_module(R.join('.', R.init(modules))),
                       R.last(modules))
    except ValueError:
        raise ImproperlyConfigured(
            '''settings.LOCATION_SCHEMA_FOR_PROJECT_CONFIG must point to the location schema config containing
    {
        model_class=Location,
        graphene_class=LocationType,
        graphene_fields=location_fields,
    }
''')
    except LookupError:
        raise ImproperlyConfigured(
            "settings.LOCATION_SCHEMA_CONFIG refers to model '%s' that has not been installed"
            % settings.LOCATION_SCHEMA_CONFIG)