# API Options for Pack 1. Builder-based options 2. Separate simple calls 3. Aggregated calls ## Recommended Options: Go with Option 3 (Aggregated calls). For the languages where named arguments (or appropriate alternative) is supported (Python, JS), use it as-is (see example below). Consider using a builder pattern (or similar alternative) for the aggregated functions for the languages without named arguments support (Rust, Java). Kotlin has named arguments, but Java doesn't have them, so take this into account for Kotlin/Java API. ## Option 1: Builder-based options ``` packed_msg = await PackBuilder() \ .did_resolver(TestDIDResolver()) \ .secrets_resolver(TestSecretsResolver()) \ .sign(from_did=ALICE_DID) \ .auth_crypt(from_did=ALICE_DID, to_dids=[BOB_DID, CAROL_DID]) \ .anon_crypt(to_dids=[BOB_DID, CAROL_DID], enc=EncAlgAnonCrypt.XC20P) \ .finalize() \ .pack(msg) ``` Pros: - The user can not build an invalid message (invalid combination of nested calls). - Individual API calls are compact and have minimal number of arguments. Cons: - More complex in implementations and maintanence. - A possibility of invalid message: different `to_dids` in anoncrypt and authcrypt. ## Option 2: Separate simple calls ``` packer = Packer( did_resolver=TestDIDResolver(), secrets_resolver=TestSecretsResolver() ) signed_msg = await packer.sign(msg=msg, from_did=ALICE_DID) authcrypted_msg = await packer.auth_crypt( msg=signed_msg, from_did=ALICE_DID, to_dids=[BOB_DID, CAROL_DID] ) anoncrypted_msg = await packer.anon_crypt( msg=authcrypted_msg, to_dids=[BOB_DID, CAROL_DID], enc=EncAlgAnonCrypt.XC20P ) ``` Pros: - The API looks clear and simple. - Individual API calls are compact and have minimal number of arguments. - Easy to implement and maintain. Cons: - A possibility of invalid message due to invalid usage of nested combination (it's possible to call `auth(auth(auth)))` for example), and there will be no static checks to prevent it. So, it requirs some DID Comm spec knowledge from the user. - A possibility of invalid message: different `to_dids` in anoncrypt and authcrypt. ## Option 3: Aggregated calls Short case (default params): ``` packer = Packer( did_resolver=TestDIDResolver(), secrets_resolver=TestSecretsResolver() ) packed_msg = await packer.anoncrypt_authcrypt_signed( msg=msg, from_did=ALICE_DID, to_dids=[BOB_DID, CAROL_DID], enc_anon=EncAlgAnonCrypt.XC20P ) ``` Full case: ``` packer = Packer( did_resolver=TestDIDResolver(), secrets_resolver=TestSecretsResolver() ) packed_msg = await packer.anoncrypt_authcrypt_signed( msg=msg, from_did=ALICE_DID, from_sign_did=ALICE_DID, from_kid=kid1, from_sign_kid=kid2, to_dids=[BOB_DID, CAROL_DID], enc_anon=EncAlgAnonCrypt.XC20P, enc_auth=EncAlgAuthCrypt.A256CBC_HS512, alg_auth= KWAlgAuthCrypt.ECDH_1PU_A256KW, alg_anon=KWAlgAuthCrypt.ECDH_1PU_A256KW, ) ``` Example with Builder (when there is no named arguments): ``` packer = Packer( did_resolver=TestDIDResolver(), secrets_resolver=TestSecretsResolver() ) packed_msg = await packer.build_anoncrypt_authcrypt_signed() .from_did(ALICE_DID) .from_sign_did(ALICE_DID) .from_kid(from_kid) .from_sign_kid(kid2) .to_dids([BOB_DID, CAROL_DID]) .enc_anon(EncAlgAnonCrypt.XC20P) .enc_auth(EncAlgAuthCrypt.A256CBC_HS512) .alg_auth(KWAlgAuthCrypt.ECDH_1PU_A256KW) .alg_anon(KWAlgAuthCrypt.ECDH_1PU_A256KW) .finalize() ``` Pros: - The API looks clear and simple. - The user can not build an invalid message (invalid combination of nested calls). - Easy to implement and maintain. Cons: - The total number of arguments for the most complicated call `anoncrypt_authcrypt_signed` is `10`. Although the API calls look nice when default parameters are used (which will probably be used in most of the cases), if there is a need for all argments, the call may not look so nice. This can be solved by using either named arguments with default values, or a builder pattern for the aggregated call.