Esempio n. 1
0
 def serialize(self, obj: Integration, attrs: Mapping[str, Any], user: User,
               **kwargs: Any) -> MutableMapping[str, JSONData]:
     provider = obj.get_provider()
     return {
         "id": str(obj.id),
         "name": obj.name,
         "icon": obj.metadata.get("icon"),
         "domainName": obj.metadata.get("domain_name"),
         "accountType": obj.metadata.get("account_type"),
         "status": obj.get_status_display(),
         "provider": serialize_provider(provider),
     }
Esempio n. 2
0
    def serialize(
        self,
        obj: Integration,
        attrs: Mapping[str, Any],
        user: User,
        include_config: bool = True,
        **kwargs: Any,
    ) -> MutableMapping[str, JSONData]:
        data = super().serialize(obj, attrs, user)

        if not include_config:
            return data

        data.update({"configOrganization": []})

        try:
            install = obj.get_installation(
                organization_id=self.organization_id)
        except NotImplementedError:
            # The integration may not implement a Installed Integration object
            # representation.
            pass
        else:
            data.update(
                {"configOrganization": install.get_organization_config()})

            # Query param "action" only attached in TicketRuleForm modal.
            if self.params.get("action") == "create":
                data["createIssueConfig"] = install.get_create_issue_config(
                    None, user, params=self.params)

        return data
Esempio n. 3
0
    def serialize(
        self, obj: Integration, attrs: Mapping[str, Any], user: User, include_config: bool = True
    ) -> MutableMapping[str, JSONData]:
        # XXX(epurkhiser): This is O(n) for integrations, especially since
        # we're using the IntegrationConfigSerializer which pulls in the
        # integration installation config object which very well may be making
        # API request for config options.
        integration: MutableMapping[str, Any] = serialize(
            objects=obj.integration,
            user=user,
            serializer=IntegrationConfigSerializer(obj.organization.id, params=self.params),
            include_config=include_config,
        )

        dynamic_display_information = None
        config_data = None

        try:
            installation = obj.integration.get_installation(obj.organization_id)
        except NotImplementedError:
            # slack doesn't have an installation implementation
            config_data = obj.config if include_config else None
        else:
            try:
                # just doing this to avoid querying for an object we already have
                installation._org_integration = obj
                config_data = installation.get_config_data() if include_config else None
                dynamic_display_information = installation.get_dynamic_display_information()
            except ApiError as e:
                # If there is an ApiError from our 3rd party integration
                # providers, assume there is an problem with the configuration
                # and set it to disabled.
                integration.update({"status": "disabled"})
                name = "sentry.serializers.model.organizationintegration"
                log_info = {
                    "error": str(e),
                    "integration_id": obj.integration.id,
                    "integration_provider": obj.integration.provider,
                }
                logger.info(name, extra=log_info)

        integration.update(
            {
                "configData": config_data,
                "externalId": obj.integration.external_id,
                "organizationId": obj.organization.id,
                "organizationIntegrationStatus": obj.get_status_display(),
                "gracePeriodEnd": obj.grace_period_end,
            }
        )

        if dynamic_display_information:
            integration.update({"dynamicDisplayInformation": dynamic_display_information})

        return integration
Esempio n. 4
0
    def _handle_delete(self, event: Mapping[str, Any],
                       integration: Integration) -> None:
        organizations = integration.organizations.all()

        logger.info(
            "InstallationEventWebhook._handle_delete",
            extra={
                "external_id": event["installation"]["id"],
                "integration_id": integration.id,
                "organization_id_list": organizations.values_list("id",
                                                                  flat=True),
            },
        )

        integration.update(status=ObjectStatus.DISABLED)

        Repository.objects.filter(
            organization_id__in=organizations.values_list("id", flat=True),
            provider=f"integrations:{self.provider}",
            integration_id=integration.id,
        ).update(status=ObjectStatus.DISABLED)
Esempio n. 5
0
    def serialize(
        self, obj: Integration, attrs: Mapping[str, Any], user: User, **kwargs: Any
    ) -> MutableMapping[str, JSONData]:
        data = super().serialize(obj, attrs, user)
        organization_id = kwargs.pop("organization_id")
        installation = obj.get_installation(organization_id)

        if self.action == "link":
            config = installation.get_link_issue_config(self.group, params=self.params)
            data["linkIssueConfig"] = config

        if self.action == "create":
            config = installation.get_create_issue_config(self.group, user, params=self.params)
            data["createIssueConfig"] = config

        return data
Esempio n. 6
0
def handle_status_change(
    integration: Integration,
    external_issue_key: str,
    status_change: Mapping[str, str] | None,
    project: str | None,
) -> None:
    if status_change is None:
        return

    for installation in integration.get_installations():
        installation.sync_status_inbound(
            external_issue_key,
            {
                "new_state": status_change["newValue"],
                # old_state is None when the issue is New
                "old_state": status_change.get("oldValue"),
                "project": project,
            },
        )
Esempio n. 7
0
def where_should_sync(
    integration: Integration,
    key: str,
    organization_id: int | None = None,
) -> Sequence[Organization]:
    """
    Given an integration, get the list of organizations where the sync type in
    `key` is enabled. If an optional `organization_id` is passed, then only
    check the integration for that organization.
    """
    kwargs = dict()
    if organization_id:
        kwargs["id"] = organization_id

    return [
        organization
        for organization in integration.organizations.filter(**kwargs)
        if features.has("organizations:integrations-issue-sync", organization)
        and integration.get_installation(organization.id).should_sync(key)
    ]
Esempio n. 8
0
    def handle_status_change(
        self,
        integration: Integration,
        external_issue_key: str,
        status_change: Optional[Mapping[str, str]],
        project: Optional[str],
    ) -> None:
        if status_change is None:
            return

        organization_ids = OrganizationIntegration.objects.filter(
            integration_id=integration.id).values_list("organization_id",
                                                       flat=True)

        for organization_id in organization_ids:
            installation = integration.get_installation(organization_id)
            data = {
                "new_state": status_change["newValue"],
                # old_state is None when the issue is New
                "old_state": status_change.get("oldValue"),
                "project": project,
            }

            installation.sync_status_inbound(external_issue_key, data)
Esempio n. 9
0
    def _handle(
        self,
        integration: Integration,
        event: Mapping[str, Any],
        organization: Organization,
        repo: Repository,
        host: str | None = None,
    ) -> None:
        authors = {}
        client = integration.get_installation(
            organization_id=organization.id).get_client()
        gh_username_cache: MutableMapping[str, str | None] = {}

        for commit in event["commits"]:
            if not commit["distinct"]:
                continue

            if self.should_ignore_commit(commit):
                continue

            author_email = commit["author"]["email"]
            if "@" not in author_email:
                author_email = f"{author_email[:65]}@localhost"
            # try to figure out who anonymous emails are
            elif self.is_anonymous_email(author_email):
                gh_username: str | None = commit["author"].get("username")
                # bot users don't have usernames
                if gh_username:
                    external_id = self.get_external_id(gh_username)
                    if gh_username in gh_username_cache:
                        author_email = gh_username_cache[
                            gh_username] or author_email
                    else:
                        try:
                            commit_author = CommitAuthor.objects.get(
                                external_id=external_id,
                                organization_id=organization.id)
                        except CommitAuthor.DoesNotExist:
                            commit_author = None

                        if commit_author is not None and not self.is_anonymous_email(
                                commit_author.email):
                            author_email = commit_author.email
                            gh_username_cache[gh_username] = author_email
                        else:
                            try:
                                gh_user = client.get_user(gh_username)
                            except ApiError as exc:
                                logger.exception(str(exc))
                            else:
                                # even if we can't find a user, set to none so we
                                # don't re-query
                                gh_username_cache[gh_username] = None
                                try:
                                    identity = Identity.objects.get(
                                        external_id=gh_user["id"],
                                        idp__type=self.provider,
                                        idp__external_id=self.
                                        get_idp_external_id(integration, host),
                                    )
                                except Identity.DoesNotExist:
                                    pass
                                else:
                                    author_email = identity.user.email
                                    gh_username_cache[
                                        gh_username] = author_email
                                    if commit_author is not None:
                                        try:
                                            with transaction.atomic():
                                                commit_author.update(
                                                    email=author_email,
                                                    external_id=external_id)
                                        except IntegrityError:
                                            pass

                        if commit_author is not None:
                            authors[author_email] = commit_author

            # TODO(dcramer): we need to deal with bad values here, but since
            # its optional, lets just throw it out for now
            if len(author_email) > 75:
                author = None
            elif author_email not in authors:
                authors[
                    author_email] = author = CommitAuthor.objects.get_or_create(
                        organization_id=organization.id,
                        email=author_email,
                        defaults={"name": commit["author"]["name"][:128]},
                    )[0]

                update_kwargs = {}

                if author.name != commit["author"]["name"][:128]:
                    update_kwargs["name"] = commit["author"]["name"][:128]

                gh_username = commit["author"].get("username")
                if gh_username:
                    external_id = self.get_external_id(gh_username)
                    if author.external_id != external_id and not self.is_anonymous_email(
                            author.email):
                        update_kwargs["external_id"] = external_id

                if update_kwargs:
                    try:
                        with transaction.atomic():
                            author.update(**update_kwargs)
                    except IntegrityError:
                        pass
            else:
                author = authors[author_email]

            try:
                with transaction.atomic():
                    c = Commit.objects.create(
                        repository_id=repo.id,
                        organization_id=organization.id,
                        key=commit["id"],
                        message=commit["message"],
                        author=author,
                        date_added=parse_date(commit["timestamp"]).astimezone(
                            timezone.utc),
                    )
                    for fname in commit["added"]:
                        CommitFileChange.objects.create(
                            organization_id=organization.id,
                            commit=c,
                            filename=fname,
                            type="A")
                    for fname in commit["removed"]:
                        CommitFileChange.objects.create(
                            organization_id=organization.id,
                            commit=c,
                            filename=fname,
                            type="D")
                    for fname in commit["modified"]:
                        CommitFileChange.objects.create(
                            organization_id=organization.id,
                            commit=c,
                            filename=fname,
                            type="M")
            except IntegrityError:
                pass