Example #1
0
    def inflate_results(results, resources_to_expand):
        """
        Inflate and serialize the results.

        :param str results: results obtained from Neo4j
        :param list resources_to_expand: resources to expand
        :return: a list of dictionaries containing serialized results received from Neo4j
        :rtype: list
        """
        results_list = []
        for raw_result in results:
            temp = {}
            for node in raw_result:
                if node:
                    inflated_node = inflate_node(node)
                    node_label = inflated_node.__label__
                    if node_label not in temp:
                        temp[node_label] = []

                    if node_label.lower() in resources_to_expand:
                        serialized_node = inflated_node.serialized_all
                    else:
                        serialized_node = inflated_node.serialized

                    serialized_node['resource_type'] = node_label
                    if serialized_node not in temp[node_label]:
                        temp[node_label].append(serialized_node)
            results_list.append(temp)

        return results_list
Example #2
0
    def inflate_results(results):
        """
        Inflate the results.

        :param str results: results obtained from Neo4j
        :return: a list of dictionaries containing serialized results received from Neo4j
        :rtype: list
        """
        results_list = []
        for raw_result in results:
            nodes = []
            for node in raw_result:
                if node:
                    inflated_node = inflate_node(node)
                    nodes.append(inflated_node)
            results_list.append(nodes)

        return results_list
Example #3
0
def get_recent_nodes():
    """
    Get the most recent nodes of each node type.

    :return: a tuple with the first value as a dictionary with the keys as
        names of each node type, and values of arrays of the most recents of
        each node, and the second as metadata
    :rtype: tuple
    """
    timestamp_dict = {
        FreshmakerEvent.__label__: 'time_created',
        BugzillaBug.__label__: 'modified_time',
        DistGitCommit.__label__: 'commit_date',
        KojiBuild.__label__: 'completion_time',
        Advisory.__label__: 'update_date'
    }
    id_dict = {}
    final_result_data = {}
    final_result_metadata = {
        'id_keys': id_dict,
        'timestamp_keys': timestamp_dict
    }
    for label, time_property in timestamp_dict.items():
        query = (
            'MATCH (node:{label}) '
            'WHERE node.{time_property} IS NOT NULL '
            'RETURN node '
            'ORDER BY node.{time_property} DESC LIMIT 5'
        ).format(label=label, time_property=time_property)
        results, _ = db.cypher_query(query)
        for result in results:
            node_results = final_result_data.setdefault(label, [])
            # result is always a list of a single node
            node = inflate_node(result[0])
            serialized_node = node.serialized_all
            serialized_node['resource_type'] = node.__label__
            serialized_node['display_name'] = node.display_name
            node_results.append(serialized_node)
            if node.__label__ not in id_dict:
                id_dict[node.__label__] = node.unique_id_property

    return (final_result_data, final_result_metadata)
Example #4
0
    def serialized_all(self):
        """
        Generate a serialized form of the node that includes all its relationships.

        :return: a serialized form of the node with relationships
        :rtype: dictionary
        :raises RuntimeError: if the label of a Neo4j node can't be mapped back to a neomodel class
        """
        # Avoid circular imports
        from estuary.models import models_inheritance
        # A set that will keep track of all properties on the node that weren't returned from Neo4j
        null_properties = set()
        # A mapping of Neo4j relationship names in the format of:
        # {
        #     node_label: {
        #         relationship_name: {direction: (property_name, cardinality_class) ...},
        #     }
        # }
        relationship_map = {}
        for property_name, relationship in self.__all_relationships__:
            node_label = relationship.definition['node_class'].__label__
            relationship_name = relationship.definition['relation_type']
            for label in models_inheritance[node_label]:
                if label not in relationship_map:
                    relationship_map[label] = {}

                relationship_direction = relationship.definition['direction']
                if relationship_direction == EITHER:
                    # The direction can be coming from either direction, so map both
                    properties = {
                        INCOMING: (property_name, relationship.manager),
                        OUTGOING: (property_name, relationship.manager),
                    }
                else:
                    properties = {
                        relationship_direction:
                        (property_name, relationship.manager)
                    }

                if relationship_name not in relationship_map[label]:
                    relationship_map[label][relationship_name] = properties
                else:
                    relationship_map[label][relationship_name].update(
                        properties)
                null_properties.add(property_name)

        # This variable will contain the current node as serialized + all relationships
        serialized = self.serialized
        # Get all the direct relationships in both directions
        results, _ = self.cypher(
            'MATCH (a) WHERE id(a)={self} MATCH (a)-[r]-(all) RETURN r, all')
        for relationship, node in results:
            # If the starting node in the relationship is the same as the node being serialized,
            # we know that the relationship is outgoing
            if relationship.start_node.id == self.id:
                direction = OUTGOING
            else:
                direction = INCOMING

            # Convert the Neo4j result into a model object
            inflated_node = inflate_node(node)
            try:
                property_name, cardinality_class = \
                    relationship_map[inflated_node.__label__][relationship.type][direction]
            except KeyError:
                if direction == OUTGOING:
                    direction_text = 'outgoing'
                else:
                    direction_text = 'incoming'
                log.warn(
                    'An {0} {1} relationship of {2!r} with {3!r} is not mapped in the models and '
                    'will be ignored'.format(direction_text, relationship.type,
                                             self, inflate_node))
                continue

            if not serialized.get(property_name):
                null_properties.remove(property_name)

            if cardinality_class in (One, ZeroOrOne):
                serialized[property_name] = inflated_node.serialized
            else:
                if not serialized.get(property_name):
                    serialized[property_name] = []
                serialized[property_name].append(inflated_node.serialized)

        # Neo4j won't return back relationships it doesn't know about, so just make them empty
        # so that the keys are always consistent
        for property_name in null_properties:
            prop = getattr(self, property_name)
            if isinstance(prop, One) or isinstance(prop, ZeroOrOne):
                serialized[property_name] = None
            else:
                serialized[property_name] = []

        return serialized
Example #5
0
def get_siblings(resource, uid):
    """
    Get siblings of next/previous node that are correlated to the node in question.

    :param str resource: a resource name that maps to a neomodel class
    :param str uid: the value of the UniqueIdProperty to query with
    :return: a Flask JSON response
    :rtype: flask.Response
    :raises NotFound: if the item is not found
    :raises ValidationError: if an invalid resource was requested
    """
    story_type_mapper = {
        'container': 'ContainerStoryManager',
        'module': 'ModuleStoryManager'
    }
    story_type = request.args.get('story_type', 'container').lower()

    story_manager_class = story_type_mapper.get(story_type)
    if story_manager_class:
        story_manager = getattr(estuary.utils.story, story_manager_class)()
    else:
        raise ValidationError(
            'Supplied story type is invalid. Select from: {0}'.format(
                ', '.join(current_app.config['STORY_MANAGER_SEQUENCE'])))

    # This is the node that is part of a story which has the relationship to the desired
    # sibling nodes
    story_node = get_neo4j_node(resource, uid)
    if not story_node:
        raise NotFound('This item does not exist')

    story_node_story_flow = story_manager.story_flow(story_node.__label__)
    # If backward_rel is true, we fetch siblings of the previous node, next node otherwise.
    # For example, if you pass in an advisory and want to know the Freshmaker events triggered from
    # that advisory, backward_rel would be false. If you want to know the Koji builds attached to
    # that advisory, then backward_rel would true.
    backward = str_to_bool(request.args.get('backward_rel'))
    if backward and story_node_story_flow['backward_label']:
        desired_siblings_label = story_node_story_flow['backward_label']
    elif not backward and story_node_story_flow['forward_label']:
        desired_siblings_label = story_node_story_flow['forward_label']
    else:
        raise ValidationError(
            'Siblings cannot be determined on this kind of resource')

    sibling_nodes = story_manager.get_sibling_nodes(desired_siblings_label,
                                                    story_node)
    # Inflating and formatting results from Neo4j
    serialized_results = []
    for result in sibling_nodes:
        inflated_node = inflate_node(result[0])
        serialized_node = inflated_node.serialized_all
        serialized_node['resource_type'] = inflated_node.__label__
        serialized_node['display_name'] = inflated_node.display_name
        serialized_results.append(serialized_node)

    description = story_manager.get_siblings_description(
        story_node.display_name, story_node_story_flow, backward)

    result = {'data': serialized_results, 'meta': {'description': description}}

    return jsonify(result)