예제 #1
0
def getLinkedObjects(object_id: text, request, response):
    """Return all objects linked to the object specified by its id."""
    try:
        if utils.uuidCheck(object_id):
            dataHandle = request.context['dbSession'].query(
                tableMapping.BaseObject).filter(
                    tableMapping.BaseObject.object_id == object_id).first()
            if dataHandle:
                customHeaders = {}
                utils.getCustomHeaders(request.headers, customHeaders)
                resultJson = Results(
                    'tool/link/{object_id}'.format(object_id=object_id), True)
                weakLinkObjects = set()
                StrongLinkObjects = set()
                onceFlag = False
                for relation in ['weak_first_objects', 'weak_second_objects']:
                    entry = set(getattr(dataHandle, relation))
                    weakLinkObjects = weakLinkObjects.union(entry)
                for relation in [
                        'strong_first_objects', 'strong_second_objects'
                ]:
                    if getattr(
                            inspect(dataHandle).mapper.relationships,
                            relation).uselist:
                        entry = set(getattr(dataHandle, relation))
                        StrongLinkObjects = StrongLinkObjects.union(entry)
                    else:
                        if getattr(dataHandle, relation) is not None:
                            entry = getattr(dataHandle, relation)
                            StrongLinkObjects.add(entry)
                objects = set()
                objectList = []
                for item in StrongLinkObjects:
                    objects.add(item.strong_second_object)
                    objects.add(item.strong_first_object)
                for item in weakLinkObjects:
                    objects.add(item.weak_second_object)
                    objects.add(item.weak_first_object)
                if dataHandle not in objects:
                    objects.add(dataHandle)

                for obj in objects:
                    objectId = getattr(obj, 'object_id')
                    data = None
                    if customHeaders['removePrivateAttributes']:
                        data = {
                            col: getattr(obj, col)
                            for col in inspect(obj).mapper.c.keys()
                            if col not in
                            ['container', 'object_type', 'reference_id']
                        }
                    else:
                        data = {
                            col: getattr(obj, col)
                            for col in inspect(obj).mapper.c.keys()
                        }
                    identifier, exists = resultJson.addObject(
                        inspect(obj).mapper.class_.__name__,
                        uniqueId=objectId,
                        **data)
                    objectList.append(
                        ((obj, inspect(obj).mapper.class_.__name__),
                         identifier))
                objectsDict = dict(objectList)
                for item in StrongLinkObjects:
                    first_id = objectsDict[(
                        item.strong_first_object,
                        inspect(
                            item.strong_first_object).mapper.class_.__name__)]
                    second_id = objectsDict[(
                        item.strong_second_object,
                        inspect(
                            item.strong_second_object).mapper.class_.__name__)]
                    resultJson.addLink(inspect(item).mapper.class_.__name__,
                                       firstId=first_id,
                                       secondId=second_id)

                ## Recreating weak link connection
                for item in weakLinkObjects:
                    if item.weak_first_object is dataHandle:
                        first_id = objectsDict[(item.weak_first_object,
                                                inspect(
                                                    item.weak_first_object).
                                                mapper.class_.__name__)]
                        resultJson.addLink(
                            inspect(item).mapper.class_.__name__,
                            firstId=first_id,
                            secondId=item.weak_second_object.object_id)
                    if item.weak_second_object is dataHandle:
                        second_id = objectsDict[(
                            item.weak_second_object,
                            item.weak_second_object.__class__.__name__)]
                        resultJson.addLink(
                            inspect(item).mapper.class_.__name__,
                            firstId=item.weak_first_object.object_id,
                            secondId=second_id)
                tempJson = resultJson.getJson()
                ## pop off temp_weak_links
                tempJson.pop('temp_weak_links', None)
                request.context['payload'] = tempJson
                resultJson.clearJson()
                del resultJson

            else:
                request.context['payload']['errors'].append(
                    'The provided object_id {} does not exist in the database.'
                    .format(object_id))
                response.status = HTTP_404
        else:
            request.context['payload']['errors'].append(
                'Expected 32-bit hex for id')
            response.status = HTTP_400
    except:
        errorMessage(request, response)

    ## end getLinkedObjects
    return cleanPayload(request)
예제 #2
0
class DeleteDbObject:
    """Utility for deleting objects from the database"""
    def __init__(self, logger, dbClient, deleteObjectList):
        self.logger = logger
        ## TODO: verify and then remove this section, since we're using pools
        ## across all code segments now, instead of mixed single vs pool clients
        ################################################################
        ## Normalize when passed both of these two db client types:
        ##   database.connectionPool.DatabaseClient
        ##   sqlalchemy.orm.scoping.scoped_session
        self.dbSession = None
        #logger.debug('dbClient is of type: {}'.format(type(dbClient)))
        if isinstance(dbClient, sqlAlchemySession) or isinstance(
                dbClient, sqlAlchemyScopedSession):
            #logger.debug('dbClient is a SqlAlchemy session')
            self.dbSession = dbClient
        elif isinstance(dbClient, tableMapping.DatabaseClient):
            #logger.debug('dbObject is a connectionPool.DatabaseClient; converting to a SqlAlchemy session')
            self.dbSession = dbClient.session
        else:
            raise EnvironmentError(
                'The dbClient passed to QueryProcessing must be either a connectionPool.DatabaseClient or a sqlalchemy.orm.scoping.scoped_session.'
            )
        ################################################################
        ## Storing the query json into a dictionary
        self.deleteObjectList = deleteObjectList
        self.validClassObjects = dict()
        self.validStrongLinks = dict()
        self.validWeakLinks = dict()
        self.getValidClassObjects()
        self.resultJson = Results('DeleteDbObjects')

    def getValidStrongLinks(self):
        """Creates a dictionary of valid strong links."""
        strongLinkClass = tableMapping.StrongLink
        utils.classLinkDict(self.validStrongLinks, strongLinkClass)

    def getValidWeakLinks(self):
        """Creates a dictionary of valid weak links."""
        weakLinkClass = tableMapping.WeakLink
        utils.classLinkDict(self.validWeakLinks, weakLinkClass)

    def getValidClassObjects(self):
        """Creates a dictionary of valid BaseObjects."""
        BaseObjectClass = tableMapping.BaseObject
        tempdict = dict()
        tempdict['classObject'] = BaseObjectClass
        tempdict['children'] = [
            i.__name__ for i in BaseObjectClass.__subclasses__()
        ]
        self.validClassObjects[BaseObjectClass.__name__] = tempdict
        utils.classDict(self.validClassObjects, BaseObjectClass)
        utils.finalDictBulid(self.validClassObjects)

        ## end getValidClassObjects
        return

    def preprocessNested(self, inputList, inputObject):
        """Preprocess nested format; convert to flat before deletion."""
        try:
            for inputDictionary in inputList:
                perObject = dict()
                perObject["class_name"] = inputDictionary["class_name"]
                perObject["identifier"] = inputDictionary["identifier"]
                perObject["data"] = inputDictionary["data"]
                inputObject.append(perObject)
                if "children" in inputDictionary.keys():
                    for obj in inputDictionary["children"]:
                        self.logger.debug("obj {}".format(obj))
                        objInstance = inputDictionary["children"][obj]
                        # for label in objInstance.keys():
                        self.preprocessNested(objInstance, inputObject)
        except:
            exception = traceback.format_exception(sys.exc_info()[0],
                                                   sys.exc_info()[1],
                                                   sys.exc_info()[2])
            self.logger.error(
                'Exception in preprocessNested: {}'.format(exception))

        ## end preprocessNested
        return

    def wrapNested(self):
        """This is a helper function for performing nested delete."""
        result = {}
        try:
            objectsToDelete = []
            for key in self.deleteObjectList:
                self.preprocessNested(self.deleteObjectList[key],
                                      objectsToDelete)
            self.deleteObjectList['objects'] = objectsToDelete
            self.logger.debug(
                "Deleting the objects {}".format(objectsToDelete))
            result = self.queryBeforeDeletion()
        except:
            exception = traceback.format_exception(sys.exc_info()[0],
                                                   sys.exc_info()[1],
                                                   sys.exc_info()[2])
            self.logger.error('Exception in wrapNested: {}'.format(exception))

        ## end wrapNested
        return result

    def queryBeforeDeletion(self):
        """SqlAlchemy ORM Statement is created here; later sent for deletion."""
        result = {}
        try:
            data = None
            for item in self.deleteObjectList['objects']:
                if item['class_name'] in self.validClassObjects.keys():
                    className = item['class_name']
                    if utils.uuidCheck(item["identifier"]):
                        self.logger.info(
                            "Received a 32 hex querying the DB with the UUID")
                        objectToQuery = self.validClassObjects['BaseObject'][
                            'classObject']
                        entryToDelete = self.dbSession.query(
                            objectToQuery).filter(objectToQuery.object_id ==
                                                  item["identifier"]).first()
                    else:
                        objectToQuery = self.validClassObjects[className][
                            'classObject']
                        constraints = objectToQuery.constraints()
                        if item['data'].keys() and all(
                                col in item['data'].keys()
                                for col in constraints):
                            entryToDelete = self.dbSession.query(
                                self.validClassObjects[className]
                                ['classObject']).filter(
                                    and_(
                                        (getattr(
                                            self.validClassObjects[className]
                                            ['classObject'],
                                            colName) == item["data"][colName]
                                         for colName in constraints))).first()
                        else:
                            self.logger.warn(
                                "Insufficient data to delete: {}".format(item))
                            entryToDelete = None
                    self.dbSession.commit()
                    data = self.deleteAndStore(entryToDelete)
            result = self.resultJson.getJson()
            if data:
                retult["objects"].extend(data)
            self.resultJson.clearJson()

        except:
            exception = traceback.format_exception(sys.exc_info()[0],
                                                   sys.exc_info()[1],
                                                   sys.exc_info()[2])
            self.logger.error(
                'Exception in queryBeforeDeletion: {}'.format(exception))

        ## end queryBeforeDeletion
        ## Note: returned result is a potential work flow for a future kafka topic
        return result

    def deleteAndStore(self, thisEntry):
        """Used for copying connected weak objects before deletion."""
        try:
            if thisEntry is not None:
                self.logger.info('Initiating deletion for {}'.format(
                    inspect(thisEntry).mapper.class_.__name__))
                ## Copies all connected objects into resultJson and delete.
                self.recreateMappingSubtype(thisEntry)
                ## Copies the weak object
                self.logger.info(
                    'Begin copying the weak uncertain BaseObjects connected to the current object'
                )
                ## Begins the storingReferenceIdObjectConnections
                ## to store the weak BaseOject type before deletion.
                weakObjects = self.storingReferenceIdObjectConnections(
                    thisEntry)
                self.logger.info(
                    'BaseObjects connected to the current object {}'.format(
                        weakObjects))
                self.dbSession.delete(thisEntry)
                self.dbSession.commit()
                val = weakObjects.get("objects", -1)
                if val is not -1:
                    weakObjects.get("objects")
                    return weakObjects.get("objects")

        except:
            exception = traceback.format_exception(sys.exc_info()[0],
                                                   sys.exc_info()[1],
                                                   sys.exc_info()[2])
            self.logger.error(
                'Exception in deleteAndStore: {}'.format(exception))

        ## end deleteAndStore
        return

    def recreateMappingSubtype(self, thisEntry):
        """Creates a JSON copy of an object and its children using recursion.
		Arguments:
		  thisEntry (BaseObject) : DB object instance
		"""
        weakLinkObjects = set()
        StrongLinkObjects = set()
        ## Store all the strong and weak links relevent to the BaseObject that
        ## is about to be deleted, for recreation.
        ## ignoring the weak link since are not trying to recreate the links
        for relation in ['strong_first_objects', 'strong_second_objects']:
            ## But since the strong first/second can be either a list or a
            ## single entry, we need to check object types...
            if getattr(inspect(thisEntry).mapper.relationships,
                       relation).uselist:
                entry = set(getattr(thisEntry, relation))
                StrongLinkObjects = StrongLinkObjects.union(entry)
            else:
                if getattr(thisEntry, relation) is not None:
                    entry = getattr(thisEntry, relation)
                    StrongLinkObjects.add(entry)
        self.logger.info('Strong Link objects added to the set {}'.format(
            StrongLinkObjects))

        ## Add BaseObject types into objects to be reconstructed as a dictionary
        objects = set()
        objectList = []
        for item in StrongLinkObjects:
            objects.add(item.strong_second_object)
            objects.add(item.strong_first_object)

        ## Handling standalone BaseObject types
        if thisEntry not in objects:
            objects.add(thisEntry)

        ## BaseObject type information is stored in JSON
        for obj in objects:
            self.logger.info(
                'Add old class entry into the result JSON {}'.format(
                    inspect(obj).mapper.class_.__name__))

            data = {
                col: getattr(obj, col)
                for col in inspect(obj).mapper.c.keys()
                if getattr(obj, col) is not None
            }
            identifier, exists = self.resultJson.addObject(
                inspect(obj).mapper.class_.__name__, **data)
            objectList.append(
                ((obj, inspect(obj).mapper.class_.__name__), identifier))

        objectsDict = dict(objectList)
        self.logger.debug(
            'The new recreated dictionary: {}'.format(objectsDict))

        ## Recreating strong link connection
        for item in StrongLinkObjects:
            self.logger.info(
                'Adding Strong link of type {}, into the result JSON'.format(
                    inspect(item).mapper.class_.__name__))
            first_id = objectsDict[(
                item.strong_first_object,
                inspect(item.strong_first_object).mapper.class_.__name__)]
            second_id = objectsDict[(
                item.strong_second_object,
                inspect(item.strong_second_object).mapper.class_.__name__)]
            self.resultJson.addLink(inspect(item).mapper.class_.__name__,
                                    firstId=first_id,
                                    secondId=second_id)

        ## This is used for storing grandchildren of BaseObject type
        ## which are about to be deleted.
        for child in thisEntry.strong_second_objects:
            if child.strong_second_object.strong_second_objects is not None and len(
                    child.strong_second_object.strong_second_objects) > 0:
                self.logger.info(
                    'Begin recursion loop, to identify the strongly connected grandchildren'
                )
                childObject = child.strong_second_object
                self.recreateMappingSubtype(childObject)
                self.logger.info(
                    'End of recursion loop, to identify the strongly connected grandchildren'
                )

        ## end recreateMappingSubtype
        return

    def storingReferenceIdObjectConnections(self, thisEntry):
        """Storing BaseObject connections linked via reference_id in JSON.

		Arguments:
		  thisEntry (BaseObject)     : DB object instance
		"""
        weakObjects = {}
        entry = set(getattr(thisEntry, 'base_object_children'))
        if entry:
            self.logger.info(
                'Copying the weak uncertain Baseobjects connected to the current object {}'
                .format(entry))
            weakObjects['objects'] = []
            for obj in entry:
                classObject = {}
                classObject['class_name'] = inspect(obj).mapper.class_.__name__
                self.logger.info(
                    'Adding old class object type entry into the result JSON {}'
                    .format(inspect(obj).mapper.class_.__name__))
                ## The dictionary expression below copies values from ORM
                ## instance by filtering out the 'time_created' and 'reference_id'
                data = {
                    col: getattr(obj, col)
                    for col in inspect(obj).mapper.c.keys()
                    if getattr(obj, col) is not None
                }
                classObject['data'] = data
                weakObjects['objects'].append(classObject)

        ## end storingReferenceIdObjectconnections
        return weakObjects