firebase-lib-db
Version:
Database Lib to access Firestore (CRUD) and Cache Data
411 lines (404 loc) • 13.8 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
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 __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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/v1/index.ts
var v1_exports = {};
__export(v1_exports, {
GetCache: () => cache_default,
GetDatabase: () => database_default
});
module.exports = __toCommonJS(v1_exports);
// src/logger/index.ts
var import_winston = __toESM(require("winston"));
var loggerInstance = null;
function GetLogger() {
if (!loggerInstance) {
loggerInstance = import_winston.default.createLogger({
level: process.env.LOG_LEVEL || "info",
format: import_winston.default.format.json(),
transports: [new import_winston.default.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
var import_redis = require("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 = (0, import_redis.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
var import_crypto = __toESM(require("crypto"));
var FirestoreDatabase = class {
constructor(firestore) {
this.__getCacheKey = (collectionName, data) => {
const hashStr = import_crypto.default.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;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
GetCache,
GetDatabase
});
//# sourceMappingURL=index.js.map