dynolink
Version:
A TypeScript ORM for DynamoDB
186 lines (185 loc) • 6.73 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseRepository = void 0;
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
const decorators_1 = require("./decorators");
const query_builder_1 = require("./query-builder");
const uuid_1 = require("uuid");
/**
* BaseRepository class for managing DynamoDB entities.
*/
class BaseRepository {
constructor(client) {
this.client = client;
this.metadata = (0, decorators_1.getEntityMetadata)(this.getEntityType());
this.tableName = this.metadata.tableName;
}
/**
* Applies default values and auto-generated IDs to the entity.
* @param entity
* @protected
*/
applyDefaultsAndAutoId(entity) {
for (const [key, attr] of this.metadata.attributes.entries()) {
const val = entity[key];
if (attr.autoId && !val)
entity[key] = (0, uuid_1.v4)();
else if ((val === undefined || val === null) && attr.defaultValue !== undefined)
entity[key] = typeof attr.defaultValue === 'function'
? attr.defaultValue()
: attr.defaultValue;
}
return entity;
}
/**
* Transforms the entity using the defined transformers.
* @param entity
* @param dir
* @protected
*/
transform(entity, dir) {
for (const [key, attr] of this.metadata.attributes.entries()) {
const transformer = attr.transformer;
if (transformer)
entity[key] = transformer(entity[key], dir);
}
return entity;
}
/**
* Saves the entity to DynamoDB.
* @param entity
*/
async save(entity) {
this.applyDefaultsAndAutoId(entity);
const data = (0, util_dynamodb_1.marshall)(this.transform(entity, 'toDb'), { removeUndefinedValues: true, convertClassInstanceToMap: true });
await this.client.send(new client_dynamodb_1.PutItemCommand({ TableName: this.tableName, Item: data }));
}
/**
* Updates an entity in DynamoDB.
* @param key
* @param updatedEntity
*/
async update(key, updatedEntity) {
const fullItem = { ...key, ...updatedEntity };
const data = (0, util_dynamodb_1.marshall)(this.transform(fullItem, 'toDb'), { removeUndefinedValues: true, convertClassInstanceToMap: true });
await this.client.send(new client_dynamodb_1.PutItemCommand({ TableName: this.tableName, Item: data }));
}
/**
* Updates partial fields of an entity in DynamoDB.
* @param key
* @param updates
*/
async updatePartial(key, updates) {
const pk = this.getKey(key);
const expressions = [];
const names = {};
const values = {};
Object.entries(updates).forEach(([k, v], i) => {
const attr = `#attr${i}`;
const val = `:val${i}`;
expressions.push(`${attr} = ${val}`);
names[attr] = k;
values[val] = v;
});
await this.client.send(new client_dynamodb_1.UpdateItemCommand({
TableName: this.tableName,
Key: (0, util_dynamodb_1.marshall)(pk),
UpdateExpression: `SET ${expressions.join(', ')}`,
ExpressionAttributeNames: names,
ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(values),
}));
}
/**
* Deletes an entity from DynamoDB.
* @param key
*/
async delete(key) {
const pk = this.getKey(key);
await this.client.send(new client_dynamodb_1.DeleteItemCommand({
TableName: this.tableName,
Key: (0, util_dynamodb_1.marshall)(pk),
}));
}
/**
* Finds an entity by its primary key.
* @param key
*/
async findOne(key) {
const pk = this.getKey(key);
const res = await this.client.send(new client_dynamodb_1.GetItemCommand({ TableName: this.tableName, Key: (0, util_dynamodb_1.marshall)(pk) }));
if (!res.Item)
return null;
const entity = (0, util_dynamodb_1.unmarshall)(res.Item);
return this.transform(entity, 'fromDb');
}
/**
* Batch retrieves entities from DynamoDB.
* @param keys
*/
async batchGet(keys) {
const requestKeys = keys.map((key) => (0, util_dynamodb_1.marshall)(this.getKey(key)));
const command = new client_dynamodb_1.BatchGetItemCommand({
RequestItems: {
[this.tableName]: {
Keys: requestKeys,
},
},
});
const result = await this.client.send(command);
const items = result.Responses?.[this.tableName] ?? [];
return items.map((item) => this.transform((0, util_dynamodb_1.unmarshall)(item), 'fromDb'));
}
/**
* Batch writes entities to DynamoDB.
* @param entities
* @param action
*/
async batchWrite(entities, action = 'put') {
const requestItems = entities.map((entity) => {
if (action === 'put') {
const transformed = (0, util_dynamodb_1.marshall)(this.transform(entity, 'toDb'), {
removeUndefinedValues: true,
convertClassInstanceToMap: true,
});
return { PutRequest: { Item: transformed } };
}
else {
return { DeleteRequest: { Key: (0, util_dynamodb_1.marshall)(this.getKey(entity)) } };
}
});
await this.client.send(new client_dynamodb_1.BatchWriteItemCommand({
RequestItems: {
[this.tableName]: requestItems,
},
}));
}
/**
* Queries the table using the provided filters.
* @param filters
*/
async query(filters) {
const builder = new query_builder_1.QueryBuilder(this.metadata);
const params = builder.build(filters);
const res = await this.client.send(new client_dynamodb_1.QueryCommand({
TableName: this.tableName,
...params,
}));
return (res.Items || []).map((item) => this.transform((0, util_dynamodb_1.unmarshall)(item), 'fromDb'));
}
/**
* Generates the key for the entity based on its partition and sort keys.
* @param input
* @protected
*/
getKey(input) {
const keys = {};
for (const [key, attr] of this.metadata.attributes.entries()) {
if (attr.isPartitionKey || attr.isSortKey) {
keys[key] = input[key];
}
}
return keys;
}
}
exports.BaseRepository = BaseRepository;