def __call__(self, request): # Assign a random unique ID to the request. This will be used to associate multiple object changes made during # the same request. request.id = uuid.uuid4() # Process the request with change logging enabled with change_logging(request): response = self.get_response(request) return response
def refresh_datasource_content(model_name, record, request, job_result, delete=False): """Invoke the refresh callbacks for every content type registered for this model. Note that these callback functions are invoked regardless of whether a given model instance actually is flagged as providing each content type; this is intentional, as there may be cleanup required if a model was previously providing content but has now been changed to no longer provide that content. Args: model_name (str): Identifier of the datasource owner, such as "extras.gitrepository" record (models.Model): Datasource model instance, such as a GitRepository record request (HttpRequest): Initiating request for this refresh, optional, used for change logging if provided job_result (JobResult): Passed through to the callback functions to use with logging their actions. delete (bool): True if the record is being deleted; False if it is being created/updated. """ job_result.log( f"Refreshing data provided by {record}...", level_choice=LogLevelChoices.LOG_INFO, ) job_result.save() if request: with change_logging(request): for entry in get_datasource_contents(model_name): job_result.log(f"Refreshing {entry.name}...", level_choice=LogLevelChoices.LOG_INFO) try: entry.callback(record, job_result, delete=delete) except Exception as exc: job_result.log( f"Error while refreshing {entry.name}: {exc}", level_choice=LogLevelChoices.LOG_FAILURE, ) job_result.set_status(JobResultStatusChoices.STATUS_ERRORED) job_result.save() job_result.log( f"Data refresh from {record} complete!", level_choice=LogLevelChoices.LOG_INFO, ) job_result.save() else: for entry in get_datasource_contents(model_name): job_result.log(f"Refreshing {entry.name}...", level_choice=LogLevelChoices.LOG_INFO) try: entry.callback(record, job_result, delete=delete) except Exception as exc: job_result.log( f"Error while refreshing {entry.name}: {exc}", level_choice=LogLevelChoices.LOG_FAILURE, ) job_result.set_status(JobResultStatusChoices.STATUS_ERRORED) job_result.save() job_result.log( f"Data refresh from {record} complete!", level_choice=LogLevelChoices.LOG_INFO, ) job_result.save()
def test_webhooks_snapshot_on_delete(self): request_id = uuid.uuid4() webhook = Webhook.objects.get(type_create=True) timestamp = str(timezone.now()) def mock_send(_, request, **kwargs): # Validate the outgoing request body body = json.loads(request.body) self.assertEqual(body["data"]["name"], "Site 1") self.assertEqual(body["snapshots"]["prechange"]["name"], "Site 1") self.assertEqual(body["snapshots"]["postchange"], None) self.assertEqual( body["snapshots"]["differences"]["removed"]["name"], "Site 1") self.assertEqual(body["snapshots"]["differences"]["added"], None) class FakeResponse: ok = True status_code = 200 return FakeResponse() # Patch the Session object with our mock_send() method, then process the webhook for sending with patch.object(Session, "send", mock_send): request = mock.MagicMock() request.user = self.user request.id = request_id with change_logging(request): site = Site(name="Site 1", slug="site-1") site.save() # deepcopy instance state to be used by SiteSerializer and get_snapshots temp_site = deepcopy(site) site.delete() serializer = SiteSerializer(temp_site, context={"request": None}) snapshots = get_snapshots( temp_site, ObjectChangeActionChoices.ACTION_DELETE) process_webhook( webhook.pk, serializer.data, Site._meta.model_name, ObjectChangeActionChoices.ACTION_CREATE, timestamp, self.user.username, request_id, snapshots, )
def test_webhooks_process_webhook_on_update(self): """ Mock a Session.send to inspect the result of `process_webhook()`. Note that process_webhook is called directly, not via a celery task. """ request_id = uuid.uuid4() webhook = Webhook.objects.get(type_create=True) timestamp = str(timezone.now()) def mock_send(_, request, **kwargs): """ A mock implementation of Session.send() to be used for testing. Always returns a 200 HTTP response. """ signature = generate_signature(request.body, webhook.secret) # Validate the outgoing request headers self.assertEqual(request.headers["Content-Type"], webhook.http_content_type) self.assertEqual(request.headers["X-Hook-Signature"], signature) self.assertEqual(request.headers["X-Foo"], "Bar") # Validate the outgoing request body body = json.loads(request.body) self.assertEqual(body["event"], "created") self.assertEqual(body["timestamp"], timestamp) self.assertEqual(body["model"], "site") self.assertEqual(body["username"], "testuser") self.assertEqual(body["request_id"], str(request_id)) self.assertEqual(body["data"]["name"], "Site Update") self.assertEqual(body["data"]["status"]["value"], self.planned_status.slug) self.assertEqual(body["data"]["region"]["slug"], self.region_two.slug) self.assertEqual(body["snapshots"]["prechange"]["name"], "Site 1") self.assertEqual(body["snapshots"]["prechange"]["status"]["value"], self.active_status.slug) self.assertEqual(body["snapshots"]["prechange"]["region"]["slug"], self.region_one.slug) self.assertEqual(body["snapshots"]["postchange"]["name"], "Site Update") self.assertEqual( body["snapshots"]["postchange"]["status"]["value"], self.planned_status.slug) self.assertEqual(body["snapshots"]["postchange"]["region"]["slug"], self.region_two.slug) self.assertEqual( body["snapshots"]["differences"]["removed"]["name"], "Site 1") self.assertEqual(body["snapshots"]["differences"]["added"]["name"], "Site Update") class FakeResponse: ok = True status_code = 200 return FakeResponse() # Patch the Session object with our mock_send() method, then process the webhook for sending with patch.object(Session, "send", mock_send): self.client.force_login(self.user) request = mock.MagicMock() request.user = self.user request.id = request_id with change_logging(request): site = Site(name="Site 1", slug="site-1", status=self.active_status, region=self.region_one) site.save() site.name = "Site Update" site.status = self.planned_status site.region = self.region_two site.save() serializer = SiteSerializer(site, context={"request": None}) snapshots = get_snapshots( site, ObjectChangeActionChoices.ACTION_UPDATE) process_webhook( webhook.pk, serializer.data, Site._meta.model_name, ObjectChangeActionChoices.ACTION_CREATE, timestamp, self.user.username, request_id, snapshots, )
def test_webhooks_snapshot_without_model_api_serializer( self, get_serializer_for_model): def get_serializer(model_class): raise SerializerNotFound get_serializer_for_model.side_effect = get_serializer request_id = uuid.uuid4() webhook = Webhook.objects.get(type_create=True) timestamp = str(timezone.now()) def mock_send(_, request, **kwargs): # Validate the outgoing request body body = json.loads(request.body) self.assertEqual(body["snapshots"]["prechange"]["status"], str(self.active_status.id)) self.assertEqual(body["snapshots"]["prechange"]["region"], str(self.region_one.id)) self.assertEqual(body["snapshots"]["postchange"]["name"], "Site Update") self.assertEqual(body["snapshots"]["postchange"]["status"], str(self.planned_status.id)) self.assertEqual(body["snapshots"]["postchange"]["region"], str(self.region_two.id)) self.assertEqual( body["snapshots"]["differences"]["removed"]["name"], "Site 1") self.assertEqual(body["snapshots"]["differences"]["added"]["name"], "Site Update") class FakeResponse: ok = True status_code = 200 return FakeResponse() with patch.object(Session, "send", mock_send): self.client.force_login(self.user) request = mock.MagicMock() request.user = self.user request.id = request_id with change_logging(request): site = Site(name="Site 1", slug="site-1", status=self.active_status, region=self.region_one) site.save() site.name = "Site Update" site.status = self.planned_status site.region = self.region_two site.save() serializer = SiteSerializer(site, context={"request": None}) snapshots = get_snapshots( site, ObjectChangeActionChoices.ACTION_UPDATE) process_webhook( webhook.pk, serializer.data, Site._meta.model_name, ObjectChangeActionChoices.ACTION_CREATE, timestamp, self.user.username, request_id, snapshots, )