def merge(self, source): """Merge a resource to collection. Merge content into existing resource or create new content. Returns the message digest after merging the content. If content source does not exist in the collection it causes migrage operation. This method cannot merge a collection because so far there has been no need for it. Args: source (Resource): Resource to be merged to collection. Returns: str: None or message digest after merging the source. """ digest = None if not source or not isinstance(source, Resource): self._logger.debug( 'source was not merged to collection: {}'.format( Logger.remove_ansi(str(source)))) return digest if source.digest in self.keys(): if self[source.digest].merge(source): digest = self[source.digest].digest else: self._logger.debug( 'merging content to existing resource failed: {}'.format( Logger.remove_ansi(str(source)))) try: del self[source.digest] except KeyError: self._logger.info( 'unexpected failure to delete existing content: {}'. format(Logger.remove_ansi(str(source)))) else: if self.migrate(source): digest = source.digest else: self._logger.debug( 'migrating new content to collection failed: {}'.format( Logger.remove_ansi(str(source)))) try: del self[ source. digest] # This should never be executed. But just in case of an error in above migrate method. except KeyError: pass return digest
def _set_integrity_error(self, error, resource): """Set integrity error. Args: error (Exception): Exception string from integrity error. resource (Resource): Resource which SQL operation caused exception. """ digest = self._get_digest(resource) match = self._catch_violating_column.search(str(error)) if match: if match.group('column') == 'uuid' and not Config.defaults: cause = Cause.HTTP_500 else: cause = Cause.HTTP_CONFLICT Cause.push( cause, 'content: {} :already exist with digest: {:.16}'.format( match.group('column'), digest)) else: self._logger.info( 'database integrity error parse failure: {}'.format(error)) Cause.push( Cause.HTTP_CONFLICT, 'content already exist with digest: {:.16}'.format(digest)) if not Config.defaults: self._logger.info( 'database integrity error from database: {}'.format( traceback.format_exc())) self._logger.info( 'database integrity error from resource: {}'.format( Logger.remove_ansi(str(resource)))) self._logger.info( 'database integrity error stack trace: {}'.format( traceback.format_stack(limit=20)))
def migrate(self, source): """Migrate resource or collection to collection. Add new resources or override originals if they exist. Args: source (content): Resource or Collection that is migrated. """ migrated = False if isinstance(source, Collection): for resource in source.resources(): self.migrate(resource) elif isinstance(source, Resource): if source.category in Const.CATEGORIES: if source.seal(): if source.digest not in self.keys(): self._data['meta'][ 'total'] = self._data['meta']['total'] + 1 self._data['data'][source.digest] = {} self._data['data'][source.digest]['data'] = source migrated = True else: self._logger.debug( 'resource: {} :not migrated to collection'.format( source.digest)) else: self._logger.debug( 'migrate to collection failed due to unknown category: {}'. format(Logger.remove_ansi(str(source)))) return migrated
def _get_digest(self, resource): """Return digest of given content from database.""" digest = 'not found' category = resource.category collection = self._select_data(resource.data) if not collection: collection = self._select_uuid(resource.uuid) if len(collection) == 1: digest = next(collection.resources()).digest else: Cause.push( Cause.HTTP_500, 'internal error when searching content possibly violating database unique constraints' ) self._logger.debug( 'internal server error searching resource from database: {}'. format(Logger.remove_ansi(str(resource)))) self._logger.debug( 'internal server error searching unique digest hits: %d :from category: %s', len(collection), category) return digest