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 get_features(self, organization: Organization, request: Request) -> Mapping[str, bool]: feature_names = [ "organizations:performance-chart-interpolation", "organizations:discover-use-snql", "organizations:performance-use-metrics", ] batch_features = features.batch_has( feature_names, organization=organization, actor=request.user, ) return (batch_features.get(f"organization:{organization.id}", {}) if batch_features is not None else { feature_name: features.has(feature_name, organization=organization, actor=request.user) for feature_name in feature_names })
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 = [ feature for feature in features.all( feature_type=OrganizationFeature).keys() if feature.startswith(_ORGANIZATION_SCOPE_PREFIX) ] feature_list = set() 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( "organization:{}".format(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) for feature_name in org_features: if features.has(feature_name, obj, actor=user): # 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": 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, }