def delete(self, request, organization, search_id):
        """
        Delete a saved search

        Permanently remove a saved search.

            {method} {path}

        """
        try:
            search = SavedSearch.objects.get(
                owner__isnull=True,
                organization=organization,
                id=search_id,
            )
        except SavedSearch.DoesNotExist:
            raise ResourceDoesNotExist

        search.delete()
        analytics.record(
            'organization_saved_search.deleted',
            search_type=SearchType(search.type).name,
            org_id=organization.id,
            query=search.query,
        )
        return Response(status=204)
    def dispatch(self, request, organization, **kwargs):
        try:
            config = self.get_repository_data(organization, request.DATA)
        except Exception as e:
            return self.handle_api_error(e)

        try:
            result = self.build_repository_config(
                organization=organization,
                data=config,
            )
        except IntegrationError as e:
            return Response(
                {
                    'errors': {
                        '__all__': e.message
                    },
                }, status=400
            )

        try:
            with transaction.atomic():
                repo = Repository.objects.create(
                    organization_id=organization.id,
                    name=result['name'],
                    external_id=result.get('external_id'),
                    url=result.get('url'),
                    config=result.get('config') or {},
                    provider=self.id,
                    integration_id=result.get('integration_id'),
                )
        except IntegrityError:
            # Try to delete webhook we just created
            try:
                repo = Repository(
                    organization_id=organization.id,
                    name=result['name'],
                    external_id=result.get('external_id'),
                    url=result.get('url'),
                    config=result.get('config') or {},
                    provider=self.id,
                    integration_id=result.get('integration_id'),
                )
                self.on_delete_repository(repo)
            except IntegrationError:
                pass
            return Response(
                {'errors': {'__all__': 'A repository with that name already exists'}},
                status=400,
            )
        else:
            repo_linked.send_robust(repo=repo, user=request.user, sender=self.__class__)

        analytics.record(
            'integration.repo.added',
            provider=self.id,
            id=result.get('integration_id'),
            organization_id=organization.id,
        )
        return Response(serialize(repo, request.user), status=201)
Example #3
0
def sync_assignee_outbound(external_issue_id, user_id, assign, **kwargs):
    # sync Sentry assignee to an external issue
    external_issue = ExternalIssue.objects.get(id=external_issue_id)

    organization = Organization.objects.get(id=external_issue.organization_id)
    has_issue_sync = features.has('organizations:integrations-issue-sync',
                                  organization)

    if not has_issue_sync:
        return

    integration = Integration.objects.get(id=external_issue.integration_id)
    # assume unassign if None
    if user_id is None:
        user = None
    else:
        user = User.objects.get(id=user_id)

    installation = integration.get_installation(
        organization_id=external_issue.organization_id,
    )
    if installation.should_sync('outbound_assignee'):
        installation.sync_assignee_outbound(external_issue, user, assign=assign)
        analytics.record(
            'integration.issue.assignee.synced',
            provider=integration.provider,
            id=integration.id,
            organization_id=external_issue.organization_id,
        )
Example #4
0
 def record_analytics(self):
     analytics.record(
         'sentry_app.uninstalled',
         user_id=self.user.id,
         organization_id=self.install.organization_id,
         sentry_app=self.install.sentry_app.slug,
     )
Example #5
0
def record_issue_tracker_used(plugin, project, user, **kwargs):
    rows_affected, created = OrganizationOnboardingTask.objects.create_or_update(
        organization_id=project.organization_id,
        task=OnboardingTask.ISSUE_TRACKER,
        status=OnboardingTaskStatus.PENDING,
        values={
            'status': OnboardingTaskStatus.COMPLETE,
            'user': user,
            'project_id': project.id,
            'date_completed': timezone.now(),
            'data': {
                'plugin': plugin.slug
            }
        }
    )

    if rows_affected or created:
        check_for_onboarding_complete(project.organization_id)

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id
    analytics.record(
        'issue_tracker.used',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        issue_tracker=plugin.slug,
    )
Example #6
0
def record_new_project(project, user, **kwargs):
    if user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user = user_id = None
        default_user_id = Organization.objects.get(
            id=project.organization_id).get_default_owner().id

    analytics.record(
        'project.created',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        platform=project.platform,
    )

    success = OrganizationOnboardingTask.objects.record(
        organization_id=project.organization_id,
        task=OnboardingTask.FIRST_PROJECT,
        user=user,
        status=OnboardingTaskStatus.COMPLETE,
        project_id=project.id,
    )
    if not success:
        OrganizationOnboardingTask.objects.record(
            organization_id=project.organization_id,
            task=OnboardingTask.SECOND_PLATFORM,
            user=user,
            status=OnboardingTaskStatus.PENDING,
            project_id=project.id,
        )
Example #7
0
def sync_status_outbound(group_id, external_issue_id, **kwargs):
    try:
        group = Group.objects.filter(
            id=group_id,
            status__in=[GroupStatus.UNRESOLVED, GroupStatus.RESOLVED],
        )[0]
    except IndexError:
        return

    has_issue_sync = features.has('organizations:integrations-issue-sync',
                                  group.organization)
    if not has_issue_sync:
        return

    external_issue = ExternalIssue.objects.get(id=external_issue_id)
    integration = Integration.objects.get(id=external_issue.integration_id)
    installation = integration.get_installation(
        organization_id=external_issue.organization_id,
    )
    if installation.should_sync('outbound_status'):
        installation.sync_status_outbound(
            external_issue, group.status == GroupStatus.RESOLVED, group.project_id
        )
        analytics.record(
            'integration.issue.status.synced',
            provider=integration.provider,
            id=integration.id,
            organization_id=external_issue.organization_id,
        )
Example #8
0
 def record_analytics(self):
     analytics.record(
         'sentry_app.created',
         user_id=self.user.id,
         organization_id=self.organization.id,
         sentry_app=self.sentry_app.slug,
     )
Example #9
0
def record_issue_resolved(organization_id, project, group, user, resolution_type, **kwargs):
    """ There are three main types of ways to resolve issues
        1) via a release (current release, next release, or other)
        2) via commit (in the UI with the commit hash (marked as "in_commit")
            or tagging the issue in a commit (marked as "with_commit"))
        3) now
    """
    if resolution_type in ('in_next_release', 'in_release'):
        FeatureAdoption.objects.record(
            organization_id=organization_id, feature_slug="resolved_in_release", complete=True
        )
    if resolution_type == 'with_commit':
        FeatureAdoption.objects.record(
            organization_id=organization_id, feature_slug="resolved_with_commit", complete=True
        )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        'issue.resolved',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=organization_id,
        group_id=group.id,
        resolution_type=resolution_type,
    )
Example #10
0
def record_plugin_enabled(plugin, project, user, **kwargs):
    if isinstance(plugin, IssueTrackingPlugin) or isinstance(plugin, IssueTrackingPlugin2):
        task = OnboardingTask.ISSUE_TRACKER
        status = OnboardingTaskStatus.PENDING
    elif isinstance(plugin, NotificationPlugin):
        task = OnboardingTask.NOTIFICATION_SERVICE
        status = OnboardingTaskStatus.COMPLETE
    else:
        return

    success = OrganizationOnboardingTask.objects.record(
        organization_id=project.organization_id,
        task=task,
        status=status,
        user=user,
        project_id=project.id,
        data={'plugin': plugin.slug},
    )
    if success:
        check_for_onboarding_complete(project.organization_id)

    analytics.record(
        'plugin.enabled',
        user_id=user.id,
        organization_id=project.organization_id,
        project_id=project.id,
        plugin=plugin.slug,
    )
Example #11
0
    def on_assign(self, request, identity, group, action):
        assignee = action['selected_options'][0]['value']

        if assignee == 'none':
            assignee = None

        self.update_group(group, identity, {'assignedTo': assignee})
        analytics.record('integrations.slack.assign', actor_id=identity.user_id)
Example #12
0
def record_member_joined(member, organization, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=member.organization_id, feature_slug="invite_team", complete=True
    )
    analytics.record(
        'organization.joined',
        user_id=member.user.id,
        organization_id=organization.id,
    )
Example #13
0
def record_sso_enabled(organization, user, provider, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=organization.id, feature_slug="sso", complete=True
    )

    analytics.record(
        'sso.enabled',
        user_id=user.id,
        organization_id=organization.id,
        provider=provider,
    )
Example #14
0
def record_team_created(organization, user, team, **kwargs):
    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = organization.get_default_owner().id

    analytics.record(
        'team.created',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=organization.id,
        team_id=team.id,
    )
Example #15
0
def record_integration_issue_linked(integration, organization, user, **kwargs):
    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = organization.get_default_owner().id
    analytics.record(
        'integration.issue.linked',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=organization.id,
        provider=integration.provider,
        id=integration.id,
    )
Example #16
0
def record_issue_deleted(group, user, delete_type, **kwargs):
    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = group.project.organization.get_default_owner().id
    analytics.record(
        'issue.deleted',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=group.project.organization_id,
        group_id=group.id,
        delete_type=delete_type,
    )
Example #17
0
def record_member_invited(member, user, **kwargs):
    OrganizationOnboardingTask.objects.record(
        organization_id=member.organization_id,
        task=OnboardingTask.INVITE_MEMBER,
        user=user,
        status=OnboardingTaskStatus.PENDING,
        data={'invited_member_id': member.id}
    )
    analytics.record(
        'member.invited',
        invited_member_id=member.id,
        inviter_user_id=user.id,
        organization_id=member.organization_id,
        referrer=kwargs.get('referrer'))
Example #18
0
def record_issue_assigned(project, group, user, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=project.organization_id, feature_slug="assignment", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id
    analytics.record(
        'issue.assigned',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        group_id=group.id,
    )
Example #19
0
def record_save_search_created(project, user, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=project.organization_id, feature_slug="saved_search", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        'search.saved',
        user_id=user_id,
        default_user_id=default_user_id,
        project_id=project.id,
        organization_id=project.organization_id,
    )
Example #20
0
def record_release_received(project, group, event, **kwargs):
    if not event.get_tag('sentry:release'):
        return

    success = OrganizationOnboardingTask.objects.record(
        organization_id=project.organization_id,
        task=OnboardingTask.RELEASE_TRACKING,
        status=OnboardingTaskStatus.COMPLETE,
        project_id=project.id,
    )
    if success:
        user = Organization.objects.get(id=project.organization_id).get_default_owner()
        analytics.record(
            'first_release_tag.sent',
            user_id=user.id,
            project_id=project.id,
            organization_id=project.organization_id,
        )
        check_for_onboarding_complete(project.organization_id)
Example #21
0
def record_sourcemaps_received(project, group, event, **kwargs):
    if not has_sourcemap(event):
        return

    success = OrganizationOnboardingTask.objects.record(
        organization_id=project.organization_id,
        task=OnboardingTask.SOURCEMAPS,
        status=OnboardingTaskStatus.COMPLETE,
        project_id=project.id,
    )
    if success:
        user = Organization.objects.get(id=project.organization_id).get_default_owner()
        analytics.record(
            'first_sourcemaps.sent',
            user_id=user.id,
            organization_id=project.organization_id,
            project_id=project.id,
        )
        check_for_onboarding_complete(project.organization_id)
Example #22
0
def record_issue_resolved_in_release(project, group, user, resolution_type, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=project.organization_id, feature_slug="resolved_in_release", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        'issue.resolved',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        group_id=group.id,
        resolution_type=resolution_type,
    )
Example #23
0
def record_repo_linked(repo, user, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=repo.organization_id, feature_slug="repo_linked", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = Organization.objects.get(id=repo.organization_id).get_default_owner().id

    analytics.record(
        'repo.linked',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=repo.organization_id,
        repository_id=repo.id,
        provider=repo.provider,
    )
Example #24
0
def record_resolved_with_commit(organization_id, user, group, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=organization_id, feature_slug="resolved_with_commit", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = group.organization.get_default_owner().id

    analytics.record(
        'issue.resolved',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=organization_id,
        group_id=group.id,
        resolution_type='with_commit',
    )
Example #25
0
    def on_status(self, request, identity, group, action, data, integration):
        status = action['value']

        status_data = status.split(':', 1)
        status = {'status': status_data[0]}

        resolve_type = status_data[-1]

        if resolve_type == 'inNextRelease':
            status.update({'statusDetails': {'inNextRelease': True}})
        elif resolve_type == 'inCurrentRelease':
            status.update({'statusDetails': {'inRelease': 'latest'}})

        self.update_group(group, identity, status)

        analytics.record(
            'integrations.slack.status',
            status=status['status'],
            resolve_type=resolve_type,
            actor_id=identity.user_id
        )
Example #26
0
def record_alert_rule_created(user, project, rule, **kwargs):
    if rule.label == DEFAULT_RULE_LABEL and rule.data == DEFAULT_RULE_DATA:
        return

    FeatureAdoption.objects.record(
        organization_id=project.organization_id, feature_slug="alert_rules", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        'alert.created',
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        rule_id=rule.id,
        actions=[a['id'] for a in rule.data.get('actions', [])],
    )
Example #27
0
def record_alert_rule_created(user, project, rule, **kwargs):
    if rule.label == DEFAULT_RULE_LABEL and rule.data == DEFAULT_RULE_DATA:
        return

    FeatureAdoption.objects.record(organization_id=project.organization_id,
                                   feature_slug="alert_rules",
                                   complete=True)

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        "alert.created",
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        rule_id=rule.id,
        actions=[a["id"] for a in rule.data.get("actions", [])],
    )
Example #28
0
def record_first_transaction(project, event, **kwargs):
    project.update(flags=F("flags").bitor(Project.flags.has_transactions))

    OrganizationOnboardingTask.objects.record(
        organization_id=project.organization_id,
        task=OnboardingTask.FIRST_TRANSACTION,
        status=OnboardingTaskStatus.COMPLETE,
        date_completed=event.datetime,
    )

    try:
        default_user_id = project.organization.get_default_owner().id
    except IndexError:
        default_user_id = None

    analytics.record(
        "first_transaction.sent",
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        platform=project.platform,
    )
Example #29
0
    def post(self, request: Request, project: Project) -> Response:
        """
        Upload a CODEWONERS for project
        `````````````

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project to get.
        :param string raw: the raw CODEOWNERS text
        :param string codeMappingId: id of the RepositoryProjectPathConfig object
        :auth: required
        """
        if not self.has_feature(request, project):
            self.track_response_code("create", PermissionDenied.status_code)
            raise PermissionDenied

        serializer = ProjectCodeOwnerSerializer(
            context={
                "ownership": self.get_ownership(project),
                "project": project
            },
            data={**request.data},
        )
        if serializer.is_valid():
            project_codeowners = serializer.save()
            self.track_response_code("create", status.HTTP_201_CREATED)
            analytics.record(
                "codeowners.created",
                user_id=request.user.id
                if request.user and request.user.id else None,
                organization_id=project.organization_id,
                project_id=project.id,
                codeowners_id=project_codeowners.id,
            )
            return Response(serialize(project_codeowners, request.user),
                            status=status.HTTP_201_CREATED)

        self.track_response_code("create", status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #30
0
def record_issue_tracker_used(plugin, project, user, **kwargs):
    rows_affected, created = OrganizationOnboardingTask.objects.create_or_update(
        organization_id=project.organization_id,
        task=OnboardingTask.ISSUE_TRACKER,
        status=OnboardingTaskStatus.PENDING,
        values={
            "status": OnboardingTaskStatus.COMPLETE,
            "user": user,
            "project_id": project.id,
            "date_completed": timezone.now(),
            "data": {"plugin": plugin.slug},
        },
    )

    if rows_affected or created:
        try_mark_onboarding_complete(project.organization_id)

    if user and user.is_authenticated:
        user_id = default_user_id = user.id
    else:
        user_id = None
        try:
            default_user_id = project.organization.get_default_owner().id
        except IndexError:
            logging.getLogger("sentry").warning(
                "Cannot record issue tracker used for organization (%s) due to missing owners",
                project.organization_id,
            )
            return

    analytics.record(
        "issue_tracker.used",
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        issue_tracker=plugin.slug,
    )
Example #31
0
def record_user_context_received(project, group, event, **kwargs):
    user_context = event.data.get('user')
    if not user_context:
        return
    # checking to see if only ip address is being sent (our js library does this automatically)
    # testing for this in test_no_user_tracking_for_ip_address_only
    # list(d.keys()) pattern is to make this python3 safe
    elif list(user_context.keys()) != ['ip_address']:
        success = OrganizationOnboardingTask.objects.record(
            organization_id=project.organization_id,
            task=OnboardingTask.USER_CONTEXT,
            status=OnboardingTaskStatus.COMPLETE,
            project_id=project.id,
        )
        if success:
            user = Organization.objects.get(id=project.organization_id).get_default_owner()
            analytics.record(
                'first_user_context.sent',
                user_id=user.id,
                organization_id=project.organization_id,
                project_id=project.id,
            )
            check_for_onboarding_complete(project.organization_id)
    def add_organization(self, organization_id, default_auth_id=None, config=None):
        """
        Add an organization to this integration.

        Returns False if the OrganizationIntegration was not created
        """
        try:
            with transaction.atomic():
                return OrganizationIntegration.objects.create(
                    organization_id=organization_id,
                    integration_id=self.id,
                    default_auth_id=default_auth_id,
                    config=config or {},
                )
        except IntegrityError:
            return False
        else:
            analytics.record(
                'integration.added',
                provider=self.provider,
                id=self.id,
                organization_id=organization_id,
            )
Example #33
0
def post_comment(external_issue_id, data, user_id, **kwargs):
    # sync Sentry comments to an external issue
    external_issue = ExternalIssue.objects.get(id=external_issue_id)

    organization = Organization.objects.get(id=external_issue.organization_id)
    has_issue_sync = features.has('organizations:integrations-issue-sync',
                                  organization)
    if not has_issue_sync:
        return

    integration = Integration.objects.get(id=external_issue.integration_id)
    installation = integration.get_installation(
        organization_id=external_issue.organization_id,
    )
    if installation.should_sync('comment'):
        installation.create_comment(external_issue.key, user_id, data['text'])
        analytics.record(
            'integration.issue.comments.synced',
            provider=integration.provider,
            id=integration.id,
            organization_id=external_issue.organization_id,
            user_id=user_id,
        )
Example #34
0
def create_comment(external_issue_id, user_id, group_note_id, **kwargs):
    external_issue = ExternalIssue.objects.get(id=external_issue_id)
    installation = Integration.objects.get(id=external_issue.integration_id).get_installation(
        organization_id=external_issue.organization_id
    )

    if not should_comment_sync(installation, external_issue):
        return
    try:
        note = Activity.objects.get(type=Activity.NOTE, id=group_note_id)
    except Activity.DoesNotExist:
        return
    comment = installation.create_comment(external_issue.key, user_id, note)
    note.data["external_id"] = installation.get_comment_id(comment)
    note.save()
    analytics.record(
        # TODO(lb): this should be changed and/or specified?
        "integration.issue.comments.synced",
        provider=installation.model.provider,
        id=installation.model.id,
        organization_id=external_issue.organization_id,
        user_id=user_id,
    )
Example #35
0
def post_comment(external_issue_id, data, **kwargs):
    # sync Sentry comments to an external issue
    external_issue = ExternalIssue.objects.get(id=external_issue_id)

    organization = Organization.objects.get(id=external_issue.organization_id)
    has_issue_sync = features.has('organizations:integrations-issue-sync',
                                  organization)
    if not has_issue_sync:
        return

    integration = Integration.objects.get(id=external_issue.integration_id)
    installation = integration.get_installation(
        organization_id=external_issue.organization_id,
    )
    if installation.should_sync('comment'):
        installation.create_comment(
            external_issue.key, data['text'])
        analytics.record(
            'integration.issue.comments.synced',
            provider=integration.provider,
            id=integration.id,
            organization_id=external_issue.organization_id,
        )
Example #36
0
def sync_status_outbound(group_id, external_issue_id, **kwargs):
    try:
        group = Group.objects.filter(
            id=group_id,
            status__in=[GroupStatus.UNRESOLVED, GroupStatus.RESOLVED],
        )[0]
    except IndexError:
        return

    external_issue = ExternalIssue.objects.get(id=external_issue_id)
    integration = Integration.objects.get(id=external_issue.integration_id)
    installation = integration.get_installation(
        organization_id=external_issue.organization_id, )
    if installation.should_sync('outbound_status'):
        installation.sync_status_outbound(external_issue,
                                          group.status == GroupStatus.RESOLVED,
                                          group.project_id)
        analytics.record(
            'integration.issue.status.synced',
            provider=integration.provider,
            id=integration.id,
            organization_id=external_issue.organization_id,
        )
Example #37
0
def record_user_context_received(project, event, **kwargs):
    user_context = event.data.get("user")
    if not user_context:
        return
    # checking to see if only ip address is being sent (our js library does this automatically)
    # testing for this in test_no_user_tracking_for_ip_address_only
    # list(d.keys()) pattern is to make this python3 safe
    elif list(user_context.keys()) != ["ip_address"]:
        success = OrganizationOnboardingTask.objects.record(
            organization_id=project.organization_id,
            task=OnboardingTask.USER_CONTEXT,
            status=OnboardingTaskStatus.COMPLETE,
            project_id=project.id,
        )
        if success:
            user = Organization.objects.get(id=project.organization_id).get_default_owner()
            analytics.record(
                "first_user_context.sent",
                user_id=user.id,
                organization_id=project.organization_id,
                project_id=project.id,
            )
            check_for_onboarding_complete(project.organization_id)
Example #38
0
def build_comment_webhook(installation_id, issue_id, type, user_id, *args,
                          **kwargs):
    install, _, user = get_webhook_data(installation_id, issue_id, user_id)
    data = kwargs.get("data", {})
    project_slug = data.get("project_slug")
    comment_id = data.get("comment_id")
    payload = {
        "comment_id": data.get("comment_id"),
        "issue_id": issue_id,
        "project_slug": data.get("project_slug"),
        "timestamp": data.get("timestamp"),
        "comment": data.get("comment"),
    }
    send_webhooks(installation=install, event=type, data=payload, actor=user)
    # type is comment.created, comment.updated, or comment.deleted
    analytics.record(
        type,
        user_id=user_id,
        group_id=issue_id,
        project_slug=project_slug,
        installation_id=installation_id,
        comment_id=comment_id,
    )
Example #39
0
def update_comment(external_issue_id: int, user_id: int,
                   group_note_id: int) -> None:
    external_issue = ExternalIssue.objects.get(id=external_issue_id)
    installation = external_issue.get_installation()

    if not should_comment_sync(installation, external_issue):
        return

    try:
        note = Activity.objects.get(type=ActivityType.NOTE.value,
                                    id=group_note_id)
    except Activity.DoesNotExist:
        return

    installation.update_comment(external_issue.key, user_id, note)
    analytics.record(
        # TODO(lb): this should be changed and/or specified?
        "integration.issue.comments.synced",
        provider=installation.model.provider,
        id=installation.model.id,
        organization_id=external_issue.organization_id,
        user_id=user_id,
    )
    def put(self, request, project, codeowners):
        """
        Update a CodeOwners
        `````````````

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project to get.
        :pparam string codeowners_id: id of codeowners object
        :param string raw: the raw CODEOWNERS text
        :param string codeMappingId: id of the RepositoryProjectPathConfig object
        :auth: required
        """
        if not self.has_feature(request, project):
            self.track_response_code("update", PermissionDenied.status_code)
            raise PermissionDenied

        serializer = ProjectCodeOwnerSerializer(
            instance=codeowners,
            context={"ownership": self.get_ownership(project), "project": project},
            partial=True,
            data={**request.data},
        )
        if serializer.is_valid():
            updated_codeowners = serializer.save()

            analytics.record(
                "codeowners.updated",
                user_id=request.user.id if request.user and request.user.id else None,
                organization_id=project.organization_id,
                project_id=project.id,
                codeowners_id=updated_codeowners.id,
            )
            self.track_response_code("update", status.HTTP_200_OK)
            return Response(serialize(updated_codeowners, request.user), status=status.HTTP_200_OK)

        self.track_response_code("update", status.HTTP_400_BAD_REQUEST)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example #41
0
def record_alert_rule_created(
    user,
    project,
    rule,
    rule_type,
    is_api_token,
    referrer=None,
    session_id=None,
    alert_rule_ui_component=None,
    **kwargs,
):
    if rule_type == "issue" and rule.label == DEFAULT_RULE_LABEL and rule.data == DEFAULT_RULE_DATA:
        return

    FeatureAdoption.objects.record(organization_id=project.organization_id,
                                   feature_slug="alert_rules",
                                   complete=True)

    if user and user.is_authenticated:
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        "alert.created",
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        rule_id=rule.id,
        rule_type=rule_type,
        referrer=referrer,
        session_id=session_id,
        is_api_token=is_api_token,
        alert_rule_ui_component=alert_rule_ui_component,
    )
Example #42
0
def record_issue_ignored(project, user, group_list, activity_data, **kwargs):
    FeatureAdoption.objects.record(organization_id=project.organization_id,
                                   feature_slug="issue_ignored",
                                   complete=True)

    if user and user.is_authenticated:
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    for group in group_list:
        analytics.record(
            "issue.ignored",
            user_id=user_id,
            default_user_id=default_user_id,
            organization_id=project.organization_id,
            group_id=group.id,
            ignore_duration=activity_data.get("ignoreDuration"),
            ignore_count=activity_data.get("ignoreCount"),
            ignore_window=activity_data.get("ignoreWindow"),
            ignore_user_count=activity_data.get("ignoreUserCount"),
            ignore_user_window=activity_data.get("ignoreUserWindow"),
        )
Example #43
0
def record_issue_ignored(project, user, group_list, activity_data, **kwargs):
    FeatureAdoption.objects.record(
        organization_id=project.organization_id, feature_slug="issue_ignored", complete=True
    )

    if user and user.is_authenticated():
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    for group in group_list:
        analytics.record(
            'issue.ignored',
            user_id=user_id,
            default_user_id=default_user_id,
            organization_id=project.organization_id,
            group_id=group.id,
            ignore_duration=activity_data.get('ignoreDuration'),
            ignore_count=activity_data.get('ignoreCount'),
            ignore_window=activity_data.get('ignoreWindow'),
            ignore_user_count=activity_data.get('ignoreUserCount'),
            ignore_user_window=activity_data.get('ignoreUserWindow'),
        )
Example #44
0
def record_alert_rule_edited(
    user,
    project,
    rule,
    rule_type,
    is_api_token,
    **kwargs,
):
    if user and user.is_authenticated:
        user_id = default_user_id = user.id
    else:
        user_id = None
        default_user_id = project.organization.get_default_owner().id

    analytics.record(
        "alert.edited",
        user_id=user_id,
        default_user_id=default_user_id,
        organization_id=project.organization_id,
        project_id=project.id,
        rule_id=rule.id,
        rule_type=rule_type,
        is_api_token=is_api_token,
    )
Example #45
0
 def update_settings_bulk(
     self,
     notification_settings: Sequence["NotificationSetting"],
     user: Optional["User"] = None,
     team: Optional["Team"] = None,
 ) -> None:
     """
     Given a list of _valid_ notification settings as tuples of column
     values, save them to the DB. This does not execute as a transaction.
     """
     target_id = get_target_id(user, team)
     for (provider, type, scope_type, scope_identifier, value) in notification_settings:
         # A missing DB row is equivalent to DEFAULT.
         if value == NotificationSettingOptionValues.DEFAULT:
             self._filter(provider, type, scope_type, scope_identifier, [target_id]).delete()
         else:
             self._update_settings(
                 provider, type, value, scope_type, scope_identifier, target_id
             )
     analytics.record(
         "notifications.settings_updated",
         target_type="user" if user else "team",
         actor_id=target_id,
     )
Example #46
0
    def delete(self, request, organization, search_id):
        """
        Delete a saved search

        Permanently remove a saved search.

            {method} {path}

        """
        try:
            search = SavedSearch.objects.get(owner__isnull=True,
                                             organization=organization,
                                             id=search_id)
        except SavedSearch.DoesNotExist:
            raise ResourceDoesNotExist

        search.delete()
        analytics.record(
            "organization_saved_search.deleted",
            search_type=SearchType(search.type).name,
            org_id=organization.id,
            query=search.query,
        )
        return Response(status=204)
Example #47
0
    def link_identity(
        self,
        user: User,
        idp: IdentityProvider,
        external_id: str,
        should_reattach: bool = True,
        defaults: Mapping[str, Any | None] = None,
    ) -> Identity:
        """
        Link the user with the identity. If `should_reattach` is passed, handle
        the case where the user is linked to a different identity or the
        identity is linked to a different user.
        """
        defaults = {
            **(defaults or {}),
            "status": IdentityStatus.VALID,
            "date_verified": timezone.now(),
        }
        try:
            identity, created = self.get_or_create(
                idp=idp, user=user, external_id=external_id, defaults=defaults
            )
            if not created:
                identity.update(**defaults)
        except IntegrityError as e:
            if not should_reattach:
                raise e
            return self.reattach(idp, external_id, user, defaults)

        analytics.record(
            "integrations.identity_linked",
            provider="slack",
            actor_id=user.actor_id,
            actor_type="user",
        )
        return identity
Example #48
0
def create_incident(
    organization,
    type_,
    title,
    query,
    aggregation,
    date_started,
    date_detected=None,
    # TODO: Probably remove detection_uuid?
    detection_uuid=None,
    projects=None,
    groups=None,
    user=None,
    alert_rule=None,
):
    if groups:
        group_projects = [g.project for g in groups]
        if projects is None:
            projects = []
        projects = list(set(projects + group_projects))

    if date_detected is None:
        date_detected = date_started

    with transaction.atomic():
        incident = Incident.objects.create(
            organization=organization,
            detection_uuid=detection_uuid,
            status=IncidentStatus.OPEN.value,
            type=type_.value,
            title=title,
            query=query,
            aggregation=aggregation.value,
            date_started=date_started,
            date_detected=date_detected,
            alert_rule=alert_rule,
        )
        if projects:
            incident_projects = [
                IncidentProject(incident=incident, project=project)
                for project in projects
            ]
            IncidentProject.objects.bulk_create(incident_projects)
            # `bulk_create` doesn't send `post_save` signals, so we manually fire them here.
            for incident_project in incident_projects:
                post_save.send(sender=type(incident_project),
                               instance=incident_project,
                               created=True)

        if groups:
            IncidentGroup.objects.bulk_create([
                IncidentGroup(incident=incident, group=group)
                for group in groups
            ])

        create_incident_activity(incident,
                                 IncidentActivityType.DETECTED,
                                 user=user)
        analytics.record(
            "incident.created",
            incident_id=incident.id,
            organization_id=incident.organization_id,
            incident_type=type_.value,
        )

    return incident
Example #49
0
    def get(self, request, project):
        """
        List a Project's Issues
        ```````````````````````

        Return a list of issues (groups) bound to a project.  All parameters are
        supplied as query string parameters.

        A default query of ``is:unresolved`` is applied. To return results
        with other statuses send an new query value (i.e. ``?query=`` for all
        results).

        The ``statsPeriod`` parameter can be used to select the timeline
        stats which should be present. Possible values are: '' (disable),
        '24h', '14d'

        :qparam string statsPeriod: an optional stat period (can be one of
                                    ``"24h"``, ``"14d"``, and ``""``).
        :qparam bool shortIdLookup: if this is set to true then short IDs are
                                    looked up by this function as well.  This
                                    can cause the return value of the function
                                    to return an event issue of a different
                                    project which is why this is an opt-in.
                                    Set to `1` to enable.
        :qparam querystring query: an optional Sentry structured search
                                   query.  If not provided an implied
                                   ``"is:unresolved"`` is assumed.)
        :pparam string organization_slug: the slug of the organization the
                                          issues belong to.
        :pparam string project_slug: the slug of the project the issues
                                     belong to.
        :auth: required
        """
        stats_period = request.GET.get('statsPeriod')
        if stats_period not in (None, '', '24h', '14d'):
            return Response({"detail": ERR_INVALID_STATS_PERIOD}, status=400)
        elif stats_period is None:
            # default
            stats_period = '24h'
        elif stats_period == '':
            # disable stats
            stats_period = None

        serializer = functools.partial(
            StreamGroupSerializer,
            environment_func=self._get_environment_func(request, project.organization_id),
            stats_period=stats_period,
        )

        query = request.GET.get('query', '').strip()
        if query:
            matching_group = None
            matching_event = None
            if len(query) == 32:
                # check to see if we've got an event ID
                try:
                    matching_group = Group.objects.from_event_id(project, query)
                except Group.DoesNotExist:
                    pass
                else:
                    try:
                        matching_event = Event.objects.get(
                            event_id=query, project_id=project.id)
                    except Event.DoesNotExist:
                        pass
                    else:
                        Event.objects.bind_nodes([matching_event], 'data')
            elif matching_group is None:
                matching_group = get_by_short_id(
                    project.organization_id,
                    request.GET.get('shortIdLookup'),
                    query,
                )
                if matching_group is not None and matching_group.project_id != project.id:
                    matching_group = None

            if matching_group is not None:
                matching_event_environment = None

                try:
                    matching_event_environment = matching_event.get_environment().name if matching_event else None
                except Environment.DoesNotExist:
                    pass

                response = Response(
                    serialize(
                        [matching_group], request.user, serializer(
                            matching_event_id=getattr(matching_event, 'id', None),
                            matching_event_environment=matching_event_environment,
                        )
                    )
                )
                response['X-Sentry-Direct-Hit'] = '1'
                return response

        try:
            cursor_result, query_kwargs = self._search(request, project, {'count_hits': True})
        except ValidationError as exc:
            return Response({'detail': six.text_type(exc)}, status=400)

        results = list(cursor_result)

        context = serialize(results, request.user, serializer())

        # HACK: remove auto resolved entries
        if query_kwargs.get('status') == GroupStatus.UNRESOLVED:
            context = [r for r in context if r['status'] == 'unresolved']

        response = Response(context)

        self.add_cursor_headers(request, response, cursor_result)

        if results and query not in SAVED_SEARCH_QUERIES:
            advanced_search.send(project=project, sender=request.user)
            analytics.record('project_issue.searched', user_id=request.user.id,
                             organization_id=project.organization_id, project_id=project.id,
                             query=query)

        return response
Example #50
0
 def wrapped(instance, **kwargs):
     analytics.record(type, instance)
 def record_analytics(self):
     analytics.record(
         'sentry_app.token_exchanged',
         sentry_app_installation_id=self.install.id,
         exchange_type='authorization',
     )
Example #52
0
 def record_analytics(self, event_name: str, *args: Any,
                      **kwargs: Any) -> None:
     analytics.record(event_name, *args, **kwargs)
Example #53
0
    def post(self, request: Request, organization) -> Response:
        """
        Create a New Release for an Organization
        ````````````````````````````````````````
        Create a new release for the given Organization.  Releases are used by
        Sentry to improve its error reporting abilities by correlating
        first seen events with the release that might have introduced the
        problem.
        Releases are also necessary for sourcemaps and other debug features
        that require manual upload for functioning well.

        :pparam string organization_slug: the slug of the organization the
                                          release belongs to.
        :param string version: a version identifier for this release.  Can
                               be a version number, a commit hash etc.
        :param string ref: an optional commit reference.  This is useful if
                           a tagged version has been provided.
        :param url url: a URL that points to the release.  This can be the
                        path to an online interface to the sourcecode
                        for instance.
        :param array projects: a list of project slugs that are involved in
                               this release
        :param datetime dateReleased: an optional date that indicates when
                                      the release went live.  If not provided
                                      the current time is assumed.
        :param array commits: an optional list of commit data to be associated
                              with the release. Commits must include parameters
                              ``id`` (the sha of the commit), and can optionally
                              include ``repository``, ``message``, ``patch_set``,
                              ``author_name``, ``author_email``, and ``timestamp``.
                              See [release without integration example](/workflow/releases/).
        :param array refs: an optional way to indicate the start and end commits
                           for each repository included in a release. Head commits
                           must include parameters ``repository`` and ``commit``
                           (the HEAD sha). They can optionally include ``previousCommit``
                           (the sha of the HEAD of the previous release), which should
                           be specified if this is the first time you've sent commit data.
                           ``commit`` may contain a range in the form of ``previousCommit..commit``
        :auth: required
        """
        bind_organization_context(organization)
        serializer = ReleaseSerializerWithProjects(
            data=request.data, context={"organization": organization})

        with configure_scope() as scope:
            if serializer.is_valid():
                result = serializer.validated_data
                scope.set_tag("version", result["version"])

                allowed_projects = {
                    p.slug: p
                    for p in self.get_projects(request, organization)
                }

                projects = []
                for slug in result["projects"]:
                    if slug not in allowed_projects:
                        return Response(
                            {"projects": ["Invalid project slugs"]},
                            status=400)
                    projects.append(allowed_projects[slug])

                new_status = result.get("status")

                # release creation is idempotent to simplify user
                # experiences
                try:
                    release, created = Release.objects.get_or_create(
                        organization_id=organization.id,
                        version=result["version"],
                        defaults={
                            "ref": result.get("ref"),
                            "url": result.get("url"),
                            "owner": result.get("owner"),
                            "date_released": result.get("dateReleased"),
                            "status": new_status or ReleaseStatus.OPEN,
                        },
                    )
                except IntegrityError:
                    raise ConflictError(
                        "Could not create the release it conflicts with existing data",
                    )
                if created:
                    release_created.send_robust(release=release,
                                                sender=self.__class__)

                if not created and new_status is not None and new_status != release.status:
                    release.status = new_status
                    release.save()

                new_projects = []
                for project in projects:
                    created = release.add_project(project)
                    if created:
                        new_projects.append(project)

                if release.date_released:
                    for project in new_projects:
                        Activity.objects.create(
                            type=Activity.RELEASE,
                            project=project,
                            ident=Activity.get_version_ident(
                                result["version"]),
                            data={"version": result["version"]},
                            datetime=release.date_released,
                        )

                commit_list = result.get("commits")
                if commit_list:
                    try:
                        release.set_commits(commit_list)
                        self.track_set_commits_local(
                            request,
                            organization_id=organization.id,
                            project_ids=[project.id for project in projects],
                        )
                    except ReleaseCommitError:
                        raise ConflictError(
                            "Release commits are currently being processed")

                refs = result.get("refs")
                if not refs:
                    refs = [{
                        "repository": r["repository"],
                        "previousCommit": r.get("previousId"),
                        "commit": r["currentId"],
                    } for r in result.get("headCommits", [])]
                scope.set_tag("has_refs", bool(refs))
                if refs:
                    if not request.user.is_authenticated:
                        scope.set_tag("failure_reason",
                                      "user_not_authenticated")
                        return Response(
                            {
                                "refs": [
                                    "You must use an authenticated API token to fetch refs"
                                ]
                            },
                            status=400,
                        )
                    fetch_commits = not commit_list
                    try:
                        release.set_refs(refs,
                                         request.user,
                                         fetch=fetch_commits)
                    except InvalidRepository as e:
                        scope.set_tag("failure_reason", "InvalidRepository")
                        return Response({"refs": [str(e)]}, status=400)

                if not created and not new_projects:
                    # This is the closest status code that makes sense, and we want
                    # a unique 2xx response code so people can understand when
                    # behavior differs.
                    #   208 Already Reported (WebDAV; RFC 5842)
                    status = 208
                else:
                    status = 201

                analytics.record(
                    "release.created",
                    user_id=request.user.id
                    if request.user and request.user.id else None,
                    organization_id=organization.id,
                    project_ids=[project.id for project in projects],
                    user_agent=request.META.get("HTTP_USER_AGENT", ""),
                    created_status=status,
                )

                scope.set_tag("success_status", status)
                return Response(serialize(release, request.user),
                                status=status)
            scope.set_tag("failure_reason", "serializer_error")
            return Response(serializer.errors, status=400)
Example #54
0
    def apply_rule(self, rule, status):
        """
        If all conditions and filters pass, execute every action.

        :param rule: `Rule` object
        :return: void
        """
        condition_match = rule.data.get("action_match") or Rule.DEFAULT_CONDITION_MATCH
        filter_match = rule.data.get("filter_match") or Rule.DEFAULT_FILTER_MATCH
        rule_condition_list = rule.data.get("conditions", ())
        frequency = rule.data.get("frequency") or Rule.DEFAULT_FREQUENCY

        if (
            rule.environment_id is not None
            and self.event.get_environment().id != rule.environment_id
        ):
            return

        now = timezone.now()
        freq_offset = now - timedelta(minutes=frequency)
        if status.last_active and status.last_active > freq_offset:
            return

        state = self.get_state()

        condition_list = []
        filter_list = []
        for rule_cond in rule_condition_list:
            if self.get_rule_type(rule_cond) == "condition/event":
                condition_list.append(rule_cond)
            else:
                filter_list.append(rule_cond)

        # if conditions exist evaluate them, otherwise move to the filters section
        if condition_list:
            condition_iter = (self.condition_matches(c, state, rule) for c in condition_list)

            condition_func = self.get_match_function(condition_match)
            if condition_func:
                condition_passed = condition_func(condition_iter)
            else:
                self.logger.error(
                    "Unsupported condition_match %r for rule %d", condition_match, rule.id
                )
                return

            if not condition_passed:
                return

        # if filters exist evaluate them, otherwise pass
        if filter_list:
            filter_iter = (self.condition_matches(f, state, rule) for f in filter_list)
            filter_func = self.get_match_function(filter_match)
            if filter_func:
                passed = filter_func(filter_iter)
            else:
                self.logger.error("Unsupported filter_match %r for rule %d", filter_match, rule.id)
                return
        else:
            passed = True

        if passed:
            passed = (
                GroupRuleStatus.objects.filter(id=status.id)
                .exclude(last_active__gt=freq_offset)
                .update(last_active=now)
            )

        if not passed:
            return

        if randrange(10) == 0:
            analytics.record(
                "issue_alert.fired",
                issue_id=self.group.id,
                project_id=rule.project.id,
                organization_id=rule.project.organization.id,
                rule_id=rule.id,
            )

        for action in rule.data.get("actions", ()):
            action_cls = rules.get(action["id"])
            if action_cls is None:
                self.logger.warning("Unregistered action %r", action["id"])
                continue

            action_inst = action_cls(self.project, data=action, rule=rule)
            results = safe_execute(
                action_inst.after, event=self.event, state=state, _with_transaction=False
            )
            if results is None:
                self.logger.warning("Action %s did not return any futures", action["id"])
                continue

            for future in results:
                key = future.key if future.key is not None else future.callback
                rule_future = RuleFuture(rule=rule, kwargs=future.kwargs)

                if key not in self.grouped_futures:
                    self.grouped_futures[key] = (future.callback, [rule_future])
                else:
                    self.grouped_futures[key][1].append(rule_future)
Example #55
0
    def post(self, request, organization):
        data = {
            "name": request.json_body.get("name"),
            "user": request.user,
            "author": request.json_body.get("author"),
            "organization": organization,
            "webhookUrl": request.json_body.get("webhookUrl"),
            "redirectUrl": request.json_body.get("redirectUrl"),
            "isAlertable": request.json_body.get("isAlertable"),
            "isInternal": request.json_body.get("isInternal"),
            "verifyInstall": request.json_body.get("verifyInstall"),
            "scopes": request.json_body.get("scopes", []),
            "events": request.json_body.get("events", []),
            "schema": request.json_body.get("schema", {}),
            "overview": request.json_body.get("overview"),
            "allowedOrigins": request.json_body.get("allowedOrigins", []),
        }

        if self._has_hook_events(request) and not features.has(
                "organizations:integrations-event-hooks",
                organization,
                actor=request.user):

            return Response(
                {
                    "non_field_errors": [
                        "Your organization does not have access to the 'error' resource subscription."
                    ]
                },
                status=403,
            )

        serializer = SentryAppSerializer(data=data, access=request.access)

        if serializer.is_valid():
            data["redirect_url"] = data["redirectUrl"]
            data["webhook_url"] = data["webhookUrl"]
            data["is_alertable"] = data["isAlertable"]
            data["verify_install"] = data["verifyInstall"]
            data["allowed_origins"] = data["allowedOrigins"]
            data["is_internal"] = data.get("isInternal")

            creator = InternalCreator if data.get("isInternal") else Creator
            try:
                sentry_app = creator.run(request=request, **data)
            except ValidationError as e:
                # we generate and validate the slug here instead of the serializer since the slug never changes
                return Response(e.detail, status=400)

            return Response(serialize(sentry_app, access=request.access),
                            status=201)

        # log any errors with schema
        if "schema" in serializer.errors:
            for error_message in serializer.errors["schema"]:
                name = "sentry_app.schema_validation_error"
                log_info = {
                    "schema": json.dumps(data["schema"]),
                    "user_id": request.user.id,
                    "sentry_app_name": data["name"],
                    "organization_id": organization.id,
                    "error_message": error_message,
                }
                logger.info(name, extra=log_info)
                analytics.record(name, **log_info)
        return Response(serializer.errors, status=400)
Example #56
0
def record_first_event(project, event, **kwargs):
    """
    Requires up to 2 database calls, but should only run with the first event in
    any project, so performance should not be a huge bottleneck.
    """

    # If complete, pass (creation fails due to organization, task unique constraint)
    # If pending, update.
    # If does not exist, create.
    rows_affected, created = OrganizationOnboardingTask.objects.create_or_update(
        organization_id=project.organization_id,
        task=OnboardingTask.FIRST_EVENT,
        status=OnboardingTaskStatus.PENDING,
        values={
            "status": OnboardingTaskStatus.COMPLETE,
            "project_id": project.id,
            "date_completed": project.first_event,
            "data": {
                "platform": event.platform
            },
        },
    )

    user = Organization.objects.get(
        id=project.organization_id).get_default_owner()

    if rows_affected or created:
        analytics.record(
            "first_event.sent",
            user_id=user.id,
            organization_id=project.organization_id,
            project_id=project.id,
            platform=event.platform,
        )
        return

    try:
        oot = OrganizationOnboardingTask.objects.filter(
            organization_id=project.organization_id,
            task=OnboardingTask.FIRST_EVENT)[0]
    except IndexError:
        return

    # Only counts if it's a new project and platform
    if oot.project_id != project.id and oot.data.get(
            "platform", event.platform) != event.platform:
        rows_affected, created = OrganizationOnboardingTask.objects.create_or_update(
            organization_id=project.organization_id,
            task=OnboardingTask.SECOND_PLATFORM,
            status=OnboardingTaskStatus.PENDING,
            values={
                "status": OnboardingTaskStatus.COMPLETE,
                "project_id": project.id,
                "date_completed": project.first_event,
                "data": {
                    "platform": event.platform
                },
            },
        )
        if rows_affected or created:
            analytics.record(
                "second_platform.added",
                user_id=user.id,
                organization_id=project.organization_id,
                project_id=project.id,
                platform=event.platform,
            )
    def put(self, request, group, integration_id):
        if not self._has_issue_feature(group.organization, request.user):
            return Response({'detail': MISSING_FEATURE_MESSAGE}, status=400)

        external_issue_id = request.DATA.get('externalIssue')
        if not external_issue_id:
            return Response({'externalIssue': ['Issue ID is required']},
                            status=400)

        organization_id = group.project.organization_id
        try:
            integration = Integration.objects.get(
                id=integration_id,
                organizations=organization_id,
            )
        except Integration.DoesNotExist:
            return Response(status=404)

        if not (integration.has_feature(IntegrationFeatures.ISSUE_BASIC)
                or integration.has_feature(IntegrationFeatures.ISSUE_SYNC)):
            return Response(
                {
                    'detail':
                    'This feature is not supported for this integration.'
                },
                status=400)

        installation = integration.get_installation(organization_id)
        try:
            data = installation.get_issue(external_issue_id, data=request.DATA)
        except IntegrationFormError as exc:
            return Response(exc.field_errors, status=400)
        except IntegrationError as exc:
            return Response({'non_field_errors': [exc.message]}, status=400)

        defaults = {
            'title': data.get('title'),
            'description': data.get('description'),
            'metadata': data.get('metadata'),
        }

        external_issue_key = installation.make_external_key(data)
        external_issue, created = ExternalIssue.objects.get_or_create(
            organization_id=organization_id,
            integration_id=integration.id,
            key=external_issue_key,
            defaults=defaults,
        )

        if created:
            analytics.record(
                'integration.issue.linked',
                provider=integration.provider,
                id=integration.id,
                organization_id=organization_id,
            )
        else:
            external_issue.update(**defaults)

        try:
            installation.after_link_issue(external_issue, data=request.DATA)
        except IntegrationFormError as exc:
            return Response(exc.field_errors, status=400)
        except IntegrationError as exc:
            return Response({'non_field_errors': [exc.message]}, status=400)

        try:
            with transaction.atomic():
                GroupLink.objects.create(
                    group_id=group.id,
                    project_id=group.project_id,
                    linked_type=GroupLink.LinkedType.issue,
                    linked_id=external_issue.id,
                    relationship=GroupLink.Relationship.references,
                )
        except IntegrityError:
            return Response(
                {'non_field_errors': ['That issue is already linked']},
                status=400)

        # TODO(jess): would be helpful to return serialized external issue
        # once we have description, title, etc
        url = data.get('url') or installation.get_issue_url(external_issue.key)
        context = {
            'id': external_issue.id,
            'key': external_issue.key,
            'url': url,
            'integrationId': external_issue.integration_id,
            'displayName': installation.get_issue_display_name(external_issue),
        }
        return Response(context, status=201)
Example #58
0
    def dispatch(self, request, organization, **kwargs):
        try:
            config = self.get_repository_data(organization, request.data)
            result = self.build_repository_config(organization=organization,
                                                  data=config)
        except Exception as e:
            return self.handle_api_error(e)

        repo_update_params = {
            "external_id": result.get("external_id"),
            "url": result.get("url"),
            "config": result.get("config") or {},
            "provider": self.id,
            "integration_id": result.get("integration_id"),
        }

        # first check if there is a repository without an integration that matches
        repo = Repository.objects.filter(organization_id=organization.id,
                                         name=result["name"],
                                         integration_id=None).first()
        if repo:
            if self.logger:
                self.logger.info(
                    "repository.update",
                    extra={
                        "organization_id": organization.id,
                        "repo_name": result["name"],
                        "old_provider": repo.provider,
                    },
                )
            # update from params
            for field_name, field_value in six.iteritems(repo_update_params):
                setattr(repo, field_name, field_value)
            # also update the status if it was in a bad state
            repo.status = ObjectStatus.VISIBLE
            repo.save()
        else:
            try:
                with transaction.atomic():
                    repo = Repository.objects.create(
                        organization_id=organization.id,
                        name=result["name"],
                        **repo_update_params)
            except IntegrityError:
                # Try to delete webhook we just created
                try:
                    repo = Repository(organization_id=organization.id,
                                      name=result["name"],
                                      **repo_update_params)
                    self.on_delete_repository(repo)
                except IntegrationError:
                    pass
                return Response(
                    {
                        "errors": {
                            "__all__":
                            "A repository with that name already exists"
                        }
                    },
                    status=400,
                )
            else:
                repo_linked.send_robust(repo=repo,
                                        user=request.user,
                                        sender=self.__class__)

        analytics.record(
            "integration.repo.added",
            provider=self.id,
            id=result.get("integration_id"),
            organization_id=organization.id,
        )
        return Response(serialize(repo, request.user), status=201)
Example #59
0
def create_incident(
    organization,
    type,
    title,
    query,
    aggregation,
    date_started=None,
    date_detected=None,
    # TODO: Probably remove detection_uuid?
    detection_uuid=None,
    projects=None,
    groups=None,
    user=None,
    alert_rule=None,
):
    if groups:
        group_projects = [g.project for g in groups]
        if projects is None:
            projects = []
        projects = list(set(projects + group_projects))

    if date_started is None:
        date_started = calculate_incident_start(query, projects, groups)

    if date_detected is None:
        date_detected = date_started

    with transaction.atomic():
        incident = Incident.objects.create(
            organization=organization,
            detection_uuid=detection_uuid,
            status=IncidentStatus.OPEN.value,
            type=type.value,
            title=title,
            query=query,
            aggregation=aggregation.value,
            date_started=date_started,
            date_detected=date_detected,
            alert_rule=alert_rule,
        )
        if projects:
            IncidentProject.objects.bulk_create([
                IncidentProject(incident=incident, project=project)
                for project in projects
            ])
        if groups:
            IncidentGroup.objects.bulk_create([
                IncidentGroup(incident=incident, group=group)
                for group in groups
            ])

        if type == IncidentType.CREATED:
            activity_status = IncidentActivityType.CREATED
        else:
            activity_status = IncidentActivityType.DETECTED

        event_stats_snapshot = create_initial_event_stats_snapshot(incident)
        create_incident_activity(incident,
                                 activity_status,
                                 event_stats_snapshot=event_stats_snapshot,
                                 user=user)
        analytics.record(
            "incident.created",
            incident_id=incident.id,
            organization_id=incident.organization_id,
            incident_type=type.value,
        )

    tasks.calculate_incident_suspects.apply_async(
        kwargs={"incident_id": incident.id})
    return incident