Skip to content

Commit

Permalink
feat: added marshall / unmarshall for dynamodb fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Siemon committed Feb 23, 2024
1 parent 9280e5a commit 428de7d
Show file tree
Hide file tree
Showing 9 changed files with 476 additions and 12 deletions.
40 changes: 38 additions & 2 deletions src/module/dy/create-model.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import get from 'lodash.get';
import objectScan from 'object-scan';
import getFirst from './get-first.js';
import validateKwargs from './validate-kwargs.js';

Expand Down Expand Up @@ -61,7 +62,12 @@ export default (kwargs) => {
name,
timestamps: false,
attributes: Object.fromEntries(Object.entries(attributes).map(([k, v]) => {
const { validate, ...prunedV } = v;
const {
validate,
marshall,
unmarshall,
...prunedV
} = v;
if (prunedV.type === 'set' && Array.isArray(prunedV.default) && prunedV.default.length === 0) {
const { default: _, ...newV } = prunedV;
return [k, newV];
Expand Down Expand Up @@ -110,9 +116,39 @@ export default (kwargs) => {
BillingMode: 'PAY_PER_REQUEST'
};

const generateMrsl = (fn) => {
const logic = Object.fromEntries(
Object
.entries(attributes)
.filter(([k, v]) => typeof v?.[fn] === 'function')
.map(([k, v]) => [`{[*].${k},${k}}`, v])
);
return (itemOrItems) => {
objectScan(
Object.keys(logic),
{
filterFn: ({
parent,
property,
value,
matchedBy
}) => {
matchedBy.forEach((m) => {
// eslint-disable-next-line no-param-reassign
parent[property] = logic[m][fn](value);
});
}
}
)(itemOrItems);
return itemOrItems;
};
};

return {
schema,
table,
entity
entity,
marshall: generateMrsl('marshall'),
unmarshall: generateMrsl('unmarshall')
};
};
2 changes: 1 addition & 1 deletion src/module/dy/fns/get-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ export default (model, onNotFound_, setDefaults) => async (...args) => {
.forEach((k) => {
delete item[k];
});
return item;
return model.unmarshall(item);
};
5 changes: 4 additions & 1 deletion src/module/dy/fns/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ export default (model, validateSecondaryIndex, setDefaults, getSortKeyByIndex, c
if (toReturn !== null) {
Retainer(toReturn)(items);
}
return { items, page };
return {
items: model.unmarshall(items),
page
};
};
};
4 changes: 3 additions & 1 deletion src/module/dy/fns/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export default (model, validateSecondaryIndex, setDefaults) => async (...args) =
entity: model.table.name
});
return {
items: result.Items.map((item) => setDefaults(item, toReturn)),
items: model.unmarshall(
result.Items.map((item) => setDefaults(item, toReturn))
),
...(result.LastEvaluatedKey === undefined ? {} : { lastEvaluatedKey: result.LastEvaluatedKey })
};
};
4 changes: 2 additions & 2 deletions src/module/dy/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default ({
let result;
try {
result = await model.entity[fn](
itemRewriterByFn[fn](item),
model.marshall(itemRewriterByFn[fn](item)),
{
returnValues: 'all_old',
...(conditions === null ? {} : { conditions })
Expand All @@ -130,7 +130,7 @@ export default ({
}
const didNotExist = result.Attributes === undefined;
const mergedItem = mergeAttributes(
(didNotExist || fn === 'put') ? {} : result.Attributes,
(didNotExist || fn === 'put') ? {} : model.unmarshall(result.Attributes),
item
);
const resultItem = setDefaults(mergedItem, null);
Expand Down
2 changes: 2 additions & 0 deletions src/module/dy/validate-kwargs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const schema = Joi.object().keys({
type: Joi.string().valid('string', 'boolean', 'number', 'list', 'map', 'binary', 'set'),
partitionKey: Joi.boolean().valid(true).optional(),
sortKey: Joi.boolean().valid(true).optional(),
marshall: Joi.function().arity(1).optional(),
unmarshall: Joi.function().arity(1).optional(),
default: Joi.alternatives().try(
Joi.string(),
Joi.number(),
Expand Down
8 changes: 4 additions & 4 deletions test/module/dy/create-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('Testing create-model.js', () => {
Table,
Entity
});
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity']);
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity', 'marshall', 'unmarshall']);
});

it('Testing creation without indices', () => {
Expand All @@ -45,7 +45,7 @@ describe('Testing create-model.js', () => {
Table,
Entity
});
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity']);
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity', 'marshall', 'unmarshall']);
});

it('Testing creation different attribute types', () => {
Expand All @@ -61,7 +61,7 @@ describe('Testing create-model.js', () => {
Table,
Entity
});
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity']);
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity', 'marshall', 'unmarshall']);
});

it('Testing attribute not supported for indexing error', () => {
Expand Down Expand Up @@ -95,6 +95,6 @@ describe('Testing create-model.js', () => {
Table,
Entity
});
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity']);
expect(Object.keys(r)).to.deep.equal(['schema', 'table', 'entity', 'marshall', 'unmarshall']);
});
});
26 changes: 25 additions & 1 deletion test/module/dy/fns/get-item.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { describe } from 'node-tdd';
import { LocalTable, buildModel, createItems } from '../../../dy-helper.js';
import zlib from 'zlib';
import { buildModel, createItems, LocalTable } from '../../../dy-helper.js';
import { ModelNotFound } from '../../../../src/resources/errors.js';
import nockReqHeaderOverwrite from '../../../req-header-overwrite.js';

Expand Down Expand Up @@ -99,4 +100,27 @@ describe('Testing get-item', {
result.ids.push(1, 2, 3);
expect(def).to.deep.equal([]);
});

it('Testing getItem with custom marshalling', async () => {
await generateTable({
extraAttrs: {
bin: {
type: 'binary',
default: () => [],
marshall: (item) => zlib.gzipSync(JSON.stringify(item), { level: 9 }),
unmarshall: (item) => JSON.parse(zlib.gunzipSync(item))
}
}
});

const { item } = await model.create({
id: '123',
name: 'name',
age: 50,
bin: [{ a: 1 }]
});
const key = { id: item.id, name: item.name };
const result = await model.getItem(key, { toReturn: ['bin'] });
expect(result).to.deep.equal({ bin: [{ a: 1 }] });
});
});
Loading

0 comments on commit 428de7d

Please sign in to comment.