awslabs / aws-sdk-kotlin

Multiplatform AWS SDK for Kotlin
Apache License 2.0
412 stars 49 forks source link

Can't get the string from an AttributeValue #550

Closed TheBearF8 closed 2 years ago

TheBearF8 commented 2 years ago

Describe the issue

val response = client.userInTable(tableName, username)
   response.items?.first()?.let { user ->
            val pwd = "${user["password"]}".replace(")","").replace("S(value=","")

Can't simply get user["password"].toString() or getString() it remains and attribute value So I have to drop the S(value= and ) to be left with string

Requesting an item from dynamoDB table

Steps to Reproduce

request an item from dynamoDB can change the data returned into a normal type in the case string

Current behavior

Won't convert to string have to string replace to manipulate

AWS Kotlin SDK version used

aws.sdk.kotlin:dynamodb:0.13.1-beta

Platform (JVM/JS/Native)

JVM (desktop application)

Operating System and version

Mac OS 12.1

ianbotsf commented 2 years ago

Hi @TheBearF8.

I can't see the definition of userInTable in your code snippet but I presume it's fetching items using something like Scan or Query. If so, please note that items are maps of attribute names (strings) to values (AttributeValue) which support a variety of data types.

AttributeValue in the Kotlin SDK is a sealed data class with nested subclasses for each supported data type, including AttributeValue.S for strings. If you know the data type of your password attribute is a string at build time, you can cast it to get the value:

val pwd = (user["password"] as AttributeValue.S).value

In other cases where the data type of an attribute is not known at build time, you can use pattern matching to handle every possible case:

val foo = when (val attr = user["foo"]) {
    is AttributeValue.S -> attr.value
    is AttributeValue.N -> ...
    is AttributeValue.Bool -> ...
    ...
}

Does that answer your question?

TheBearF8 commented 2 years ago

Thank You !! and yes userInTable is doing a QueryRequest and returns query(request)

suspend fun DynamoDbClient.userInTable(name: String, user: String): QueryResponse {
    val request = QueryRequest {
        tableName = name
        keyConditionExpression    = "#user = :name"
        expressionAttributeNames  = mapOf("#user" to "username")
        expressionAttributeValues = mapOf(":name" to AttributeValue.S(user))
    }
    return query(request)
}

How would you handle the item(s) returned mapped to a data class or class object? Thanks in advance

ianbotsf commented 2 years ago

Mapping items to custom data classes is not something the SDK provides (although a later add-on library similar to DynamoDBMapper may provide it in the future). Thus, data mapping needs to be done in your own business logic.

For example:

data class User(val name: String, val password: String)

suspend fun DynamoDbClient.userInTable(table: String, user: String): List<User> {
    val request = QueryRequest {
        tableName = table
        keyConditionExpression    = "#user = :name"
        expressionAttributeNames  = mapOf("#user" to "username")
        expressionAttributeValues = mapOf(":name" to AttributeValue.S(user))
    }
    val items = query(request).items ?: listOf()
    return items.map { item ->
        User(
            name     = (item["name"] as AttributeValue.S).value,
            password = (item["password"] as AttributeValue.S).value,
        )
    }
}
ianbotsf commented 2 years ago

FWIW, we have a feature proposal that may make retrieving attribute values easier (#393). If you feel this may be useful in your code, feel free to add a 👍 reaction to that issue.

Resolving this issue as the requested guidance has been provided. @TheBearF8 feel free to re-open and ask further questions if you need.

github-actions[bot] commented 2 years ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.