question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[BUG] No error thrown for invalid condition

See original GitHub issue

Summary:

Dynamoose does not throw/log an error when we create an invalid Dynamoose#Condition, using the DynamoDB condition syntax. This causes silent logic bugs!

Code sample:

Schema

const definition = new dynamoose.Schema(
    {
        id: {
            type: String,
            required: true,
        },
        value: {
            type: String,
            required: true,
        },
        randomNumber: {
            type: Number,
            required: true,
        },
    },
    {
        /**
         * Let Dynamoose manage the `createdAt` and `updatedAt` timestamps,
         * Unix epoch in seconds (not milliseconds)
         */
        timestamps: true,
    },
);

Model

const tableName = 'dipack_dynamoose_bug_repro';
const Model = dynamoose.model(tableName, definition, { create: true });

General

// ========== ALL CODE HERE INCLUDING SCHEMA AND MODEL =============

const dynamoose = require('dynamoose');

const ddb = new dynamoose.aws.sdk.DynamoDB({
    region: 'eu-west-1',
    version: 'latest',
});

dynamoose.aws.ddb.set(ddb);

const tableName = 'dipack_dynamoose_bug_repro';

const definition = new dynamoose.Schema(
    {
        id: {
            type: String,
            required: true,
        },
        value: {
            type: String,
            required: true,
        },
        randomNumber: {
            type: Number,
            required: true,
        },
    },
    {
        /**
         * Let Dynamoose manage the `createdAt` and `updatedAt` timestamps,
         * Unix epoch in seconds (not milliseconds)
         */
        timestamps: true,
    },
);

const DELETION_THRESHOLD_MS = (1000 * 60);
const now = Date.now();

const buggyQuery = new dynamoose.Condition({
    FilterExpression: '#createdAtCol <= :createdAtValue OR #updatedAtCol <= :updatedAtValue',
    ExpressionAttributeValues: {
        ':createdAtValue': {
            N: now - DELETION_THRESHOLD_MS,
        },
        ':updatedAtValue': {
            N: now - DELETION_THRESHOLD_MS,
        },
    },
    ExpressionAttributeNames: {
        '#createdAtCol': 'createdAt',
        '#updatedAtCol': 'updatedAt',
    },
});

const workingQueryDyn = new dynamoose.Condition().where('createdAt').le(now - DELETION_THRESHOLD_MS).or().where('updatedAt').le(now - DELETION_THRESHOLD_MS);

(async () => {
    const Model = dynamoose.model(tableName, definition, { create: true });
    const existingRows = await Model.scan().exec();
    console.log(`Found ${existingRows.length} rows.`);
    const newRows = await Promise.all(
        new Array(5).fill(0).map(async (_, idx) => {
            const id = Math.floor(Math.random() * 10 ** 10);
            const row = await Model.create({
                id: String(id),
                value: `${id}-${idx}`,
                randomNumber: Math.random(),
            });
            return row;
        })
    ).catch(err => console.error('Error while creating new rows', err));
    console.log(`Created ${newRows.length} new rows.`);
    const filteredRows = await Model.scan(buggyQuery).exec();
    console.log(`Filtered ${filteredRows.length} rows using DynamoDB condition API`);
    const allRows = await Model.scan().exec();
    if (allRows.every(row => filteredRows.find(fRow => fRow.id === row.id) !== undefined)) {
        console.warn('No rows were filtered!');
    }
    const filteredRowsDyn = await Model.scan(workingQueryDyn).exec();
    console.log(`Filtered ${filteredRowsDyn.length} rows using Dynamoose condition API`);
    if (allRows.every(row => filteredRowsDyn.find(fRow => fRow.id === row.id) !== undefined)) {
        console.warn('No rows were filtered, using the Dynamoose API too!');
    }
    console.log('DynamoDB API condition object', buggyQuery.requestObject());
    console.log('Dynamoose API condition object', workingQueryDyn.requestObject());
})();

Current output and behavior (including stack trace):

The DynamoDB based Condition is currently shown as an empty object.

Found 105 rows.
Created 5 new rows.
Filtered 110 rows using DynamoDB condition API
Filtered 100 rows using Dynamoose condition API
DynamoDB API condition object {}
Dynamoose API condition object {
  ConditionExpression: '#a0 <= :v0 OR #a1 <= :v1',
  ExpressionAttributeNames: { '#a0': 'createdAt', '#a1': 'updatedAt' },
  ExpressionAttributeValues: { ':v0': { N: '1640063029486' }, ':v1': { N: '1640063029486' } }
}

Expected output and behavior:

Expect the condition objects built using the DynamoDB syntax, and the Dynamoose API to be the same. If the condition generated is invalid, then we should throw an error.

Environment:

Operating System: MacOS Operating System Version: 12.1 Monterey Node.js version (node -v): 14.17.3 NPM version: (npm -v): 6.14.13 Dynamoose version: 2.8.3

Other information (if applicable):

Other:

  • I have read through the Dynamoose documentation before posting this issue
  • I have searched through the GitHub issues (including closed issues) and pull requests to ensure this issue has not already been raised before
  • I have searched the internet and Stack Overflow to ensure this issue hasn’t been raised or answered before
  • I have tested the code provided and am confident it doesn’t work as intended
  • I have filled out all fields above
  • I am running the latest version of Dynamoose

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:16 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
dipack95commented, Dec 22, 2021

@fishcharlie It worked! 🥳

1reaction
fishcharliecommented, Dec 22, 2021

@dipack95 Forgot to push 😆. Check again now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

java - Which exception to throw for invalid input which is valid ...
The question is is throwing illegal argument exception the right thing to do? It depends on how you want / need to "frame"...
Read more >
Throwing ArgumentException and InvalidOperationException
An exception is thrown when an error is encountered in a running application, whether from bad code or bad user input.
Read more >
Throwing an exception in Java - Javamex
Exceptions are used to control error flow in a Java program. Exceptions work as follows: At any point where an error condition is...
Read more >
How to Throw Exceptions in Java - Rollbar
Checked exceptions are generally caused by faults outside code like missing files, invalid class names, and networking errors.
Read more >
Handling Exceptions - cs.utsa.edu
Exceptions are thrown when a method detects a problem and is unable to handle it. When an exception occurs and is not handled,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found