-
Notifications
You must be signed in to change notification settings - Fork 244
Config Classes
The config-class hierarchy looks like this:
classDiagram
BaseConfig <|-- BaseTaskFlowConfig
BaseConfig <|-- OrgConfig
OrgConfig <|-- SfdxOrgConfig
SfdxOrgConfig <|-- ScratchOrgConfig
BaseTaskFlowConfig <|-- BaseProjectConfig
BaseTaskFlowConfig <|-- UniversalConfig
BaseConfig <|-- ProjectConfigPropertiesMixin
ProjectConfigPropertiesMixin <|-- UniversalConfig
ProjectConfigPropertiesMixin <|-- BaseProjectConfig
BaseConfig <|-- ConnectedAppOAuthConfig
FlowConfig <|-- BaseConfig
TaskConfig <|-- BaseConfig
BaseProjectKeychain <|-- EncryptedFileProjectKeychain
ServiceConfig <|-- BaseConfig
OAuth2ServiceConfig <|-- MarketingCloudServiceConfig
ServiceConfig <|-- OAuth2ServiceConfig
BaseConfig <|-- BaseProjectKeychain
Every config class has a config
member. It's a dict
, which is parsed from the YAML, and is therefore very difficult to type comprehensively.
Access to the config
member is asymmetric. We have a __getattr__()
override on BaseConfig
that attempts to parse __
-separated config paths and look them up dynamically in the config
member. There's a new lookup()
method that is the preferred route to dynamically lookup config paths (rather than using getattr()
). However, updates to config are done by directly accessing the config
member - BaseConfig
does not override __setitem__()
or similar. This is done either via assignment:
some_config.config["some_var"] = some_value
or dict
methods:
some_config.config.update(some_dict)
This pattern breaks encapsulation and prevents us from using typing effectively on most config classes, and is a long-term challenge we need to fix.
Because of the way we've moved (some) config classes between modules in the past, while attempting to preserve backwards compatibility, the config classes are very sensitive to import order. Adding type annotations in ways that cause config modules to import from other config modules can cause very counterintuitive errors. Using if TYPE_CHECKING:
as a guard around imports can help resolve this.
- There's a potential issue in
BaseConfig
: the init method accepts akeychain
optional kwarg but does nothing with it. - In
ProjectConfig
, replacing all of therepo_info
machinery with a Pydantic settings class would be nifty.
- Replacing untyped dicts with strictly-typed Pydantic models, and adjusting value access throughout the codebase.
- Discarding backwards-compatible shims to address import-order dependencies.
classDiagram
BaseConfig <|-- OrgConfig
OrgConfig <|-- SfdxOrgConfig
SfdxOrgConfig <|-- ScratchOrgConfig
The method refresh_oauth_token()
takes a keychain and a connected app. We're not quite sure why, since the org config should know what to use. These parameters may have the sense of an override, and may be used in the web app context. We need to do some more review to be sure.
OAuth2Client.refresh_token()
returns raw JSON. Could we be using a Pydantic model to parse this return and provide type safety?
The OrgConfig class has many properties that lazy-load and cache from the org, including:
The OrgConfig
class tracks the packages installed in the org using a lazy-loaded property, installed_packages
. The type of this property is DefaultDict[str, List[VersionInfo]]
. The keys are strings, which are either the package namespace or a string formatted as "[email protected]", with the package version.
This data is used to answer the questions:
- Is Package A installed?
- Is at least version N.M of Package A installed?
has_minimum_package_version()
is the easiest entry point to this functionality. It takes a package identifier, which can be either a namespace or an 033
AllPackageId
, and a version (str
). It is then responsible for interprets the data structure and returning a result.
The data structure is also publicly exposed as installed_packages
, and preflight checks often use it as
when: "'some_package' not in org_config.installed_packages"
Tasks that install packages are responsible for resetting the cache by calling reset_installed_packages()
.
-
refresh_oauth_token()
takes a keychain parameter, but the OrgConfig has a keychain instance variable.