Ejemplo n.º 1
0
    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),
        )
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
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)
Ejemplo n.º 7
0
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]