def update_project_symbol_source(self, project: Project, allow_multiple: bool) -> json.JSONData: """Updates this configuration in the Project's symbol sources. If a symbol source of type ``appStoreConnect`` already exists the ID must match and it will be updated. If no ``appStoreConnect`` source exists yet it is added. :param allow_multiple: Whether multiple appStoreConnect sources are allowed for this project. :returns: The new value of the sources. Use this in a call to `ProjectEndpoint.create_audit_entry()` to create an audit log. :raises ValueError: if an ``appStoreConnect`` source already exists but the ID does not match """ with transaction.atomic(): all_sources_raw = project.get_option(SYMBOL_SOURCES_PROP_NAME) all_sources = json.loads( all_sources_raw) if all_sources_raw else [] for i, source in enumerate(all_sources): if source.get("type") == SYMBOL_SOURCE_TYPE_NAME: if source.get("id") == self.id: all_sources[i] = self.to_json() break elif not allow_multiple: raise ValueError( "Existing appStoreConnect symbolSource config does not match id" ) else: # No matching existing appStoreConnect symbol source, append it. all_sources.append(self.to_json()) project.update_option(SYMBOL_SOURCES_PROP_NAME, json.dumps(all_sources)) return all_sources
def update_build_refresh_date(project: Project, config_id: str) -> None: serialized_option = project.get_option( appconnect.APPSTORECONNECT_BUILD_REFRESHES_OPTION, default="{}") build_refresh_dates = json.loads(serialized_option) build_refresh_dates[config_id] = datetime.now() serialized_refresh_dates = json.dumps_htmlsafe(build_refresh_dates) project.update_option(appconnect.APPSTORECONNECT_BUILD_REFRESHES_OPTION, serialized_refresh_dates)
def all_for_project(cls, project: Project) -> "List[AppStoreConnectConfig]": sources = [] raw = project.get_option(SYMBOL_SOURCES_PROP_NAME, default="[]") all_sources = json.loads(raw) for source in all_sources: if source.get("type") == SYMBOL_SOURCE_TYPE_NAME: sources.append(cls.from_json(source)) return sources
def all_config_ids(project: Project) -> List[str]: """Return the config IDs of all appStoreConnect symbol sources configured in the project.""" raw = project.get_option(SYMBOL_SOURCES_PROP_NAME) if not raw: raw = "[]" all_sources = json.loads(raw) return [ s.get("id") for s in all_sources if s.get("type") == SYMBOL_SOURCE_TYPE_NAME and s.get("id") ]
def get_app_store_config( project: Project, credentials_id: Optional[str]) -> Optional[json.JSONData]: """Returns the appStoreConnect symbol source config for a project.""" sources_config = project.get_option(SYMBOL_SOURCES_PROP_NAME) if credentials_id is None: return None try: sources = json.loads(sources_config) for source in sources: if (source.get("type") == "appStoreConnect" and source.get("id") == credentials_id.lower()): return source return None except BaseException as e: raise ValueError("bad sources") from e
def from_project_config(cls, project: Project, config_id: str) -> "AppStoreConnectConfig": """Creates a new instance from the symbol source configured in the project. :raises KeyError: if the config is not found. :raises InvalidConfigError if the stored config is somehow invalid. """ raw = project.get_option(SYMBOL_SOURCES_PROP_NAME, default="[]") all_sources = json.loads(raw) for source in all_sources: if source.get("type") == SYMBOL_SOURCE_TYPE_NAME and ( source.get("id") == config_id): return cls.from_json(source) else: raise KeyError( f"No {SYMBOL_SOURCE_TYPE_NAME} symbol source found with id {config_id}" )
def get_transaction_metrics_settings( project: Project, breakdowns_config: Optional[Mapping[str, Any]]): metrics = [] custom_tags = [] if features.has("organizations:transaction-metrics-extraction", project.organization): metrics.extend(sorted(TRANSACTION_METRICS)) # TODO: for now let's extract all known measurements. we might want to # be more fine-grained in the future once we know which measurements we # really need (or how that can be dynamically determined) metrics.extend(sorted(ALL_MEASUREMENT_METRICS)) if breakdowns_config is not None: # we already have a breakdown configuration that tells relay which # breakdowns to compute for an event. metrics extraction should # probably be in sync with that, or at least not extract more metrics # than there are breakdowns configured. try: for breakdown_name, breakdown_config in breakdowns_config.items( ): assert breakdown_config["type"] == "spanOperations" for op_name in breakdown_config["matches"]: metrics.append( f"sentry.transactions.breakdowns.{breakdown_name}.{op_name}" ) except Exception: capture_exception() # Tells relay which user-defined tags to add to each extracted # transaction metric. This cannot include things such as `os.name` # which are computed on the server, they have to come from the SDK as # event tags. try: custom_tags.extend( project.get_option("sentry:transaction_metrics_custom_tags") or ()) except Exception: capture_exception() return { "extractMetrics": metrics, "extractCustomTags": custom_tags, "satisfactionThresholds": _get_satisfaction_thresholds(project), }
def from_project_config(cls, project: Project, config_id: str) -> "AppStoreConnectConfig": """Creates a new instance from the symbol source configured in the project. :raises KeyError: if the config is not found. :raises InvalidConfigError if the stored config is somehow invalid. """ raw = project.get_option(SYMBOL_SOURCES_PROP_NAME) # UI bug: the UI writes an empty string when removing the last symbol source from # the list. So we need to cater for both `None` and `''` being returned from # .get_option(). if not raw: raw = "[]" all_sources = json.loads(raw) for source in all_sources: if source.get("type") == SYMBOL_SOURCE_TYPE_NAME and ( source.get("id") == config_id): return cls.from_json(source) else: raise KeyError( f"No {SYMBOL_SOURCE_TYPE_NAME} symbol source found with id {config_id}" )