lemon-core
Version:
Lemon Serverless Micro-Service Platform
338 lines • 13.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CosmosService = void 0;
/**
* `cosmos-service.ts`
* - common service for cosmos
*
* @author Ian Kim <ian@lemoncloud.io>
* @date 2023-08-02 initial version
*
* @copyright (C) 2023 LemonCloud Co Ltd. - All Rights Reserved.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const engine_1 = require("../../engine");
require("dotenv/config");
// import { KeyVaultService } from '../azure'
const NS = engine_1.$U.NS('CSMS', 'green'); // NAMESPACE TO BE PRINTED.
const instance = () => {
return CosmosService.instance();
};
/**
* class: `CosmosService`
* - basic CRUD service for AZURE CosmosDB.
*/
class CosmosService {
// protected $kv: KeyVaultService;
constructor(options) {
this.hello = () => `cosmos-service:${this.options.tableName}`;
(0, engine_1._inf)(NS, `CosmosService(${options.databaseName}/${options.tableName}/${options.idName}${options.sortName ? '/' : ''}${options.sortName || ''})...`);
if (!options.databaseName)
throw new Error('.databaseName is required');
if (!options.tableName)
throw new Error('.tableName is required');
if (!options.idName)
throw new Error('.idName is required');
this.options = options;
}
// public static $kv: KeyVaultService = new KeyVaultService();
static instance() {
return __awaiter(this, void 0, void 0, function* () {
const { CosmosClient } = yield require('@azure/cosmos');
const account = process.env.COSMOS_DB_ACCOUNT;
const endpoint = `https://${account}.documents.azure.com:443/`;
const key = process.env.COSMOS_ACCOUNT_KEY;
// const endpoint = await CosmosService.$kv.decrypt(process.env.AZ_COSMOS_ENDPOINT);
// const key = await CosmosService.$kv.decrypt(process.env.AZ_COSMOS_KEY);
const client = new CosmosClient({ endpoint: endpoint, key: key });
return { client };
});
}
createTable() {
return __awaiter(this, void 0, void 0, function* () {
const { databaseName, tableName, idName } = this.options;
const { client } = yield CosmosService.instance();
const { database } = yield client.databases.createIfNotExists({ id: databaseName });
const { container } = yield database.containers.createIfNotExists({
id: tableName,
partitionKey: { paths: [`/${idName}`] },
});
});
}
/**
* save-item
* - save whole data with param (use update if partial save)
*
* **WARN** overwrited if exists.
*
* @param id
* @param item
*/
saveItem(_id, item) {
return __awaiter(this, void 0, void 0, function* () {
const { databaseName, tableName, idName } = this.options;
const { client } = yield CosmosService.instance();
const querySpec = {
query: `SELECT * FROM c WHERE c[@idName] = @id`,
parameters: [
{
name: '@id',
value: _id,
},
{
name: '@idName',
value: idName,
},
],
};
const { resources: readDoc } = yield client
.database(databaseName)
.container(tableName)
.items.query(querySpec)
.fetchAll();
const payload = { [idName]: _id };
for (const [key, value] of Object.entries(item)) {
payload[key] = value;
}
if (!payload.hasOwnProperty('id')) {
payload['id'] = _id;
}
if (readDoc.length === 0) {
const { resources: saveDoc } = yield client
.database(databaseName)
.container(tableName)
.items.create(payload);
return saveDoc;
}
const _a = readDoc[0], { id, _rid, _self, _etag, _attachments, _ts } = _a, _rest = __rest(_a, ["id", "_rid", "_self", "_etag", "_attachments", "_ts"]);
const update_payload = Object.assign(Object.assign({}, payload), { id,
_rid,
_self,
_attachments });
const { resource: updateDoc } = yield client
.database(databaseName)
.container(tableName)
.item(readDoc[0].id)
.replace(update_payload);
const result = {};
for (const key in payload) {
if (payload.hasOwnProperty(key)) {
result[key] = payload[key];
}
}
return result;
});
}
/**
* read-item
* - read whole data of item.
*
* @param id
* @param sort
*/
readItem(_id, sort) {
return __awaiter(this, void 0, void 0, function* () {
const { databaseName, tableName, idName } = this.options;
const { client } = yield CosmosService.instance();
const querySpec = {
query: `SELECT * FROM c WHERE c[@idName] = @id`,
parameters: [
{
name: '@id',
value: _id,
},
{
name: '@idName',
value: idName,
},
],
};
const { resources: readDoc } = yield client
.database(databaseName)
.container(tableName)
.items.query(querySpec)
.fetchAll();
// ! Error occurs when try-catch is used
if (readDoc.length > 0) {
const rest = __rest(readDoc[0], []);
return rest;
}
if (readDoc.length === 0) {
const notFoundMessage = `404 NOT FOUND - ${idName}:${_id}`;
throw new Error(notFoundMessage);
}
});
}
/**
* delete-item
* - destroy whole data of item.
*
* @param id
* @param sort
*/
deleteItem(_id, sort) {
return __awaiter(this, void 0, void 0, function* () {
const { databaseName, tableName, idName } = this.options;
const { client } = yield CosmosService.instance();
const querySpec = {
query: `SELECT * FROM c WHERE c[@idName] = @id`,
parameters: [
{
name: '@id',
value: _id,
},
{
name: '@idName',
value: idName,
},
],
};
const { resources: readDoc } = yield client
.database(databaseName)
.container(tableName)
.items.query(querySpec)
.fetchAll();
const { resource: deleteDoc } = yield client
.database(databaseName)
.container(tableName)
.item(readDoc[0].id, readDoc[0][idName]) //! id, partition key
.delete();
const _a = readDoc[0], { id } = _a, rest = __rest(_a, ["id"]);
return rest;
});
}
/**
* update-item (or increment-item)
* - update or create if not exists.
*
* @param id
* @param sort
* @param updates
* @param increments
*/
updateItem(_id, sort, updates, increments) {
return __awaiter(this, void 0, void 0, function* () {
const { databaseName, tableName, idName } = this.options;
const { client } = yield CosmosService.instance();
if (updates == null && increments == null) {
const message = '.slot (null) should be number!';
return message;
}
const querySpec = {
query: `SELECT * FROM c WHERE c[@idName] = @id`,
parameters: [
{
name: '@id',
value: _id,
},
{
name: '@idName',
value: idName,
},
],
};
const { resources: readDoc } = yield client
.database(databaseName)
.container(tableName)
.items.query(querySpec)
.fetchAll();
/**
*
* upsert
*
*/
if (readDoc.length === 0) {
const payload = { [idName]: _id };
if (updates !== null && updates !== undefined) {
for (const [key, value] of Object.entries(updates)) {
payload[key] = value;
}
}
if (increments !== null && increments !== undefined) {
for (const [key, value] of Object.entries(increments)) {
payload[key] = value;
}
}
if (!payload.hasOwnProperty('id')) {
payload['id'] = _id;
}
const update_payload = Object.assign({}, payload);
const { resource: updateDoc } = yield client
.database(databaseName)
.container(tableName)
.items.upsert(update_payload);
const result = {};
for (const key in payload) {
if (payload.hasOwnProperty(key)) {
result[key] = payload[key];
}
}
return result;
}
/**
*
* update
*
*/
const payload = { [idName]: _id };
if (updates !== null && updates !== undefined) {
for (const [key, value] of Object.entries(updates)) {
payload[key] = value;
}
}
if (increments !== null && increments !== undefined) {
for (const [key, value] of Object.entries(increments)) {
const existValue = readDoc[0][key] || 0;
payload[key] = value + existValue;
}
}
if (!payload.hasOwnProperty('id')) {
payload['id'] = _id;
}
const _a = readDoc[0], { _etag, _ts } = _a, _rest = __rest(_a, ["_etag", "_ts"]);
const update_payload = Object.assign(Object.assign({}, _rest), payload);
try {
// Compare the ETag values of the document and update only if they match for atomicity
const { resource: updateDoc } = yield client
.database(databaseName)
.container(tableName)
.item(readDoc[0].id)
.replace(update_payload, { accessCondition: { type: 'IfMatch', condition: readDoc[0]._etag } });
}
catch (error) {
// Handle concurrency conflict
const message = 'Increments do not satisfy atomicity';
return message;
}
const result = {};
for (const key in payload) {
if (payload.hasOwnProperty(key)) {
result[key] = payload[key];
}
}
return result;
});
}
}
exports.CosmosService = CosmosService;
//# sourceMappingURL=cosmos-service.js.map
;