def get(self, request, organization): try: project = Project.objects.get(id=request.GET["projectId"], organization_id=organization.id) except Project.DoesNotExist: return Response([], status=404) components = [] for install in SentryAppInstallation.get_installed_for_org( organization.id): _components = SentryAppComponent.objects.filter( sentry_app_id=install.sentry_app_id) if "filter" in request.GET: _components = _components.filter(type=request.GET["filter"]) for component in _components: try: sentry_app_components.Preparer.run(component=component, install=install, project=project) components.append(component) except APIError: continue return self.paginate( request=request, queryset=components, paginator_cls=OffsetPaginator, on_results=lambda x: serialize(x, request.user), )
def get_custom_actions(self, project: Project) -> Sequence[Mapping[str, Any]]: action_list = [] for install in SentryAppInstallation.get_installed_for_org( project.organization_id): component = install.prepare_sentry_app_components( "alert-rule-action", project) if component: kwargs = { "install": install, "event_action": self, } action_details = serialize( component, None, SentryAppAlertRuleActionSerializer(), **kwargs) action_list.append(action_details) return action_list
def get(self, request, organization): """ Fetches actions that an alert rule can perform for an organization """ if not features.has( "organizations:incidents", organization, actor=request.user): raise ResourceDoesNotExist actions = [] # Cache Integration objects in this data structure to save DB calls. provider_integrations = defaultdict(list) for integration in get_available_action_integrations_for_org( organization): provider_integrations[integration.provider].append(integration) for registered_type in AlertRuleTriggerAction.get_registered_types(): # Used cached integrations for each `registered_type` instead of making N calls. if registered_type.integration_provider: actions += [ build_action_response(registered_type, integration=integration, organization=organization) for integration in provider_integrations[ registered_type.integration_provider] ] # Add all alertable SentryApps to the list. elif registered_type.type == AlertRuleTriggerAction.Type.SENTRY_APP: actions += [ build_action_response(registered_type, sentry_app_installation=install) for install in SentryAppInstallation.get_installed_for_org( organization.id).filter(sentry_app__is_alertable=True, ) ] else: actions.append(build_action_response(registered_type)) return Response(actions, status=status.HTTP_200_OK)
def get_custom_actions(self, project: Project) -> Sequence[Mapping[str, Any]]: action_list = [] for install in SentryAppInstallation.get_installed_for_org( project.organization_id): _components = SentryAppComponent.objects.filter( sentry_app_id=install.sentry_app_id, type="alert-rule-action") for component in _components: try: sentry_app_components.Preparer.run(component=component, install=install, project=project) kwargs = { "install": install, "event_action": self, } action_details = serialize( component, None, SentryAppAlertRuleActionSerializer(), **kwargs) action_list.append(action_details) except APIError: continue return action_list
def _process_resource_change(action, sender, instance_id, retryer=None, *args, **kwargs): # The class is serialized as a string when enqueueing the class. model = TYPES[sender] # The Event model has different hooks for the different event types. The sender # determines which type eg. Error and therefore the 'name' eg. error if issubclass(model, Event): if not kwargs.get("instance"): extra = {"sender": sender, "action": action, "event_id": instance_id} logger.info("process_resource_change.event_missing_event", extra=extra) return name = sender.lower() else: # Some resources are named differently than their model. eg. Group vs Issue. # Looks up the human name for the model. Defaults to the model name. name = RESOURCE_RENAMES.get(model.__name__, model.__name__.lower()) # By default, use Celery's `current` but allow a value to be passed for the # bound Task. retryer = retryer or current # We may run into a race condition where this task executes before the # transaction that creates the Group has committed. try: if issubclass(model, Event): # XXX:(Meredith): Passing through the entire event was an intentional choice # to avoid having to query NodeStore again for data we had previously in # post_process. While this is not ideal, changing this will most likely involve # an overhaul of how we do things in post_process, not just this task alone. instance = kwargs.get("instance") else: instance = model.objects.get(id=instance_id) except model.DoesNotExist as e: # Explicitly requeue the task, so we don't report this to Sentry until # we hit the max number of retries. return retryer.retry(exc=e) event = f"{name}.{action}" if event not in VALID_EVENTS: return org = None if isinstance(instance, Group) or isinstance(instance, Event): org = Organization.objects.get_from_cache( id=Project.objects.get_from_cache(id=instance.project_id).organization_id ) installations = filter( lambda i: event in i.sentry_app.events, SentryAppInstallation.get_installed_for_org(org.id).select_related("sentry_app"), ) for installation in installations: data = {} if isinstance(instance, Event): data[name] = _webhook_event_data(instance, instance.group_id, instance.project_id) else: data[name] = serialize(instance) # Trigger a new task for each webhook send_resource_change_webhook.delay(installation_id=installation.id, event=event, data=data)
def get(self, request, project): """ Retrieve the list of configuration options for a given project. """ action_list = [] condition_list = [] filter_list = [] project_has_filters = features.has("projects:alert-filters", project) can_create_tickets = features.has( "organizations:integrations-ticket-rules", project.organization) has_percent_condition = features.has( "organizations:issue-percent-filters", project.organization) # TODO: conditions need to be based on actions for rule_type, rule_cls in rules: node = rule_cls(project) # skip over conditions if they are not in the migrated set for a project with alert-filters if project_has_filters and node.id in MIGRATED_CONDITIONS: continue if not can_create_tickets and node.id in TICKET_ACTIONS: continue context = { "id": node.id, "label": node.label, "enabled": node.is_enabled() } if hasattr(node, "prompt"): context["prompt"] = node.prompt if hasattr(node, "form_fields"): context["formFields"] = node.form_fields if node.id in TICKET_ACTIONS: context["actionType"] = "ticket" context["ticketType"] = node.ticket_type context["link"] = node.link # It is possible for a project to have no services. In that scenario we do # not want the front end to render the action as the action does not have # options. if (node.id == "sentry.rules.actions.notify_event_service.NotifyEventServiceAction" and len(node.get_services()) == 0): continue if rule_type.startswith("condition/"): if (has_percent_condition or context["id"] != "sentry.rules.conditions.event_frequency.EventFrequencyPercentCondition" ): condition_list.append(context) elif rule_type.startswith("filter/"): filter_list.append(context) elif rule_type.startswith("action/"): action_list.append(context) for install in SentryAppInstallation.get_installed_for_org( project.organization_id): _components = SentryAppComponent.objects.filter( sentry_app_id=install.sentry_app_id, type="alert-rule-action") for component in _components: try: sentry_app_components.Preparer.run(component=component, install=install, project=project) action_list.append( serialize(component, request.user, SentryAppAlertRuleActionSerializer())) except APIError: continue context = { "actions": action_list, "conditions": condition_list, "filters": filter_list } return Response(context)
def installations_to_notify(organization, event): installations = SentryAppInstallation.get_installed_for_org( organization.id).select_related("sentry_app") return [i for i in installations if event in i.sentry_app.events]