UNPKG

lemon-core

Version:
338 lines 13.1 kB
"use strict"; 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