firebase-lib-db
Version:
Database Lib to access Firestore (CRUD) and Cache Data
376 lines (370 loc) • 12.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/logger/index.ts
import winston from "winston";
var loggerInstance = null;
function GetLogger() {
if (!loggerInstance) {
loggerInstance = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.json(),
transports: [new winston.transports.Console()]
});
}
return loggerInstance;
}
var info = (message, rest) => {
log("info", message, rest);
};
var warn = (message, rest) => {
log("warn", message, rest);
};
var error = (message, rest) => {
log("error", message, rest);
};
var log = (severity, message, rest) => {
const logger = GetLogger()[severity];
if (rest.error instanceof Error) {
rest.error = {
message: rest.error.message,
stack: rest.error.stack
};
}
const metadata = __spreadValues({}, rest);
if (rest.operation) {
logger(
`[Database] [${severity.toUpperCase()}] [${rest.operation}] - ${message}`,
{ metadata }
);
} else {
logger(`[Database] [${severity.toUpperCase()}] - ${message}`, {
metadata
});
}
};
// src/util/ConnectionPool.ts
import { createClient } from "redis";
var endpoint = process.env.REDISCLOUD_URL;
var _RedisPool = class _RedisPool {
static GetConnection() {
return __async(this, null, function* () {
if (_RedisPool.pool.length > 0) {
const connection = _RedisPool.pool.pop();
if (connection)
return connection;
}
const newConnection = createClient({ url: endpoint });
try {
yield newConnection.connect();
} catch (err) {
throw new Error(`Erro ao conectar no Redis - ${err}`);
}
return newConnection;
});
}
static releaseConnection(connection) {
if (!endpoint || !connection.isOpen)
return;
if (_RedisPool.pool.length < _RedisPool.MAX_CONNECTION) {
_RedisPool.pool.push(connection);
} else {
connection.quit();
}
}
};
_RedisPool.MAX_CONNECTION = 10;
_RedisPool.pool = [];
var RedisPool = _RedisPool;
// src/v1/cache/Redis.cache.ts
var RedisRepository = class {
del(key) {
return __async(this, null, function* () {
if (process.env.REDISCLOUD_URL) {
const conn = yield RedisPool.GetConnection();
yield conn.DEL(key);
RedisPool.releaseConnection(conn);
}
});
}
get(key) {
return __async(this, null, function* () {
if (process.env.REDISCLOUD_URL) {
const conn = yield RedisPool.GetConnection();
try {
const data = yield conn.get(key);
if (!data)
throw new Error();
return JSON.parse(data);
} catch (e) {
return yield conn.GET(key);
} finally {
RedisPool.releaseConnection(conn);
}
}
});
}
set(key, obj, ttl) {
return __async(this, null, function* () {
if (process.env.REDISCLOUD_URL) {
const conn = yield RedisPool.GetConnection();
if (obj) {
yield conn.SETEX(key, !ttl ? 86400 : ttl, JSON.stringify(obj));
}
RedisPool.releaseConnection(conn);
}
});
}
delPattern(pattern) {
return __async(this, null, function* () {
if (process.env.REDISCLOUD_URL) {
const conn = yield RedisPool.GetConnection();
yield conn.EVAL(
"for _,k in ipairs(redis.call('keys','" + pattern + "')) do redis.call('del',k) end"
);
RedisPool.releaseConnection(conn);
}
});
}
getByPattern(pattern) {
return __async(this, null, function* () {
if (process.env.REDISCLOUD_URL) {
let cursor = 0;
const keys = [];
const conn = yield RedisPool.GetConnection();
do {
const reply = yield conn.scan(cursor, {
MATCH: pattern,
COUNT: 100
});
cursor = reply.cursor;
keys.push(...reply.keys);
} while (cursor !== 0);
const values = [];
for (const key of keys) {
const value = yield conn.get(key);
values.push(value);
}
RedisPool.releaseConnection(conn);
return values;
}
return [];
});
}
};
// src/v1/cache/index.ts
var cacheInstance = null;
function GetCache() {
if (!cacheInstance) {
cacheInstance = new RedisRepository();
}
return cacheInstance;
}
var cache_default = GetCache;
// src/v1/database/Firestore.ts
import crypto from "crypto";
var FirestoreDatabase = class {
constructor(firestore) {
this.__getCacheKey = (collectionName, data) => {
const hashStr = crypto.createHash("md5").update(JSON.stringify(data)).digest("hex");
return `${collectionName}::${hashStr}`;
};
this._cache = cache_default();
this._db = firestore;
}
Save(collectionName, data) {
return __async(this, null, function* () {
const operation = "SaveDocument";
try {
if (data.id) {
yield this._db.collection(collectionName).doc(data.id).set(__spreadProps(__spreadValues({}, data), { modified: Date.now() }));
info(
`The id was provided. Setting document [${data.id}] in collection ${collectionName} instead create a new one.`,
{ operation, collectionName, documentId: data.id }
);
yield this._cache.delPattern(`${collectionName}::*`);
return data.id;
}
const docRef = yield this._db.collection(collectionName).add(__spreadProps(__spreadValues({}, data), { modified: Date.now() }));
info(
`Creating a new document with id ${docRef.id} in collection ${collectionName}.`,
{ operation, collectionName, documentId: data.id }
);
yield this._cache.delPattern(`${collectionName}::*`);
return docRef.id;
} catch (err) {
error(`Could not Save Data in ${collectionName} - ${err.message}`, {
collectionName,
data,
operation
});
throw new Error("Could not save item in Database");
}
});
}
FindOne(collectionName, id) {
return __async(this, null, function* () {
const operation = "FindOne";
try {
const cacheKey = this.__getCacheKey(collectionName, id);
const cacheData = yield this._cache.get(cacheKey);
if (cacheData)
return cacheData;
const docRef = this._db.collection(collectionName).doc(id);
const docSnapshot = yield docRef.get();
if (docSnapshot.exists) {
info(
`Fetching data with sucess in collection ${collectionName} with id ${id}`,
{ collectionName, documentId: id, operation }
);
const data = docSnapshot.data();
this._cache.set(cacheKey, data);
return data;
}
warn(
`Don't exists any document in collection ${collectionName} with id ${id}`,
{ collectionName, documentId: id, operation }
);
return null;
} catch (err) {
error(
`Could not FindOne Data - ${collectionName}[${id}] - ${err.message}`,
{ collectionName, documentId: id, operation }
);
throw new Error("Could not fetch data. Try again later");
}
});
}
QueryData(collectionName, wheres, orderBy, noCache) {
return __async(this, null, function* () {
const operation = "QueryData";
try {
let queryRef = this._db.collection(collectionName);
wheres == null ? void 0 : wheres.forEach((where) => {
queryRef = queryRef.where(where == null ? void 0 : where.field, where == null ? void 0 : where.operator, where == null ? void 0 : where.value);
});
if (orderBy) {
queryRef = queryRef.orderBy(orderBy.field, orderBy.direction);
}
const cacheKey = this.__getCacheKey(collectionName, queryRef);
const cached = yield this._cache.get(cacheKey);
if (cached && !noCache)
return cached;
const querySnapshot = yield queryRef.get();
const res = querySnapshot.docs.map((doc) => doc.data());
if (!noCache)
this._cache.set(cacheKey, res);
return res;
} catch (err) {
error(
`Could not Query Data - ${collectionName}[Wheres: ${JSON.stringify(
wheres
)}][OrderBy >> Field: ${orderBy == null ? void 0 : orderBy.field} | Direction: ${orderBy == null ? void 0 : orderBy.direction}] - ${err.message}`,
{ collectionName, operation }
);
throw new Error(err.message);
}
});
}
Delete(collectionName, query) {
return __async(this, null, function* () {
const operation = "DeleteData";
try {
const idsDeleted = [];
const queryResult = this._db.collection(collectionName).where(query.field, "==", query.value);
const snapshot = yield queryResult.get();
const batch = this._db.batch();
snapshot.forEach((doc) => {
batch.delete(doc.ref);
idsDeleted.push(doc.id);
});
yield batch.commit();
info(
`Deleting data in collection ${collectionName}. Ids deleted: ${idsDeleted}`,
{ collectionName, operation }
);
yield this._cache.delPattern(`${collectionName}::*`);
} catch (err) {
error(
`Could not Delete Data - ${collectionName}[Wheres: ${JSON.stringify(
query
)}] - ${err.message}`,
{ collectionName, operation }
);
throw new Error("Error while delete. Try again later");
}
});
}
Update(collectionName, id, data) {
return __async(this, null, function* () {
const operation = "UpdateDocument";
try {
const docRef = this._db.collection(collectionName).doc(id);
yield docRef.update(__spreadProps(__spreadValues({}, data), { modified: Date.now() }));
info(`Updating document ${id} in collection ${collectionName}`, {
operation,
documentId: id,
collectionName
});
yield this._cache.delPattern(`${collectionName}::*`);
return true;
} catch (err) {
error(
`Could not Update Document - ${collectionName}[${id}] - ${err.message}`,
{ collectionName, documentId: id, data, operation }
);
throw new Error("Error while Update data. Try again later.");
}
});
}
};
// src/v1/database/index.ts
var databaseInstance = null;
function GetDatabase(firestore) {
if (!databaseInstance) {
databaseInstance = new FirestoreDatabase(firestore);
}
return databaseInstance;
}
var database_default = GetDatabase;
export {
cache_default as GetCache,
database_default as GetDatabase
};
//# sourceMappingURL=index.mjs.map