Hi. Does DynamoDb Fake support `Conditional Expres...
# http4k
a
Hi. Does DynamoDb Fake support
Conditional Expressions
? Example below uses
attribute_not_exists
. I noticed similar issue earlier when i worked on the update functionality and i was also not able to unit test it as usual.
Copy code
override fun insert(evTrip: EvTrip, agreement: Agreement): Either<DeduplicationStoreError, Unit> = either {
        val partitionKey = evTrip.contractId
        val sortKey = evTrip.sortKey()
        val ttl = evTrip.startTimestamp.plusDaysToMillis(TTL_DELAY_DAYS)
        val record = DeduplicationRecord(evTrip, agreement, partitionKey, sortKey, ttl)

        catch({
            putItem(
                TableName = tableName,
                ConditionExpression = "attribute_not_exists($sortKeyAttr)",
                Item = Item().with(lens of record),
            )
        }) { t -> raise(InsertEvTripFailed(evTrip.tripId, t.message)) }
    }
and test
Copy code
@Test
    fun `insert existing evTrip and returns left`() {
        sut().run {
            insert(evTrip, agreement)

            table[evTrip.contractId, evTrip.sortKey()] shouldNotBe null // Inserted

            insert(evTrip, agreement) shouldBeLeft InsertEvTripFailed(evTrip.tripId, "") // Fails here as it was inserted it again
        }
    }
a
Alright. is there example of how to use the ConditionExpression builder or something like that?
i think i found it:
Copy code
ConditionExpression = "attribute_not_exists(#key1)",
ExpressionAttributeNames = mapOf("#key1" to sortKeyAttr.name),
a
Interesting that you needed to do that. What was the attribute name? Perhaps it was reserved. Also, http4k connect operations are designed to return errors rather than throw them. Are you using a try-catch simply out of abundance of caution?
a
well later i noticed it was returning the Result4k so i didn't consume the error, which pointed me in the right direction. Now it works
a
Ah!
So in your test, the record wasn't being inserted twice, but your code thought it was
a
the updated code looks like this:
Copy code
override fun insert(evTrip: EvTrip, agreement: Agreement): Either<DeduplicationStoreError, Unit> {
        val partitionKey = evTrip.contractId
        val sortKey = evTrip.sortKey()
        val ttl = evTrip.startTimestamp.plusDaysToMillis(TTL_DELAY_DAYS)
        val record = DeduplicationRecord(evTrip, agreement, partitionKey, sortKey, ttl)

        return putItem(
            TableName = tableName,
            ConditionExpression = "attribute_not_exists(#key1)",
            ExpressionAttributeNames = mapOf("#key1" to sortKeyAttr.name),
            Item = Item().with(lens of record),
        )
            .toEither()
            .mapLeft { e -> InsertEvTripFailed(evTrip.tripId, e.message) }
            .map {} // throw away result
    }
and the test:
Copy code
@Test
    fun `insert existing evTrip and returns left`() {
        sut().run {
            table[evTrip.contractId, evTrip.sortKey()] shouldBe null

            insert(evTrip, agreement)

            table[evTrip.contractId, evTrip.sortKey()] shouldNotBe null

            insert(evTrip, agreement).shouldBeLeftOf<InsertEvTripFailed>()
        }
    }
yes, because i was not handling the error from Result4k in my Arrow function
a
Gotcha. So replacing the condition expression attribute name with #key1 isn't necessary, but doesn't hurt
a
well i can check if it works without it
removing. the
ConditionExpression
makes test to fail so it is needed
it may be because my condition asserts the SK not the PK?
a
That would be very strange. IIRC, the function should work on ANY attribute
Later, I may revisit the tests to see if I can find an issue.
a
so the putItem should fail on conflict by default?
a
No no, I'm not suggesting you remove the condition expression. I'm wondering if you really need to replace the attribute name with the #key1 placeholder
a
alright i'm with you. yes i can confirm that having only
ConditionExpression = "attribute_not_exists",
works
a
I was thinking more like 'attribute_not_exists(sortKeyAttr)'. I can't imagine your expression would be considered valid. Perhaps your code misinterpreted the error.
Sorry for misformatting. Am on mobile. Is early morning for me here.
a
this one works
"attribute_not_exists($sortKeyAttr)"
a
Perfect. It's not necessary at all as I'm sure you noticed. I just wanted to check, because initially it seems like you thought adding the placeholder fixed the problem
👍 1
a
No worries. Thanks for help @Andrew O'Hara
❤️ 1
ow and thanks for help @dave too 🙂
I've got an update on this, as AWS does not like the
attribute_not_exists
on its own...
Copy code
{
  "__type": "com.amazon.coral.validate#ValidationException",
  "message": "Invalid ConditionExpression: Syntax error; token: \"<EOF>\", near: \"attribute_not_exists\""
}
so ended up adding the sortKey back
attribute_not_exists($sortKeyAttr)