@@ -0,0 +1,3098 @@
+%22%22%22%0AHandler base classes.%0A%0A%22%22%22%0Afrom abc import ABCMeta%0Afrom inflection import humanize%0A%0Afrom requests import get%0A%0A%0Aclass URIHandler(object):%0A    %22%22%22%0A    Base handler for URI-driven events.%0A%0A    As a general rule, we want PubSub events to convey the URI of a resource that was created%0A    (because resources are ideally immutable state). In this case, we want asynchronous workers%0A    to query the existing URI to get more information (and to handle race conditions where the%0A    message was delivered before the resource was committed.)%0A%0A    There are five expected outcomes for this handler:%0A      - Raising an error (e.g. a bug)%0A      - Skipping the handlers (because the pubsub message carried enough information to bypass processing)%0A      - Handling the message after fetching the resource by URI%0A      - Ignore the message after fetching the resource by URI%0A      - Raising a nack (e.g. because the resource was not committed yet)%0A%0A    The middle three cases are all handled here with the expectation that we produce *one* INFO-level%0A    log per message processed (unless an error/nack is raised).%0A%0A    %22%22%22%0A    __metaclass__ = ABCMeta%0A%0A    @property%0A    def name(self):%0A        return humanize(self.__class__.__name__)%0A%0A    def __call__(self, message):%0A        uri = message%5B%22uri%22%5D%0A        self.on_call(message, uri)%0A%0A        skip_reason = self.get_reason_to_skip(message, uri)%0A        if skip_reason is not None:%0A            self.on_skip(message, uri, skip_reason)%0A            return False%0A%0A        resource = self.get_resource(uri=uri)%0A%0A        if self.handle(message, uri, resource):%0A            self.on_handle(message, uri, resource)%0A            return True%0A        else:%0A            self.on_ignore(message, uri, resource)%0A            return False%0A%0A    def on_call(self, message, uri):%0A        self.logger.debug(%0A            %22Starting %7Bhandler%7D%22,%0A            extra=dict(%0A                handler=self.name,%0A                uri=uri,%0A            ),%0A        )%0A%0A    def on_skip(self, message, uri, reason):%0A        self.logger.info(%0A            %22Skipping %7Bhandler%7D because %7Breason%7D%22,%0A            extra=dict(%0A                handler=self.name,%0A                reason=reason,%0A                uri=uri,%0A            ),%0A        )%0A%0A    def on_handle(self, message, uri, resource):%0A        self.logger.info(%0A            %22Handled %7Bhandler%7D%22,%0A            extra=dict(%0A                handler=self.name,%0A                uri=uri,%0A            ),%0A        )%0A%0A    def on_ignore(self, message, uri, resource):%0A        self.logger.info(%0A            %22Ignored %7Bhandler%7D%22,%0A            extra=dict(%0A                handler=self.name,%0A                uri=uri,%0A            ),%0A        )%0A%0A    def get_reason_to_skip(self, message, uri):%0A        %22%22%22%0A        Some messages carry enough context that we can avoid resolving the URI entirely.%0A%0A        %22%22%22%0A        return None%0A%0A    def get_resource(self, uri):%0A        %22%22%22%0A        Mock-friendly URI getter.%0A%0A        %22%22%22%0A        response = get(uri)%0A        response.raise_for_status()%0A        return response.json()%0A%0A    def handle(self, message, resource):%0A        return True%0A

Add a base handler for URI-oriented pubsub messages