Ejemplo n.º 1
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.features.base import OrganizationFeature

        if attrs.get("avatar"):
            avatar = {
                "avatarType":
                attrs["avatar"].get_avatar_type_display(),
                "avatarUuid":
                attrs["avatar"].ident if attrs["avatar"].file_id else None,
            }
        else:
            avatar = {"avatarType": "letter_avatar", "avatarUuid": None}

        status = OrganizationStatus(obj.status)

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith("organizations:"):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len("organizations:"):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if ("onboarding" in feature_list and
                OrganizationOption.objects.filter(organization=obj).exists()):
            feature_list.remove("onboarding")

        # Include api-keys feature if they previously had any api-keys
        if "api-keys" not in feature_list and ApiKey.objects.filter(
                organization=obj).exists():
            feature_list.add("api-keys")

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add("legacy-rate-limits")
        if getattr(obj.flags, "allow_joinleave"):
            feature_list.add("open-membership")
        if not getattr(obj.flags, "disable_shared_issues"):
            feature_list.add("shared-issues")

        return {
            "id": six.text_type(obj.id),
            "slug": obj.slug,
            "status": {
                "id": status.name.lower(),
                "name": status.label
            },
            "name": obj.name or obj.slug,
            "dateCreated": obj.date_added,
            "isEarlyAdopter": bool(obj.flags.early_adopter),
            "require2FA": bool(obj.flags.require_2fa),
            "avatar": avatar,
            "features": feature_list,
        }
Ejemplo n.º 2
0
    def _get_features_for_projects(all_projects, user):
        # Arrange to call features.has_for_batch rather than features.has
        # for performance's sake
        projects_by_org = defaultdict(list)
        for project in all_projects:
            projects_by_org[project.organization].append(project)

        features_by_project = defaultdict(list)
        for feature_name in features.all(feature_type=ProjectFeature).keys():
            if not feature_name.startswith(_PROJECT_SCOPE_PREFIX):
                continue
            abbreviated_feature = feature_name[len(_PROJECT_SCOPE_PREFIX):]
            for (organization, projects) in projects_by_org.items():
                result = features.has_for_batch(feature_name, organization,
                                                projects, user)
                for (project, flag) in result.items():
                    if flag:
                        features_by_project[project].append(
                            abbreviated_feature)

        for project in all_projects:
            if project.flags.has_releases:
                features_by_project[project].append("releases")

        return features_by_project
Ejemplo n.º 3
0
    def get_feature_list(self, obj, user):
        from sentry import features
        from sentry.features.base import ProjectFeature

        if not self.include_features:
            return None

        with sentry_sdk.start_span(
            op="project_feature_list", description=getattr(obj, "name")
        ) as span:
            # Retrieve all registered organization features
            project_features = features.all(feature_type=ProjectFeature).keys()
            feature_list = set()

            for feature_name in project_features:
                if not feature_name.startswith("projects:"):
                    continue
                if features.has(feature_name, obj, actor=user):
                    # Remove the project scope prefix
                    feature_list.add(feature_name[len("projects:") :])

            if obj.flags.has_releases:
                feature_list.add("releases")

            span.set_data("Feature Count", len(feature_list))

            return feature_list
Ejemplo n.º 4
0
def get_features_for_projects(
        all_projects: Sequence[Project],
        user: User) -> MutableMapping[Project, List[str]]:
    # Arrange to call features.has_for_batch rather than features.has
    # for performance's sake
    projects_by_org = defaultdict(list)
    for project in all_projects:
        projects_by_org[project.organization].append(project)

    features_by_project = defaultdict(list)
    project_features = [
        feature
        for feature in features.all(feature_type=ProjectFeature).keys()
        if feature.startswith(_PROJECT_SCOPE_PREFIX)
    ]

    batch_checked = set()
    for (organization, projects) in projects_by_org.items():
        batch_features = features.batch_has(project_features,
                                            actor=user,
                                            projects=projects,
                                            organization=organization)

        # batch_has has found some features
        if batch_features:
            for project in projects:
                for feature_name, active in batch_features.get(
                        f"project:{project.id}", {}).items():
                    if active:
                        features_by_project[project].append(
                            feature_name[len(_PROJECT_SCOPE_PREFIX):])

                    batch_checked.add(feature_name)

    for feature_name in project_features:
        if feature_name in batch_checked:
            continue
        abbreviated_feature = feature_name[len(_PROJECT_SCOPE_PREFIX):]
        for (organization, projects) in projects_by_org.items():
            result = features.has_for_batch(feature_name, organization,
                                            projects, user)
            for (project, flag) in result.items():
                if flag:
                    features_by_project[project].append(abbreviated_feature)

    for project in all_projects:
        if project.flags.has_releases:
            features_by_project[project].append("releases")

    return features_by_project
Ejemplo n.º 5
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.features.base import ProjectFeature

        # Retrieve all registered organization features
        project_features = features.all(feature_type=ProjectFeature).keys()
        feature_list = set()

        for feature_name in project_features:
            if not feature_name.startswith('projects:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the project scope prefix
                feature_list.add(feature_name[len('projects:'):])

        if obj.flags.has_releases:
            feature_list.add('releases')

        status_label = STATUS_LABELS.get(obj.status, 'unknown')

        if attrs.get('avatar'):
            avatar = {
                'avatarType': attrs['avatar'].get_avatar_type_display(),
                'avatarUuid': attrs['avatar'].ident if attrs['avatar'].file_id else None
            }
        else:
            avatar = {'avatarType': 'letter_avatar', 'avatarUuid': None}

        context = {
            'id': six.text_type(obj.id),
            'slug': obj.slug,
            'name': obj.name,
            'isPublic': obj.public,
            'isBookmarked': attrs['is_bookmarked'],
            'color': obj.color,
            'dateCreated': obj.date_added,
            'firstEvent': obj.first_event,
            'features': feature_list,
            'status': status_label,
            'platform': obj.platform,
            'isInternal': obj.is_internal_project(),
            'isMember': attrs['is_member'],
            'hasAccess': attrs['has_access'],
            'avatar': avatar,
        }
        if 'stats' in attrs:
            context['stats'] = attrs['stats']
        return context
Ejemplo n.º 6
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.features.base import ProjectFeature

        # Retrieve all registered organization features
        project_features = features.all(feature_type=ProjectFeature).keys()
        feature_list = set()

        for feature_name in project_features:
            if not feature_name.startswith('projects:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the project scope prefix
                feature_list.add(feature_name[len('projects:'):])

        if obj.flags.has_releases:
            feature_list.add('releases')

        status_label = STATUS_LABELS.get(obj.status, 'unknown')

        if attrs.get('avatar'):
            avatar = {
                'avatarType': attrs['avatar'].get_avatar_type_display(),
                'avatarUuid': attrs['avatar'].ident if attrs['avatar'].file_id else None
            }
        else:
            avatar = {'avatarType': 'letter_avatar', 'avatarUuid': None}

        context = {
            'id': six.text_type(obj.id),
            'slug': obj.slug,
            'name': obj.name,
            'isPublic': obj.public,
            'isBookmarked': attrs['is_bookmarked'],
            'color': obj.color,
            'dateCreated': obj.date_added,
            'firstEvent': obj.first_event,
            'features': feature_list,
            'status': status_label,
            'platform': obj.platform,
            'isInternal': obj.is_internal_project(),
            'isMember': attrs['is_member'],
            'hasAccess': attrs['has_access'],
            'avatar': avatar,
        }
        if 'stats' in attrs:
            context['stats'] = attrs['stats']
        return context
Ejemplo n.º 7
0
    def get_feature_list(self, obj, user):
        from sentry import features
        from sentry.features.base import ProjectFeature

        # Retrieve all registered organization features
        project_features = features.all(feature_type=ProjectFeature).keys()
        feature_list = set()

        for feature_name in project_features:
            if not feature_name.startswith("projects:"):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the project scope prefix
                feature_list.add(feature_name[len("projects:"):])

        if obj.flags.has_releases:
            feature_list.add("releases")
        return feature_list
Ejemplo n.º 8
0
    def get_feature_list(self, obj, user):
        from sentry import features
        from sentry.features.base import ProjectFeature

        # Retrieve all registered organization features
        project_features = features.all(feature_type=ProjectFeature).keys()
        feature_list = set()

        for feature_name in project_features:
            if not feature_name.startswith('projects:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the project scope prefix
                feature_list.add(feature_name[len('projects:'):])

        if obj.flags.has_releases:
            feature_list.add('releases')
        return feature_list
Ejemplo n.º 9
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.features.base import OrganizationFeature

        if attrs.get('avatar'):
            avatar = {
                'avatarType':
                attrs['avatar'].get_avatar_type_display(),
                'avatarUuid':
                attrs['avatar'].ident if attrs['avatar'].file_id else None
            }
        else:
            avatar = {
                'avatarType': 'letter_avatar',
                'avatarUuid': None,
            }

        status = OrganizationStatus(obj.status)

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(
                organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.add('require-2fa')

        return {
            'id':
            six.text_type(obj.id),
            'slug':
            obj.slug,
            'status': {
                'id': status.name.lower(),
                'name': status.label,
            },
            'name':
            obj.name or obj.slug,
            'dateCreated':
            obj.date_added,
            'isEarlyAdopter':
            bool(obj.flags.early_adopter),
            'disableNewVisibilityFeatures':
            bool(obj.flags.disable_new_visibility_features),
            'require2FA':
            bool(obj.flags.require_2fa),
            'avatar':
            avatar,
            'features':
            feature_list
        }
Ejemplo n.º 10
0
    def get_attrs(self, item_list, user):
        def measure_span(op_tag):
            span = sentry_sdk.start_span(
                op="serialize.get_attrs.project.{}".format(op_tag))
            span.set_data("Object Count", len(item_list))
            return span

        with measure_span("preamble"):
            project_ids = [i.id for i in item_list]
            if user.is_authenticated() and item_list:
                bookmarks = set(
                    ProjectBookmark.objects.filter(
                        user=user,
                        project_id__in=project_ids).values_list("project_id",
                                                                flat=True))
                user_options = {
                    (u.project_id, u.key): u.value
                    for u in UserOption.objects.filter(
                        Q(user=user, project__in=item_list, key="mail:alert")
                        | Q(user=user,
                            key="subscribe_by_default",
                            project__isnull=True))
                }
                default_subscribe = user_options.get("subscribe_by_default",
                                                     "1") == "1"
            else:
                bookmarks = set()
                user_options = {}
                default_subscribe = False

            if self.stats_period:
                # we need to compute stats at 1d (1h resolution), and 14d
                project_ids = [o.id for o in item_list]

                segments, interval = STATS_PERIOD_CHOICES[self.stats_period]
                now = timezone.now()
                stats = tsdb.get_range(
                    model=tsdb.models.project,
                    keys=project_ids,
                    end=now,
                    start=now - ((segments - 1) * interval),
                    rollup=int(interval.total_seconds()),
                    environment_ids=self.environment_id
                    and [self.environment_id],
                )
            else:
                stats = None

            avatars = {
                a.project_id: a
                for a in ProjectAvatar.objects.filter(project__in=item_list)
            }
            project_ids = [i.id for i in item_list]
            platforms = ProjectPlatform.objects.filter(
                project_id__in=project_ids).values_list(
                    "project_id", "platform")
            platforms_by_project = defaultdict(list)
            for project_id, platform in platforms:
                platforms_by_project[project_id].append(platform)

        with measure_span("access"):
            result = self.get_access_by_project(item_list, user)

        with measure_span("features"):
            project_features = features.all(feature_type=ProjectFeature).keys()
            feature_checker = features.build_checker()
            for item in item_list:
                result[item]["features"] = self.get_feature_list(
                    item, user, project_features, feature_checker)

        with measure_span("other"):
            for item in item_list:
                result[item].update({
                    "is_bookmarked":
                    item.id in bookmarks,
                    "is_subscribed":
                    bool(
                        user_options.get((item.id, "mail:alert"),
                                         default_subscribe)),
                    "avatar":
                    avatars.get(item.id),
                    "platforms":
                    platforms_by_project[item.id],
                })
                if stats:
                    result[item]["stats"] = stats[item.id]
        return result
Ejemplo n.º 11
0
    def serialize(self, obj, attrs, user):
        from sentry import features, experiments
        from sentry.features.base import OrganizationFeature
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = sorted(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for team in team_list:
            team._organization_cache = obj

        project_list = sorted(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.add('require-2fa')

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
Ejemplo n.º 12
0
    def serialize(self, obj, attrs, user):
        from sentry import features, experiments
        from sentry.features.base import OrganizationFeature
        from sentry.app import env
        from sentry.api.serializers.models.project import ProjectSummarySerializer
        from sentry.api.serializers.models.team import TeamSerializer

        team_list = sorted(Team.objects.filter(
            organization=obj,
            status=TeamStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for team in team_list:
            team._organization_cache = obj

        project_list = sorted(Project.objects.filter(
            organization=obj,
            status=ProjectStatus.VISIBLE,
        ), key=lambda x: x.slug)

        for project in project_list:
            project._organization_cache = obj

        onboarding_tasks = list(
            OrganizationOnboardingTask.objects.filter(
                organization=obj,
            ).select_related('user')
        )

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):  # noqa: B009
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):  # noqa: B009
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):  # noqa: B009
            feature_list.add('require-2fa')

        experiment_assignments = experiments.all(org=obj, actor=user)

        context = super(DetailedOrganizationSerializer, self).serialize(obj, attrs, user)
        max_rate = quotas.get_maximum_quota(obj)
        context['experiments'] = experiment_assignments
        context['quota'] = {
            'maxRate': max_rate[0],
            'maxRateInterval': max_rate[1],
            'accountLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:account-rate-limit',
                    default=ACCOUNT_RATE_LIMIT_DEFAULT,
                )
            ),
            'projectLimit': int(
                OrganizationOption.objects.get_value(
                    organization=obj,
                    key='sentry:project-rate-limit',
                    default=PROJECT_RATE_LIMIT_DEFAULT,
                )
            ),
        }

        context.update({
            'isDefault': obj.is_default,
            'defaultRole': obj.default_role,
            'availableRoles': [{
                'id': r.id,
                'name': r.name,
            } for r in roles.get_all()],
            'openMembership': bool(obj.flags.allow_joinleave),
            'require2FA': bool(obj.flags.require_2fa),
            'allowSharedIssues': not obj.flags.disable_shared_issues,
            'enhancedPrivacy': bool(obj.flags.enhanced_privacy),
            'dataScrubber': bool(obj.get_option('sentry:require_scrub_data', REQUIRE_SCRUB_DATA_DEFAULT)),
            'dataScrubberDefaults': bool(obj.get_option('sentry:require_scrub_defaults', REQUIRE_SCRUB_DEFAULTS_DEFAULT)),
            'sensitiveFields': obj.get_option('sentry:sensitive_fields', SENSITIVE_FIELDS_DEFAULT) or [],
            'safeFields': obj.get_option('sentry:safe_fields', SAFE_FIELDS_DEFAULT) or [],
            'storeCrashReports': bool(obj.get_option('sentry:store_crash_reports', STORE_CRASH_REPORTS_DEFAULT)),
            'scrubIPAddresses': bool(obj.get_option('sentry:require_scrub_ip_address', REQUIRE_SCRUB_IP_ADDRESS_DEFAULT)),
            'scrapeJavaScript': bool(obj.get_option('sentry:scrape_javascript', SCRAPE_JAVASCRIPT_DEFAULT)),
            'trustedRelays': obj.get_option('sentry:trusted-relays', TRUSTED_RELAYS_DEFAULT) or [],
        })
        context['teams'] = serialize(team_list, user, TeamSerializer())
        context['projects'] = serialize(project_list, user, ProjectSummarySerializer())
        if env.request:
            context['access'] = access.from_request(env.request, obj).scopes
        else:
            context['access'] = access.from_user(user, obj).scopes
        context['features'] = feature_list
        context['pendingAccessRequests'] = OrganizationAccessRequest.objects.filter(
            team__organization=obj,
        ).count()
        context['onboardingTasks'] = serialize(onboarding_tasks, user, OnboardingTasksSerializer())
        return context
Ejemplo n.º 13
0
    def serialize(
        self, obj: Organization, attrs: Mapping[str, Any], user: User
    ) -> OrganizationSerializerResponse:
        from sentry import features
        from sentry.features.base import OrganizationFeature

        if attrs.get("avatar"):
            avatar = {
                "avatarType": attrs["avatar"].get_avatar_type_display(),
                "avatarUuid": attrs["avatar"].ident if attrs["avatar"].file_id else None,
            }
        else:
            avatar = {"avatarType": "letter_avatar", "avatarUuid": None}

        status = OrganizationStatus(obj.status)

        # Retrieve all registered organization features
        org_features = [
            feature
            for feature in features.all(feature_type=OrganizationFeature).keys()
            if feature.startswith(_ORGANIZATION_SCOPE_PREFIX)
        ]
        feature_list = set()

        # Check features in batch using the entity handler
        batch_features = features.batch_has(org_features, actor=user, organization=obj)

        # batch_has has found some features
        if batch_features:
            for feature_name, active in batch_features.get(f"organization:{obj.id}", {}).items():
                if active:
                    # Remove organization prefix
                    feature_list.add(feature_name[len(_ORGANIZATION_SCOPE_PREFIX) :])

                # This feature_name was found via `batch_has`, don't check again using `has`
                org_features.remove(feature_name)

        # Remaining features should not be checked via the entity handler
        for feature_name in org_features:
            if features.has(feature_name, obj, actor=user, skip_entity=True):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len(_ORGANIZATION_SCOPE_PREFIX) :])

        # Do not include the onboarding feature if OrganizationOptions exist
        if (
            "onboarding" in feature_list
            and OrganizationOption.objects.filter(organization=obj).exists()
        ):
            feature_list.remove("onboarding")

        # Include api-keys feature if they previously had any api-keys
        if "api-keys" not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add("api-keys")

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
            organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS
        ).exists():
            feature_list.add("legacy-rate-limits")
        if getattr(obj.flags, "allow_joinleave"):
            feature_list.add("open-membership")
        if not getattr(obj.flags, "disable_shared_issues"):
            feature_list.add("shared-issues")

        return {
            "id": str(obj.id),
            "slug": obj.slug,
            "status": {"id": status.name.lower(), "name": status.label},
            "name": obj.name or obj.slug,
            "dateCreated": obj.date_added,
            "isEarlyAdopter": bool(obj.flags.early_adopter),
            "require2FA": bool(obj.flags.require_2fa),
            "requireEmailVerification": bool(
                features.has("organizations:required-email-verification", obj)
                and obj.flags.require_email_verification
            ),
            "avatar": avatar,
            "features": feature_list,
        }
Ejemplo n.º 14
0
    def serialize(self, obj, attrs, user):
        from sentry import features
        from sentry.features.base import OrganizationFeature

        if attrs.get('avatar'):
            avatar = {
                'avatarType': attrs['avatar'].get_avatar_type_display(),
                'avatarUuid': attrs['avatar'].ident if attrs['avatar'].file_id else None
            }
        else:
            avatar = {
                'avatarType': 'letter_avatar',
                'avatarUuid': None,
            }

        status = OrganizationStatus(obj.status)

        # Retrieve all registered organization features
        org_features = features.all(feature_type=OrganizationFeature).keys()
        feature_list = set()

        for feature_name in org_features:
            if not feature_name.startswith('organizations:'):
                continue
            if features.has(feature_name, obj, actor=user):
                # Remove the organization scope prefix
                feature_list.add(feature_name[len('organizations:'):])

        # Do not include the onboarding feature if OrganizationOptions exist
        if 'onboarding' in feature_list and \
                OrganizationOption.objects.filter(organization=obj).exists():
            feature_list.remove('onboarding')

        # Include api-keys feature if they previously had any api-keys
        if 'api-keys' not in feature_list and ApiKey.objects.filter(organization=obj).exists():
            feature_list.add('api-keys')

        # Organization flag features (not provided through the features module)
        if OrganizationOption.objects.filter(
                organization=obj, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists():
            feature_list.add('legacy-rate-limits')
        if getattr(obj.flags, 'allow_joinleave'):
            feature_list.add('open-membership')
        if not getattr(obj.flags, 'disable_shared_issues'):
            feature_list.add('shared-issues')
        if getattr(obj.flags, 'require_2fa'):
            feature_list.add('require-2fa')

        return {
            'id': six.text_type(obj.id),
            'slug': obj.slug,
            'status': {
                'id': status.name.lower(),
                'name': status.label,
            },
            'name': obj.name or obj.slug,
            'dateCreated': obj.date_added,
            'isEarlyAdopter': bool(obj.flags.early_adopter),
            'disableNewVisibilityFeatures': bool(obj.flags.disable_new_visibility_features),
            'require2FA': bool(obj.flags.require_2fa),
            'avatar': avatar,
            'features': feature_list
        }