Skip to content

Adds various null checks required for AWS SDK v4#88

Open
njlr wants to merge 20 commits intofsprojects:masterfrom
njlr:bugfix/check-nulls-aws-sdk-v4
Open

Adds various null checks required for AWS SDK v4#88
njlr wants to merge 20 commits intofsprojects:masterfrom
njlr:bugfix/check-nulls-aws-sdk-v4

Conversation

@njlr
Copy link
Contributor

@njlr njlr commented Mar 18, 2026

This PR adds null checks to TableContext.fs that are required for AWS SDK v4.

Somewhat related to #87


if response.IsItemSet then
return Some response.Item
return Option.ofObj response.Item
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you're doing it for consistency but the guard guarantees it won't be null (in fact the guard will return Null if the Item.Count = 0)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had to return an option either way so figured why not.
I guess Some response.Item might be marginally more efficient since it is one fewer null check?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR Its more about not putting 'just in case' code that's not justified as that makes the reader have to think more (and/or if you were trying to autogen coverage of all paths, you'd be trying to generate a null with the guard returning true, which would be impossible)

It happens to be more efficient, but its more about keeping the code straightforward and not putting diffs in unless there is an actual correctness improvement - the IsItemSet absolutely guarantees a non-null value, and the general pattern should be to think about guarding all inspections of properties and then either do the work conditionally, or substitute an empty if the arrays are being processed in some way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense; updated

|> Seq.map _.Item
|> Seq.toArray
| false, _ -> [||]
if isNull response.UnprocessedItems then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps clearer if we switch to array comprehensions:

        let unprocessed =
            [| if response.UnprocessedItems <> null then
                   match response.UnprocessedItems.TryGetValue tableName with
                   | true, reqs ->
                       for r in reqs do
                           if r.PutRequest <> null then
                               yield r.PutRequest.Item
                   | false, _ -> () |]

NOTE in the above I did not use isNull / isNotNull
These should normally only be used when you're checking a reference to an F# type that is marked as not allowing null (the default) (to circumvent the compiler error that would arise if you did)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the comprehensions too; updated

| false, _ -> [||]
maybeReport
|> Option.iter (fun r -> r BatchWriteItems (Seq.toList response.ConsumedCapacity) (items.Length - unprocessed.Length))
|> Option.iter (fun r -> r BatchWriteItems (if isNull response.ConsumedCapacity then [] else Seq.toList response.ConsumedCapacity) (items.Length - unprocessed.Length))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these get pretty illegible and reformatting might split the lines.

        |> Option.iter (fun r ->
            let cc = if response.ConsumedCapacity = null then [] else Seq.toList response.ConsumedCapacity
            r BatchWriteItems cc (items.Length - unprocessed.Length))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will wait till you have made all comments then fix these in one go.
Can also apply Fantomas if it doesn't blow up the diff?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@njlr we did a big reformat with fantomas last year so it should only update the changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these get pretty illegible and reformatting might split the lines.

Much nicer; updated

@njlr we did a big reformat with fantomas last year so it should only update the changes

Great, I have applied Fantomas

maybeReport
|> Option.iter (fun r ->
response.ConsumedCapacity
(if isNull response.ConsumedCapacity then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

            let cc = if response.ConsumedCapacity = null then [] else Seq.toList response.ConsumedCapacity
            for tableName, consumedCapacity in cc |> Seq.groupBy _.TableName do
                r tableName Operation.TransactWriteItems (Seq.toList consumedCapacity) (Seq.length transactionItems))

|> Option.iter (fun r -> r BatchGetItems (List.ofSeq response.ConsumedCapacity) response.Responses[tableName].Count)
|> Option.iter (fun r ->
let cc =
if isNull response.ConsumedCapacity then
Copy link
Member

@bartelink bartelink Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use = null and/or remove all usage of isNull/isnotNull?

(I really would like this to format as the one liner it should be! did fantomas/auto format do it this way?)

Copy link
Contributor Author

@njlr njlr Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it odd this library defines an isNull when that is part of FSharp.Core 🤔

Will update the rest 👍

Edit:

(I really would like this to format as the one liner it should be! did fantomas/auto format do it this way?)

Yes, that's Fantomas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants