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, }
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
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
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
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
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
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
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
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 }
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
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
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
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, }
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 }