UNPKG

cosmos-db-repositories

Version:
181 lines (180 loc) 8.78 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const cosmos_1 = require("@azure/cosmos"); const entity_utils_1 = __importDefault(require("./entity.utils")); const uuid_1 = require("uuid"); const time_utils_1 = require("./time.utils"); let container; const containerInstance = () => __awaiter(void 0, void 0, void 0, function* () { if (!container) { const endpoint = process.env.AZURE_DATASOURCE_URL; const key = process.env.AZURE_DATASOURCE_KEY; const databaseId = process.env.AZURE_DATASOURCE_DATABASE_ID; const containerId = process.env.AZURE_DATASOURCE_CONTAINER_ID; container = new cosmos_1.CosmosClient({ endpoint, key }).database(databaseId).container(containerId); } return container; }); const save = (item) => __awaiter(void 0, void 0, void 0, function* () { const container = yield containerInstance(); const { resource } = yield container.items.create(item); return entity_utils_1.default.cleanUp(resource); }); function chunk(array, size) { return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size)); } const bulk = (operations, chunkSize = 100) => __awaiter(void 0, void 0, void 0, function* () { const partitionKey = process.env.AZURE_DATASOURCE_PARTITION_KEY; const failed = []; const token = (0, uuid_1.v4)(); const startDate = new Date(); let importDataCount = 0; let importDataFailedCount = 0; console.log('Start migrate (' + operations.length + ') users at ', startDate.toISOString()); // Group by partition key const groups = new Map(); for (const item of operations) { const pk = item.resourceBody[partitionKey]; item.partitionKey = pk; if (!groups.has(pk)) { groups.set(pk, []); } groups.get(pk).push(item); } const container = yield containerInstance(); for (const [partitionKey, groupItems] of groups) { const chunks = chunk(groupItems, chunkSize); let groupImportDataCount = 0; let groupImportDataFailedCount = 0; for (const chunkItems of chunks) { const response = yield container.items.bulk(chunkItems); let chunkImportDataCount = 0; let chunkImportDataFailedCount = 0; response.forEach((res, i) => { var _a, _b; if (res.statusCode >= 200 && res.statusCode < 300) { chunkImportDataCount++; groupImportDataCount++; importDataCount++; } else { chunkImportDataFailedCount++; groupImportDataFailedCount++; importDataFailedCount++; failed.push(chunkItems[i].resourceBody); console.error(`Failed upsert for item [${(_b = (_a = chunkItems[i]) === null || _a === void 0 ? void 0 : _a.resourceBody) === null || _b === void 0 ? void 0 : _b.id}] with statusCode: ${res.statusCode}`); } }); console.log(`Partition [${partitionKey}] Summary: ${groupImportDataCount} succeeded, ${groupImportDataFailedCount} failed of ${groupItems.length} total items`); } } const now = new Date(); const duration = (0, time_utils_1.timeDuration)(startDate.getTime(), now.getTime()); return { total: operations.length, imported: importDataCount, failed, duration, token, message: 'Data import [' + importDataCount + '] successfully completed and [' + importDataFailedCount + '] failed' }; }); const get = (id, partitionKey) => __awaiter(void 0, void 0, void 0, function* () { const container = yield containerInstance(); const { resource } = yield container.item(id, partitionKey).read(); return entity_utils_1.default.cleanUp(resource); }); const update = (updatedData) => __awaiter(void 0, void 0, void 0, function* () { const container = yield containerInstance(); const id = updatedData.id; const partitionKey = updatedData[process.env.AZURE_DATASOURCE_PARTITION_KEY]; const { resource: existingItem } = yield container.item(id, partitionKey).read(); // Merge the updates into the existing item const updatedItem = Object.assign(Object.assign({}, existingItem), updatedData); const { resource } = yield container.item(id, partitionKey).replace(updatedItem); return entity_utils_1.default.cleanUp(resource); }); const remove = (id, partitionKey) => __awaiter(void 0, void 0, void 0, function* () { const container = yield containerInstance(); const { resource } = yield container.item(id, partitionKey).delete(); return entity_utils_1.default.cleanUp(resource); }); const query = (querySpec, pageRequest) => __awaiter(void 0, void 0, void 0, function* () { const pageable = Object.assign({ pageIndex: 1, rowsPerPage: 25 }, (pageRequest || {})); const { pageIndex, rowsPerPage } = pageable; const container = yield containerInstance(); const totalItems = yield fetchTotalCount(querySpec, container); // Total item count // Iterate through pages until the desired pageIndex is reached const { results, continuationToken } = yield fetchResults(querySpec, pageable, container); const data = { pageIndex, rowsPerPage, totalItems, continuationToken, totalPages: Math.ceil(totalItems / rowsPerPage), content: results.map(entity_utils_1.default.cleanUp), map: (converter) => data.content = data.content.map(converter) }; return data; }); const nativeQuery = (query, options) => __awaiter(void 0, void 0, void 0, function* () { const container = yield containerInstance(); const queryIterator = container.items.query(query, options); // Fetch results for the current page const { resources: results } = yield queryIterator.fetchAll(); return results || []; }); const fetchTotalCount = (querySpec, container) => __awaiter(void 0, void 0, void 0, function* () { const { query, parameters } = querySpec; const tokens = query.split("WHERE"); let totalQuery = 'SELECT VALUE COUNT(1) FROM c '; if (tokens.length > 1) { totalQuery += 'WHERE' + tokens[1]; } const totalQuerySpec = { query: totalQuery.trim(), parameters }; const { resources } = yield container.items.query(totalQuerySpec).fetchAll(); return resources ? resources[0] : 0; // Single value for the total count }); const fetchPaginatedResults = (querySpec, rowsPerPage, continuationToken = null, container) => __awaiter(void 0, void 0, void 0, function* () { // Calculate the offset based on pageIndex and rowsPerPage const options = { maxItemCount: rowsPerPage, continuationToken, // Token for the next page }; const queryIterator = container.items.query(querySpec, options); // Fetch results for the current page const { resources: results, continuationToken: nextToken } = yield queryIterator.fetchNext(); return { results: results || [], nextToken }; }); const fetchResults = (querySpec, pageable, container) => __awaiter(void 0, void 0, void 0, function* () { const { pageIndex, rowsPerPage } = pageable; let continuationToken = null; let currentPageIndex = 1; // 1 base page index let pageResults = []; // Iterate through pages until the desired pageIndex is reached do { const { results, nextToken } = yield fetchPaginatedResults(querySpec, rowsPerPage, continuationToken, container); continuationToken = nextToken; if (currentPageIndex === pageIndex) { pageResults = results; break; } currentPageIndex++; } while (continuationToken); return { results: pageResults, continuationToken }; }); exports.default = { get, query, save, update, remove, bulk, nativeQuery, containerInstance };