@@ -57,13 +57,19 @@ def configure(self, configs: List[SecretProviderConfig]) -> None:
5757 logger .error (f"Failed to configure secret provider '{ config .id } ': { e } " )
5858
5959 def resolve (self , secret_ref : str ) -> str :
60- """
61- Resolves a secret reference string.
60+ """Resolves a secret reference string.
61+
6262 Format: ${provider_id:key}
63- Examples:
64- '${env:DB_PASS}' -> fetches DB_PASS from env
65- '${azure-main:my-secret}' -> fetches my-secret from provider with id 'azure-main'
66- '${aws-prod:my-secret}' -> fetches my-secret from provider with id 'aws-prod'
63+
64+ Args:
65+ secret_ref: The reference string to resolve.
66+
67+ Returns:
68+ str: The resolved secret value.
69+
70+ Raises:
71+ ValueError: If the format is invalid, provider is unknown, or secret
72+ is not found.
6773 """
6874 cleaned_ref = secret_ref .replace ('${' , '' ).replace ('}' , '' )
6975
@@ -82,50 +88,50 @@ def resolve(self, secret_ref: str) -> str:
8288
8389 raise ValueError (f"Secret not found: { secret_ref } " )
8490
85- return obj
86-
8791 def resolve_object (self , obj : Any ) -> Any :
88- """
89- Recursively resolves secret references in a generic object (Pydantic model, dict, list, str, SecretStr).
90- Returns a new object with secrets resolved.
92+ """Recursively resolves secret references in a generic object.
93+
94+ Traverses the object structure (Pydantic models, dicts, lists) and resolves
95+ any string values matching the secret pattern "${...}". Handles SecretStr
96+ by unwrapping, resolving, and re-wrapping.
97+
98+ Args:
99+ obj: The object to resolve secrets in. Can be a Pydantic model,
100+ dictionary, list, string, or SecretStr.
101+
102+ Returns:
103+ Any: A new object with all secret references resolved to their
104+ actual values.
91105 """
92106 from pydantic import BaseModel , SecretStr
93107
94- # Base case: String
95108 if isinstance (obj , str ):
96109 if obj .startswith ("${" ) and obj .endswith ("}" ):
97110 return self .resolve (obj )
98111 return obj
99112
100- # Handle SecretStr
101113 if isinstance (obj , SecretStr ):
102114 secret_val = obj .get_secret_value ()
103115 if secret_val and secret_val .startswith ("${" ) and secret_val .endswith ("}" ):
104116 resolved_val = self .resolve (secret_val )
105117 return SecretStr (resolved_val )
106118 return obj
107119
108- # Pydantic Model
109120 if isinstance (obj , BaseModel ):
110- # Recursively resolve fields
111121 updates = {}
112- # Access model_fields from the class, not the instance
113122 for field_name in type (obj ).model_fields .keys ():
114123 val = getattr (obj , field_name )
115124 resolved = self .resolve_object (val )
116- # Only update if changed (optimization)
117125 if resolved != val :
118126 updates [field_name ] = resolved
119127
120128 if updates :
121129 return obj .model_copy (update = updates )
122130 return obj
123131
124- # Lists
125132 if isinstance (obj , list ):
126133 return [self .resolve_object (item ) for item in obj ]
127134
128- # Dicts
129135 if isinstance (obj , dict ):
130136 return {k : self .resolve_object (v ) for k , v in obj .items ()}
131137
0 commit comments