Idiomatic Kotlin API Guide
This document describes the idiomatic Kotlin features added to the did-core module.
Overview
The module now includes:
Builder DSLs for creating resolvers and registries
Extension functions for fluent, functional-style operations
Operator overloads for intuitive API usage
Retry logic with exponential backoff
Builder DSLs
Universal Resolver Builder
1
2
3
4
5
6
7
8
9
10
11
12
import org.trustweave.did.dsl.universalResolver
val resolver = universalResolver ( "https://dev.uniresolver.io" ) {
timeout = 60
apiKey = "my-api-key"
retry {
maxRetries = 3
initialDelayMs = 200
maxDelayMs = 2000
}
protocolAdapter = StandardUniversalResolverAdapter ()
}
Registry Builder
1
2
3
4
5
6
7
import org.trustweave.did.registry.didMethodRegistry
val registry = didMethodRegistry {
register ( KeyDidMethod ( kms ))
register ( WebDidMethod ())
registerAll ( OtherMethod1 (), OtherMethod2 ())
}
Operator Overloads
Registry Operators
1
2
3
4
5
6
7
8
9
10
11
12
val registry = DidMethodRegistry ()
// Bracket notation for access
val method = registry [ "key" ]
// `in` operator for checking existence
if ( "key" in registry ) {
// Method is registered
}
// Assignment operator
registry [ "new" ] = NewDidMethod ()
Extension Functions
DidResolutionResult extensions
DidResolutionResult is a sealed class (not Result<T>). Use when , resolveOrNull / resolveOrThrow on Did , or the small did-core helpers below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.resolver.errorCode
import org.trustweave.did.resolver.errorMessage
import org.trustweave.did.resolver.hasError
import org.trustweave.did.resolver.isNotFound
import org.trustweave.did.resolver.isSuccess
val result : DidResolutionResult = // ... from resolver.resolve(did)
// Diagnostics
if ( result . isSuccess ) { /* ... */ }
if ( result . hasError ) {
println ( result . errorMessage )
println ( result . errorCode )
}
if ( result . isNotFound ) { /* ... */ }
// Document extraction
val documentOrNull : DidDocument ? = when ( result ) {
is DidResolutionResult . Success -> result . document
else -> null
}
val message = when ( result ) {
is DidResolutionResult . Success -> "Success: ${result.document.id}"
is DidResolutionResult . Failure -> "Failed: ${result.errorMessage}"
}
Did Extensions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.trustweave.did.identifiers.Did
import org.trustweave.did.dsl.resolveWith
import org.trustweave.did.dsl.resolveOrNull
import org.trustweave.did.dsl.resolveOrThrow
import org.trustweave.did.dsl.resolveOrDefault
val did = Did ( "did:key:123" )
val resolver : DidResolver = // ... resolver
// Resolve to document (throws DidException on failure)
val document = did . resolveOrThrow ( resolver )
val doc = did . resolveOrNull ( resolver )
val docOrDefault = did . resolveOrDefault ( resolver , defaultDocument )
// With callback
did . resolveWith ( resolver ) { document ->
println ( "Resolved: ${document.id}" )
}
Retry Configuration
Default Retry
1
2
3
4
val resolver = DefaultUniversalResolver (
baseUrl = "https://dev.uniresolver.io" ,
retryConfig = RetryConfig . default () // 3 retries, 100ms initial delay
)
No Retry
1
2
3
4
val resolver = DefaultUniversalResolver (
baseUrl = "https://dev.uniresolver.io" ,
retryConfig = RetryConfig . noRetry ()
)
Aggressive Retry
1
2
3
4
val resolver = DefaultUniversalResolver (
baseUrl = "https://dev.uniresolver.io" ,
retryConfig = RetryConfig . aggressive () // 5 retries, 200ms initial delay
)
Custom Retry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
val retryConfig = RetryConfig (
maxRetries = 5 ,
initialDelayMs = 200 ,
maxDelayMs = 5000 ,
retryableStatusCodes = setOf ( 500 , 502 , 503 , 504 ),
retryableExceptions = setOf (
java . net . ConnectException :: class . java ,
java . net . SocketTimeoutException :: class . java
)
)
val resolver = DefaultUniversalResolver (
baseUrl = "https://dev.uniresolver.io" ,
retryConfig = retryConfig
)
Cached Validation
The baseUrl validation is now cached during initialization, improving performance for repeated resolutions.
Retry Logic
Automatic retry with exponential backoff for transient failures:
Network errors (ConnectException, SocketTimeoutException)
HTTP 5xx errors (configurable)
Exponential backoff with jitter to avoid thundering herd
Migration Guide
Before
1
2
3
4
5
6
7
8
9
10
val registry = DidMethodRegistry ()
registry . register ( KeyDidMethod ( kms ))
val method = registry . get ( "key" )
val resolver = DefaultUniversalResolver ( "https://dev.uniresolver.io" )
val result = resolver . resolveDid ( "did:key:123" )
val document = when ( result ) {
is DidResolutionResult . Success -> result . document
else -> null
}
After
1
2
3
4
5
6
7
8
9
10
val registry = didMethodRegistry {
register ( KeyDidMethod ( kms ))
}
val method = registry [ "key" ] // Operator overload
val resolver = universalResolver ( "https://dev.uniresolver.io" ) {
timeout = 60
retry { maxRetries = 3 }
}
val document = Did ( "did:key:123" ). resolveOrNull ( resolver )
Best Practices
Use builder DSLs for complex configurations
Use extension functions for fluent, readable code (resolveOrThrow, resolveOrNull, resolveOrDefault)
Use operator overloads for intuitive API usage
Configure retry logic for production environments
Handle the sealed DidResolutionResult with when when you need per-failure behaviour