@@ -230,6 +230,121 @@ async def create_credential_request(
230230
231231 return cred_req_json , cred_req_metadata_json
232232
233+ def _parse_and_validate_ids (self , cred_recvd ) -> tuple [tuple , tuple ]:
234+ """Parse and validate schema and credential definition IDs.
235+
236+ Returns:
237+ Tuple of (schema_id_parts, cdef_id_parts)
238+ """
239+ schema_id = cred_recvd .schema_id
240+ # Handle both qualified (did:sov:V4SG:2:schema:1.0)
241+ # and unqualified (V4SG:2:schema:1.0) schema IDs
242+ schema_id_parts = re .match (
243+ r"^([^:]+(?::[^:]+:[^:]+)?):2:([^:]+):([^:]+)$" , schema_id
244+ )
245+ if not schema_id_parts :
246+ raise IndyHolderError (ERR_PARSING_SCHEMA_ID .format (schema_id ))
247+
248+ cred_def_id = cred_recvd .cred_def_id
249+ cdef_id_parts = re .match (
250+ r"^([^:]+(?::[^:]+:[^:]+)?):3:CL:([^:]+):([^:]+)$" , cred_def_id
251+ )
252+ if not cdef_id_parts :
253+ raise IndyHolderError (ERR_PARSING_CRED_DEF_ID .format (cred_def_id ))
254+
255+ return schema_id_parts , cdef_id_parts
256+
257+ def _normalize_did (self , did : str ) -> str :
258+ """Normalize DID to unqualified format for consistent storage."""
259+ return did [8 :] if did .startswith ("did:sov:" ) else did
260+
261+ def _build_credential_tags (
262+ self ,
263+ cred_recvd ,
264+ schema_id_parts : tuple ,
265+ cdef_id_parts : tuple ,
266+ credential_data : dict ,
267+ credential_attr_mime_types : Optional [dict ],
268+ ) -> tuple [dict , dict ]:
269+ """Build tags and mime_types for credential storage.
270+
271+ Returns:
272+ Tuple of (tags, mime_types)
273+ """
274+ schema_issuer_did = self ._normalize_did (schema_id_parts [1 ])
275+ issuer_did = self ._normalize_did (cdef_id_parts [1 ])
276+
277+ tags = {
278+ "schema_id" : cred_recvd .schema_id ,
279+ "schema_issuer_did" : schema_issuer_did ,
280+ "schema_name" : schema_id_parts [2 ],
281+ "schema_version" : schema_id_parts [3 ],
282+ "issuer_did" : issuer_did ,
283+ "cred_def_id" : cred_recvd .cred_def_id ,
284+ "rev_reg_id" : cred_recvd .rev_reg_id or "None" ,
285+ }
286+
287+ mime_types = {}
288+ for k , attr_value in credential_data ["values" ].items ():
289+ attr_name = _normalize_attr_name (k )
290+ tags [f"attr::{ attr_name } ::value" ] = attr_value ["raw" ]
291+ if credential_attr_mime_types and k in credential_attr_mime_types :
292+ mime_types [k ] = credential_attr_mime_types [k ]
293+
294+ return tags , mime_types
295+
296+ async def _insert_credential_record (
297+ self , txn , credential_id : str , cred_recvd , tags : dict
298+ ) -> None :
299+ """Insert credential record into storage."""
300+ insert_method = txn .handle .insert
301+ if inspect .iscoroutinefunction (insert_method ):
302+ await insert_method (
303+ CATEGORY_CREDENTIAL ,
304+ credential_id ,
305+ cred_recvd .to_json_buffer (),
306+ tags = tags ,
307+ )
308+ else :
309+ insert_method (
310+ CATEGORY_CREDENTIAL ,
311+ credential_id ,
312+ cred_recvd .to_json_buffer (),
313+ tags = tags ,
314+ )
315+
316+ async def _insert_mime_types_record (
317+ self , txn , credential_id : str , mime_types : dict
318+ ) -> None :
319+ """Insert MIME types record if needed."""
320+ if not mime_types :
321+ return
322+
323+ insert_method = txn .handle .insert
324+ if inspect .iscoroutinefunction (insert_method ):
325+ await insert_method (
326+ IndyHolder .RECORD_TYPE_MIME_TYPES ,
327+ credential_id ,
328+ value_json = mime_types ,
329+ )
330+ else :
331+ insert_method (
332+ IndyHolder .RECORD_TYPE_MIME_TYPES ,
333+ credential_id ,
334+ value_json = mime_types ,
335+ )
336+
337+ async def _commit_transaction (self , txn ) -> None :
338+ """Commit transaction if commit method exists."""
339+ commit_method = getattr (txn , "commit" , None )
340+ if not commit_method :
341+ return
342+
343+ if inspect .iscoroutinefunction (commit_method ):
344+ await commit_method ()
345+ else :
346+ commit_method ()
347+
233348 async def store_credential (
234349 self ,
235350 credential_definition : dict ,
@@ -269,87 +384,23 @@ async def store_credential(
269384 except CredxError as err :
270385 raise IndyHolderError (ERR_PROCESS_RECEIVED_CRED ) from err
271386
272- schema_id = cred_recvd .schema_id
273- # Handle both qualified (did:sov:V4SG:2:schema:1.0)
274- # and unqualified (V4SG:2:schema:1.0) schema IDs
275- schema_id_parts = re .match (
276- r"^([^:]+(?::[^:]+:[^:]+)?):2:([^:]+):([^:]+)$" , schema_id
277- )
278- if not schema_id_parts :
279- raise IndyHolderError (ERR_PARSING_SCHEMA_ID .format (schema_id ))
280- cred_def_id = cred_recvd .cred_def_id
281- cdef_id_parts = re .match (
282- r"^([^:]+(?::[^:]+:[^:]+)?):3:CL:([^:]+):([^:]+)$" , cred_def_id
283- )
284- if not cdef_id_parts :
285- raise IndyHolderError (ERR_PARSING_CRED_DEF_ID .format (cred_def_id ))
387+ schema_id_parts , cdef_id_parts = self ._parse_and_validate_ids (cred_recvd )
286388
287389 credential_id = credential_id or str (uuid4 ())
288390
289- # Normalize DIDs to unqualified format for consistent storage and querying
290- # This matches the pattern used in BaseConnectionManager.store_did_document()
291- schema_issuer_did = schema_id_parts [1 ]
292- if schema_issuer_did .startswith ("did:sov:" ):
293- schema_issuer_did = schema_issuer_did [8 :]
294-
295- issuer_did = cdef_id_parts [1 ]
296- if issuer_did .startswith ("did:sov:" ):
297- issuer_did = issuer_did [8 :]
298-
299- tags = {
300- "schema_id" : schema_id ,
301- "schema_issuer_did" : schema_issuer_did ,
302- "schema_name" : schema_id_parts [2 ],
303- "schema_version" : schema_id_parts [3 ],
304- "issuer_did" : issuer_did ,
305- "cred_def_id" : cred_def_id ,
306- "rev_reg_id" : cred_recvd .rev_reg_id or "None" ,
307- }
308-
309- mime_types = {}
310- for k , attr_value in credential_data ["values" ].items ():
311- attr_name = _normalize_attr_name (k )
312- tags [f"attr::{ attr_name } ::value" ] = attr_value ["raw" ]
313- if credential_attr_mime_types and k in credential_attr_mime_types :
314- mime_types [k ] = credential_attr_mime_types [k ]
391+ tags , mime_types = self ._build_credential_tags (
392+ cred_recvd ,
393+ schema_id_parts ,
394+ cdef_id_parts ,
395+ credential_data ,
396+ credential_attr_mime_types ,
397+ )
315398
316399 try :
317400 async with self ._profile .transaction () as txn :
318- insert_method = txn .handle .insert
319- if inspect .iscoroutinefunction (insert_method ):
320- await insert_method (
321- CATEGORY_CREDENTIAL ,
322- credential_id ,
323- cred_recvd .to_json_buffer (),
324- tags = tags ,
325- )
326- else :
327- insert_method (
328- CATEGORY_CREDENTIAL ,
329- credential_id ,
330- cred_recvd .to_json_buffer (),
331- tags = tags ,
332- )
333- if mime_types :
334- insert_method = txn .handle .insert
335- if inspect .iscoroutinefunction (insert_method ):
336- await insert_method (
337- IndyHolder .RECORD_TYPE_MIME_TYPES ,
338- credential_id ,
339- value_json = mime_types ,
340- )
341- else :
342- insert_method (
343- IndyHolder .RECORD_TYPE_MIME_TYPES ,
344- credential_id ,
345- value_json = mime_types ,
346- )
347- commit_method = getattr (txn , "commit" , None )
348- if commit_method :
349- if inspect .iscoroutinefunction (commit_method ):
350- await commit_method ()
351- else :
352- commit_method ()
401+ await self ._insert_credential_record (txn , credential_id , cred_recvd , tags )
402+ await self ._insert_mime_types_record (txn , credential_id , mime_types )
403+ await self ._commit_transaction (txn )
353404 except (DBStoreError , AskarError ) as err :
354405 raise IndyHolderError (ERR_STORING_CREDENTIAL ) from err
355406
0 commit comments