Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove DynamodbWrapper client dependency #32

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

lfantone
Copy link
Collaborator

No description provided.

@lfantone lfantone self-assigned this Jan 21, 2021
@lfantone lfantone marked this pull request as ready for review January 21, 2021 17:14
@lfantone lfantone requested a review from nfantone as a code owner January 21, 2021 17:14
@lfantone lfantone requested review from a team, crash7, ftschopp and ivelazco and removed request for a team January 21, 2021 17:14
}
}

result.Items = items;
Copy link
Contributor

Choose a reason for hiding this comment

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

You may want to update the other fields that refer to the amount of items read (like count or consumed capacity)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, I forgot about the Count, ScannedCount and ConsumedCapacity

Copy link
Collaborator

@nfantone nfantone left a comment

Choose a reason for hiding this comment

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

Thanks for this! Left some comments here and there. The only bits and blobs I see missing are the batchWrite operations which would now behave differently after the removal on dynamodb-wrapper. Not really sold on keeping current functionality, though - so opening the discussion. We might as well cut a new major release and make flynamo be on par with the official AWS.DynamoDB client.

src/get-all.js Outdated

const DEFAULT_OPTIONS = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd freeze this object and add docs to the props.

const DEFAULT_OPTIONS = Object.freeze({ ... });

src/get-all.js Outdated
autopagination: true,
raw: false
};
const mergeWithDefaults = mergeRight(DEFAULT_OPTIONS);
Copy link
Collaborator

@nfantone nfantone Jan 21, 2021

Choose a reason for hiding this comment

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

This kinda works, but won't default to values in DEFAULT_OPTIONS for null, undefined or empty properties. Maybe remove those before merging?

const mergeWithDefaults = compose(mergeRight(DEFAULT_OPTIONS), rejectNilOrEmpty);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. If the user put a nil value, why this should change it?
  2. I can add the _ rejectNilOrEmpty_ but we don't have ramda-land here, do see that is necessary to add another dependency just for this?

const { getAll } = createAllGetter({ scan: mockScan });
await getAll();
expect(mockScan).toHaveBeenCalled();
});

test('should return `undefined` if no items are returned', async () => {
const mockScan = jest.fn().mockResolvedValue({});
test('should return `[]` if no items are returned', async () => {
Copy link
Collaborator

@nfantone nfantone Jan 21, 2021

Choose a reason for hiding this comment

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

While I agree [] makes more sense, this would be a breaking change.

Copy link
Collaborator Author

@lfantone lfantone Jan 22, 2021

Choose a reason for hiding this comment

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

I revert this change to return undefined instead

src/count.js Outdated

/**
* @private
*/
const createCount = scan => pipeP(scan, prop('Count'));
const createCount = scan => params => withPaginatorHelper(scan, params, true).then(prop('Count'));
Copy link
Collaborator

@nfantone nfantone Jan 21, 2021

Choose a reason for hiding this comment

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

I like the simplicity of what we had before. Maybe we could make the withPaginatorHelper return an async function that expects params? (see comment further below)

Suggested change
const createCount = scan => params => withPaginatorHelper(scan, params, true).then(prop('Count'));
const createCount = scan => compose(andThen(prop('Count')), withPaginator(scan));

/**
* @private
*/
const createGetAll = scan => pipeP(scan, unwrapAll('Items'));
const createGetAll = scan => (params, options = {}) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Following the idea of having the paginator helper return a function accepting params, this could be turned into something like:

const createGetAll = scan => (params, options = {}) => {
  const { raw, autopagination } = mergeWithDefaults(options);
  const scanFn = autopagination ? withPaginator(scan) : scan;
  return scanFn(params).then(
    raw ? unwrapOverAll('Items') : unwrapAll('Items')
  );
};

src/get.js Outdated
@@ -13,7 +13,8 @@ const addTableName = require('./table-name');
/**
* @private
*/
const getUnwrappedItem = getItem => pipeP(apply(getItem), unwrapProp('Item'));
const getUnwrappedItem = getItem =>
compose(andThen(unwrapProp('Item')), request => request.promise(), apply(getItem));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is now used everywhere, what about adding some helpers including a swap in replacement for andThen that automatically invokes request.promise() first?

'use strict';
const R = require('ramda');

/**
 * Invokes `promise` on the given object passing
 * in no arguments.
 *
 * @param {object} obj The obj to invoke `promise` on.
 * @returns {Promise.<*>} A `Promise` as returned by a call to `promise`.
 */
const toPromise = R.invoker(0, 'promise')

/**
 * Returns the result of applying an `fn` function to the value inside a fulfilled promise,
 * after having invoked `promise()` on the target object.
 * 
 * @private
 * @see https://ramdajs.com/docs/#andThen
 * @param {Function} fn The function to apply. Can return a value or a promise of a value.
 */
const andThen = fn => compose(
  R.andThen(fn),
  toPromise
);

module.exports = { toPromise, andThen };

src/query.js Outdated
const { unwrapAll, unwrapOverAll } = require('./wrapper');
const addTableName = require('./table-name');
const withPaginatorHelper = require('./with-paginator-helper');

const DEFAULT_OPTIONS = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same as the scan comments above.

if (autopagination) {
while (result.LastEvaluatedKey) {
result = await methodFn({ ...params, ExclusiveStartKey: result.LastEvaluatedKey }).promise();
result.Items && result.Items.length >= 0 && items.push(...result.Items);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
result.Items && result.Items.length >= 0 && items.push(...result.Items);
isNotNilOrEmpty(result.Items) && items.push(...result.Items);

* @param {boolean} autopagination Whether to return all the records from DynamoDB or just the first batch of records.
* @returns {Promise} A promise that resolves to the response from DynamoDB.
*/
async function withPaginatorHelper(methodFn, params, autopagination) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd turn this into a factory function expecting an operationFn and returning another function that takes in only input as argument ("operation" and "input" as that's what the AWS.DynamoDB typings typically calls them). The autopagination bit seems redundant to me.

function withPaginator(operationFn) {
  async function autopaginate(input) { /* ... */ }
  return autopaginate;
}

@lfantone
Copy link
Collaborator Author

What the wrapper is doing for those cases is to be able to do an unlimited amount of request to the database taking care of the 25 request limit that the aws-sdk client has.
So maybe it is a good idea to keep the wrapper client for those batch methods.

Thoughts?

@github-actions
Copy link

Code coverage

Filename Statements Branches Functions Lines
src/with-paginator.js 100% 95.83% 100% 100%
src/update.js 100% 100% 100% 100%
src/wrapper.js 95% 100% 83.33% 100%
src/table-name.js 100% 100% 100% 100%
src/return-values.js 100% 100% 100% 100%
src/map-merge-args.js 100% 100% 100% 100%
src/cast-array.js 100% 100% 100% 100%
src/generate-key.js 100% 100% 100% 100%
src/and-then.js 100% 100% 100% 100%
src/builders/append.js 100% 100% 100% 100%
src/builders/catcon.js 100% 100% 100% 100%
src/builders/put.js 100% 100% 100% 100%
src/builders/update-expression.js 100% 100% 100% 100%
src/batch-get-item.js 100% 100% 100% 100%
src/get.js 100% 100% 100% 100%
src/query.js 100% 75% 100% 100%
src/remove.js 100% 100% 100% 100%
src/batch-write-item.js 100% 100% 100% 100%
src/generate-item.js 100% 100% 100% 100%
src/insert.js 100% 100% 100% 100%
src/builders/remove.js 100% 100% 100% 100%
src/get-all.js 100% 60% 100% 100%
src/count.js 100% 100% 100% 100%
src/describe-table.js 100% 100% 100% 100%

@crash7 crash7 marked this pull request as draft February 22, 2022 18:21
@crash7
Copy link
Contributor

crash7 commented Apr 14, 2024

What the wrapper is doing for those cases is to be able to do an unlimited amount of request to the database taking care of the 25 request limit that the aws-sdk client has. So maybe it is a good idea to keep the wrapper client for those batch methods.

Thoughts?

@lfantone we may need to drop the dependency because it's blocking the upgrade to aws-sdk v3. Maybe we can bring the implementation for the batch methods to flynamo itself?

@lfantone
Copy link
Collaborator Author

Yes, feel free to remove it

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

Successfully merging this pull request may close these issues.

3 participants