Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@discue/mongodb-resource-client

Package Overview
Dependencies
Maintainers
0
Versions
43
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@discue/mongodb-resource-client - npm Package Compare versions

Comparing version 0.40.0 to 1.0.0

lib/tracer.d.ts

136

lib/aggregations.d.ts

@@ -1,32 +0,61 @@

export function EQUALS(field: string, target: unknown): any;
export function EQUALS_ALL(object: any, { prefix }?: {
prefix: any;
}): any;
export function EQUALS_ANY_OF(field: string, targets: any[]): any;
export function EQUALS_WILDCARD(): any;
export function LESS_THAN(field: string, target: unknown): any;
export function LESS_THAN_OR_EQUAL(field: string, target: unknown): any;
export function GREATER_THAN(field: string, target: unknown): any;
export function GREATER_THAN_OR_EQUAL(field: string, target: unknown): any;
export function LIMIT(amount: number): any;
export function PROJECT(projection: any): any;
export function SORT_BY_ASC(...field: string[]): any;
export function SORT_BY_DESC(...field: string[]): any;
export function SORT_BY_COUNT(field: string): any;
export function UNWIND(field: string): any;
export function COUNT(): any;
export function LOOKUP({ from, as, localField, foreignField, pipeline }: LookupOptions): any;
export function REDUCE({ input, initialValue, inExpression }: ReduceOptions): any;
export function APPEND_OBJECTS(...expressions: any[]): any;
export function LET({ vars, inExpression }: LetOptions): any;
export function TO_OBJECT({ key, value }: ToObjectOptions): any;
export function CONCAT_STRINGS(...strings: any[]): any;
export function JOIN_STRINGS(separator: string, ...strings: any[]): any;
export function CONCAT_ARRAYS(): any;
export function ELEMENT_AT(arrayName: string, index: number): any;
export function AS_ROOT(fieldName: string): any;
export function EXISTS(fieldName: string): any;
export function TO_LONG(fieldname: string): object;
export function DATE_TRUNC(fieldname: string, { unit, timezone, binSize, startOfWeek }: string): any;
export function INDEX_STATS(): any;
export function EQUALS(field: string, target: string): Match;
export function EQUALS_ALL(object: object, { prefix }?: {
prefix: string;
}): Match;
export function EQUALS_ANY_OF(field: string, targets: any[]): Match;
export function EQUALS_WILDCARD(): Match;
export function LESS_THAN(field: string, target: string): Match;
export function LESS_THAN_OR_EQUAL(field: string, target: string): Match;
export function GREATER_THAN(field: string, target: string): Match;
export function GREATER_THAN_OR_EQUAL(field: string, target: string): Match;
export function LIMIT(amount: number): Limit;
export function PROJECT(projection: object): Projection;
export function SORT_BY_ASC(...field: string[]): Sort;
export function SORT_BY_DESC(...field: string[]): Sort;
export function SORT_BY_COUNT(field: string): SortByCount;
export function UNWIND(field: string): Unwind;
export function COUNT(): Count;
export function LOOKUP({ from, as, localField, foreignField, pipeline }: LookupOptions): Lookup;
export function REDUCE({ input, initialValue, inExpression }: ReduceOptions): Reduce;
export function APPEND_OBJECTS(...expressions: any[]): AppendObjects;
export function LET({ vars, inExpression }: LetOptions): Let;
export function TO_OBJECT({ key, value }: ToObjectOptions): ToObject;
export function CONCAT_STRINGS(...strings: any): Concat;
export function JOIN_STRINGS(separator: string, ...strings: string[]): Concat;
export function CONCAT_ARRAYS(): ConcatArray;
export function ELEMENT_AT(arrayName: string, index: number): ArrayElementAt;
export function AS_ROOT(fieldName: string): ReplaceRoot;
export function EXISTS(fieldName: string): Match;
export function TO_LONG(fieldname: string): Set;
export function DATE_TRUNC(fieldname: string, { unit, timezone, binSize, startOfWeek }: {
unit: string;
binSize?: number;
timezone?: string;
startOfWeek?: string;
}): DateTrunc;
export function INDEX_STATS(): IndexStats;
export type Match = {
$match: object;
};
export type Limit = {
$limit: object;
};
export type Projection = {
$limit: object;
};
export type Sort = {
$sort: object;
};
export type SortByCount = {
$sortByCount: object;
};
export type Unwind = {
$unwind: object;
};
export type Count = {
$count: object;
};
export type Lookup = {
$lookup: object;
};
export type LookupOptions = {

@@ -50,6 +79,9 @@ /**

/**
* pipeline for aggregation of the lookup query
* pipeline for of the lookup query
*/
pipeline: any[];
};
export type Reduce = {
$reduce: object;
};
export type ReduceOptions = {

@@ -67,4 +99,10 @@ /**

*/
inExpression: any;
inExpression: object;
};
export type AppendObjects = {
mergeObjects: object;
};
export type Let = {
$let: object;
};
export type LetOptions = {

@@ -74,8 +112,11 @@ /**

*/
vars: any;
vars: object;
/**
* any expression
*/
inExpression: any;
inExpression: object;
};
export type ToObject = {
arrayToObject: object;
};
export type ToObjectOptions = {

@@ -89,3 +130,26 @@ /**

*/
expression: any;
expression: object;
};
export type Concat = {
$concat: object;
};
export type ConcatArray = {
$concatArrays: Array<string>;
};
export type ArrayElementAt = {
$arrayElemAt: Array<string>;
};
export type ReplaceRoot = {
$replaceRoot: {
newRoot: string;
};
};
export type Set = {
$set: object;
};
export type DateTrunc = {
$dateTrunc: object;
};
export type IndexStats = {
$indexStats: object;
};

@@ -1,12 +0,38 @@

'use strict'
/**
*
* @param {string} string
* @returns {string}
*/
function withLeadingDollar(string) {
if (!string.startsWith('$')) {
string = `$${string}`
}
return string
}
/**
* Returns a $match aggregation stage
*
* @param {Array.<string>} array
* @param {string} value
*/
function reduce(array, value) {
return array.reduce((context, element) => {
return Object.assign(context, {
[element]: value
})
}, {})
}
/**
* @typedef Match
* @property {object} $match
*/
/**
*
* @param {String} field the target field name
* @param {unknown} target the target value
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {string} target the target value
* @returns {Match}
*/
module.exports.EQUALS = (field, target) => {
export const EQUALS = (field, target) => {
return {

@@ -20,9 +46,9 @@ $match: {

/**
* Returns a $match aggregation stage for all entries of the object
*
* @param {object} field the target field name
* @returns {Object} an object containing a MongoDb projection object
*
* @param {object} object an object containing they key value pairs to query for
* @param {object} [options] options
* @param {string} options.prefix a prefix to add to all keys
* @returns {Match}
*/
module.exports.EQUALS_ALL = (object, { prefix } = {}) => {
export const EQUALS_ALL = (object, { prefix } = {}) => {
const keyPrefix = prefix ? `${prefix}.` : ''

@@ -37,10 +63,8 @@ const query = Object.entries(object).reduce((context, [key, value]) => {

/**
* Returns a $in aggregation stage
*
* @param {String} field the target field name
* @param {Array} targets the target values
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {Array} targets the target value
* @returns {Match}
*/
module.exports.EQUALS_ANY_OF = (field, targets) => {
export const EQUALS_ANY_OF = (field, targets) => {
return {

@@ -56,7 +80,6 @@ $match: {

/**
* Matches all documents of a collection
*
* @returns {Object} an object containing a MongoDb match object
*
* @returns {Match}
*/
module.exports.EQUALS_WILDCARD = () => {
export const EQUALS_WILDCARD = () => {
return {

@@ -67,12 +90,9 @@ $match: { _id: { $exists: true } }

/**
* Returns a $match aggregation stage
*
* @param {String} field the target field name
* @param {unknown} target the target value
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {string} target the target value
* @returns {Match}
*/
module.exports.LESS_THAN = (field, target) => {
export const LESS_THAN = (field, target) => {
return {

@@ -88,10 +108,8 @@ $match: {

/**
* Returns a $match aggregation stage
*
* @param {String} field the target field name
* @param {unknown} target the target value
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {string} target the target value
* @returns {Match}
*/
module.exports.LESS_THAN_OR_EQUAL = (field, target) => {
export const LESS_THAN_OR_EQUAL = (field, target) => {
return {

@@ -107,10 +125,8 @@ $match: {

/**
* Returns a $match aggregation stage
*
* @param {String} field the target field name
* @param {unknown} target the target value
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {string} target the target value
* @returns {Match}
*/
module.exports.GREATER_THAN = (field, target) => {
export const GREATER_THAN = (field, target) => {
return {

@@ -126,10 +142,8 @@ $match: {

/**
* Returns a $match aggregation stage
*
* @param {String} field the target field name
* @param {unknown} target the target value
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field the name of the field
* @param {string} target the target value
* @returns {Match}
*/
module.exports.GREATER_THAN_OR_EQUAL = (field, target) => {
export const GREATER_THAN_OR_EQUAL = (field, target) => {
return {

@@ -145,9 +159,12 @@ $match: {

/**
* Returns a $limit aggregation stage
* @typedef Limit
* @property {object} $limit
*/
/**
*
* @param {Number} amount the desired maximum number of elements the query should return
* @returns {Object} an object containing a MongoDb projection object
*
* @param {number} amount the amount of documents to return
* @returns {Limit}
*/
module.exports.LIMIT = (amount) => {
export const LIMIT = (amount) => {
return {

@@ -159,9 +176,12 @@ $limit: amount

/**
* Returns a $sort aggregation stage
* @typedef Projection
* @property {object} $limit
*/
/**
*
* @param {Object} projection
* @returns {Object} an object containing a MongoDb projection object
*
* @param {object} projection the projection object
* @returns {Projection}
*/
module.exports.PROJECT = (projection) => {
export const PROJECT = (projection) => {
return {

@@ -173,9 +193,12 @@ $project: projection

/**
* Returns a $sort aggregation stage
* @typedef Sort
* @property {object} $sort
*/
/**
*
* @param {...String} field the target field name
* @returns {Object} an object containing a MongoDb projection object
*
* @param {...string} field
* @returns {Sort}
*/
module.exports.SORT_BY_ASC = (...field) => {
export const SORT_BY_ASC = (...field) => {
return {

@@ -187,9 +210,7 @@ $sort: reduce(field, 1)

/**
* Returns a $sort aggregation stage
*
* @param {...String} field the target field name
* @returns {Object} an object containing a MongoDb projection object
*
* @param {...string} field
* @returns {Sort}
*/
module.exports.SORT_BY_DESC = (...field) => {
export const SORT_BY_DESC = (...field) => {
return {

@@ -201,9 +222,12 @@ $sort: reduce(field, -1)

/**
* Returns a $sortByCount aggregation stage. Will add required $ prefix to field if missing.
* @typedef SortByCount
* @property {object} $sortByCount
*/
/**
*
* @param {String} field the target field name
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field
* @returns {SortByCount}
*/
module.exports.SORT_BY_COUNT = (field) => {
export const SORT_BY_COUNT = (field) => {
return {

@@ -215,9 +239,12 @@ $sortByCount: withLeadingDollar(field)

/**
* Returns a $unwind aggregation stage. Will add required $ prefix to field if missing.
* @typedef Unwind
* @property {object} $unwind
*/
/**
*
* @param {String} field the target field name
* @returns {Object} an object containing a MongoDb projection object
*
* @param {string} field
* @returns {Unwind}
*/
module.exports.UNWIND = (field) => {
export const UNWIND = (field) => {
return {

@@ -229,8 +256,11 @@ $unwind: withLeadingDollar(field)

/**
* Returns a $count aggregation stage
* @typedef Count
* @property {object} $count
*/
/**
*
* @returns {Object} an object containing a MongoDb projection object
*
* @returns {Count}
*/
module.exports.COUNT = () => {
export const COUNT = () => {
return {

@@ -242,8 +272,13 @@ $count: 'count'

/**
* @typedef Lookup
* @property {object} $lookup
*/
/**
* @typedef LookupOptions
* @property {String} from the collection to lookup from
* @property {String} as name of the merged field
* @property {String} localField local field name
* @property {String} foreignField field name of the `from` collection
* @property {Array} pipeline pipeline for aggregation of the lookup query
* @property {string} from the collection to lookup from
* @property {string} as name of the merged field
* @property {string} localField local field name
* @property {string} foreignField field name of the `from` collection
* @property {Array} pipeline pipeline for of the lookup query
*/

@@ -254,5 +289,5 @@

* @param {LookupOptions} options
* @returns {Object}
* @returns {Lookup}
*/
module.exports.LOOKUP = ({ from, as, localField, foreignField = 'id', pipeline = [] }) => {
export const LOOKUP = ({ from, as, localField, foreignField = 'id', pipeline = [] }) => {
return {

@@ -266,6 +301,11 @@ $lookup: {

/**
* @typedef Reduce
* @property {object} $reduce
*/
/**
* @typedef ReduceOptions
* @property {String} input any expression resolving to an array
* @property {string} input any expression resolving to an array
* @property {any} initialValue the initial value used for reduction
* @property {Object} inExpression any expression applied to each element of the array
* @property {object} inExpression any expression applied to each element of the array
*/

@@ -276,5 +316,5 @@

* @param {ReduceOptions} options
* @returns {Object}
* @returns {Reduce}
*/
module.exports.REDUCE = ({ input, initialValue = {}, inExpression }) => {
export const REDUCE = ({ input, initialValue = {}, inExpression }) => {
return {

@@ -288,8 +328,13 @@ $reduce: {

/**
* @typedef AppendObjects
* @property {object} mergeObjects
*/
/**
* Returns a $mergeObjects expression that appends the result of the given expressions to $$value
*
* @param {...any} expressions a list of expressions that evaluate to an object
* @returns {Object}
* @returns {AppendObjects}
*/
module.exports.APPEND_OBJECTS = (...expressions) => {
export const APPEND_OBJECTS = (...expressions) => {
return {

@@ -301,5 +346,10 @@ $mergeObjects: ['$$value', ...expressions]

/**
* @typedef Let
* @property {object} $let
*/
/**
* @typedef LetOptions
* @property {Object} vars an object defining additional variables for the expression
* @property {Object} inExpression any expression
* @property {object} vars an object defining additional variables for the expression
* @property {object} inExpression any expression
*/

@@ -310,5 +360,5 @@

* @param {LetOptions} options
* @returns {Object}
* @returns {Let}
*/
module.exports.LET = ({ vars, inExpression }) => {
export const LET = ({ vars, inExpression }) => {
return {

@@ -322,5 +372,10 @@ $let: {

/**
* @typedef ToObject
* @property {object} arrayToObject
*/
/**
* @typedef ToObjectOptions
* @property {String} key the object key
* @property {Object} expression any expression
* @property {string} key the object key
* @property {object} expression any expression
*/

@@ -331,5 +386,5 @@

* @param {ToObjectOptions} options
* @returns {Object}
* @returns {ToObject}
*/
module.exports.TO_OBJECT = ({ key = '$$this', value }) => {
export const TO_OBJECT = ({ key = '$$this', value }) => {
return {

@@ -348,7 +403,12 @@ $arrayToObject: [

/**
* @typedef Concat
* @property {object} $concat
*/
/**
*
* @param {...any} strings strings to concat
* @returns {Object}
* @param {...strings} strings strings to concat
* @returns {Concat}
*/
module.exports.CONCAT_STRINGS = (...strings) => {
export const CONCAT_STRINGS = (...strings) => {
return {

@@ -359,9 +419,10 @@ $concat: strings

/**
*
* @param {String} separator separator to be used for joining given strings
* @param {...any} strings strings to concat
* @returns {Object}
* @param {string} separator separator to be used for joining given strings
* @param {...string} strings strings to concat
* @returns {Concat}
*/
module.exports.JOIN_STRINGS = (separator, ...strings) => {
export const JOIN_STRINGS = (separator, ...strings) => {
const array = strings.reduce((context, next) => {

@@ -371,10 +432,15 @@ context.push(separator, next)

}, [])
return module.exports.CONCAT_STRINGS.apply(null, array)
return CONCAT_STRINGS.apply(null, array)
}
/**
* @typedef ConcatArray
* @property {Array.<string>} $concatArrays
*/
/**
*
* @returns {Object}
* @returns {ConcatArray}
*/
module.exports.CONCAT_ARRAYS = () => {
export const CONCAT_ARRAYS = () => {
return {

@@ -389,8 +455,13 @@ $concatArrays: [

/**
* @typedef ArrayElementAt
* @property {Array.<string>} $arrayElemAt
*/
/**
*
* @param {String} arrayName name of the array
* @param {Number} index
* @returns {Object}
* @param {string} arrayName name of the array
* @param {number} index
* @returns {ArrayElementAt}
*/
module.exports.ELEMENT_AT = (arrayName, index) => {
export const ELEMENT_AT = (arrayName, index) => {
return {

@@ -402,7 +473,13 @@ $arrayElemAt: [withLeadingDollar(arrayName), index]

/**
* @typedef ReplaceRoot
* @property {object} $replaceRoot
* @property {string} $replaceRoot.newRoot
*/
/**
*
* @param {String} fieldName the field to return as root
* @returns {Object}
* @param {string} fieldName the field to return as root
* @returns {ReplaceRoot}
*/
module.exports.AS_ROOT = (fieldName) => {
export const AS_ROOT = (fieldName) => {
return {

@@ -417,6 +494,6 @@ $replaceRoot: {

*
* @param {String} fieldName the field to check for existence
* @returns {Object}
* @param {string} fieldName the target fieldname
* @returns {Match}
*/
module.exports.EXISTS = (fieldName) => {
export const EXISTS = (fieldName) => {
return {

@@ -428,7 +505,12 @@ $match: { [fieldName]: { $exists: true } }

/**
* @typedef Set
* @property {object} $set
*/
/**
*
* @param {string} fieldname
* @returns {object}
* @param {string} fieldname the target fieldname
* @returns {Set}
*/
module.exports.TO_LONG = (fieldname) => {
export const TO_LONG = (fieldname) => {
return {

@@ -444,13 +526,18 @@ $set: {

/**
* @typedef DateTrunc
* @property {object} $dateTrunc
*/
/**
*
* @param {string} fieldname
* @param {string} options
* @param {string} unit e.g. day, month, quarter, year.
* @param {number} [binSize=1] specifies the amount units
* @param {string} [timezone=Europe/Berlin]
* @param {string} [startOfWeek=monday]
* @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/
* @returns {Object}
* @param {object} options
* @param {string} options.unit e.g. day, month, quarter, year.
* @param {number} [options.binSize=1] specifies the amount units
* @param {string} [options.timezone=Europe/Berlin]
* @param {string} [options.startOfWeek=monday]
* @returns {DateTrunc}
* @see https://www.mongodb.com/docs/manual/reference/operator//dateTrunc/
*/
module.exports.DATE_TRUNC = (fieldname, { unit, timezone = 'Europe/Berlin', binSize = 1, startOfWeek = 'monday' }) => {
export const DATE_TRUNC = (fieldname, { unit, timezone = 'Europe/Berlin', binSize = 1, startOfWeek = 'monday' }) => {
return {

@@ -465,29 +552,14 @@ $dateTrunc: {

/**
*
* @returns {Object}
* @typedef IndexStats
* @property {object} $indexStats
*/
module.exports.INDEX_STATS = () => {
return {
'$indexStats': {}
}
}
/**
*
* @param {string} string
* @returns {string}
* @returns {IndexStats}
*/
function withLeadingDollar(string) {
if (!string.startsWith('$')) {
string = `$${string}`
export const INDEX_STATS = () => {
return {
'$indexStats': {}
}
return string
}
function reduce(array, value) {
return array.reduce((context, element) => {
return Object.assign(context, {
[element]: value
})
}, {})
}

@@ -1,102 +0,102 @@

export = exports;
declare class exports {
/**
* @public
* @param {ConstructorOptions} options
* @returns
*/
constructor({ client, databaseName, collectionName }?: ConstructorOptions);
/** @private */ private _mongoDbClient;
/** @private */ private _databaseName;
/** @private */ private _collectionName;
/** @private */ private _transactionsEnabled;
/**
* @protected
* @returns {import('mongodb').MongoClient}
*/
protected _getConnectedClient(): import("mongodb").MongoClient;
/**
* @protected
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {import('mongodb').Db}
*/
protected _getDb(givenClient?: import("mongodb").MongoClient): import("mongodb").Db;
/**
* @protected
* @param {import('mongodb').MongoClient} [collectionName]
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {Promise.<Collection>}
*/
protected _getCollection(collectionName?: import("mongodb").MongoClient, givenClient?: import("mongodb").MongoClient): Promise<Collection>;
/**
* @protected
*/
protected _runWithActiveSpan(spanName: any, resourceIds: any, callback: any): Promise<any>;
/**
* @typedef {import('mongodb').ClientSession} ClientSession
*/
/**
* @callback WithSessionCallback
* @param {ClientSession} clientSession
*/
/**
* @protected
* @param {WithSessionCallback} callback
*/
protected _withTransaction(callback: (clientSession: import("mongodb").ClientSession) => any): Promise<any>;
/**
* @protected
* @param {object} options
* @returns {object}
*/
protected _passSessionIfTransactionEnabled(options: object): object;
/**
* @protected
* @returns {object}
*/
protected _createMetadata(): object;
/**
* @protected
* @returns {object}
*/
protected _createUpdateMetadata(): object;
/**
* Closes the database client
*
* @method close
* @returns {void}
*/
close(): void;
}
declare namespace exports {
export { ConstructorOptions, GetOptions, WithMongoClient, Collection, MongoClient };
}
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
declare const _default: {
new ({ client, databaseName, collectionName }?: ConstructorOptions): {
/** @private */ _mongoDbClient: any;
/** @private */ _databaseName: any;
/** @private */ _collectionName: any;
/** @private */ _transactionsEnabled: boolean;
/** @private */ _history: History;
/** @protected */ _emitter: Emittery<Record<PropertyKey, any>, Record<PropertyKey, any> & import("emittery").OmnipresentEventData, import("emittery").DatalessEventNames<Record<PropertyKey, any>>>;
/** @private */ _eventListenersCount: {
create: number;
update: number;
delete: number;
};
/**
* @returns {import('mongodb').MongoClient}
* @protected
*/
_getConnectedClient(): import("mongodb").MongoClient;
/**
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {import('mongodb').Db}
* @protected
*/
_getDb(givenClient?: import("mongodb").MongoClient): import("mongodb").Db;
/**
* @param {import('mongodb').MongoClient} [collectionName]
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {Promise.<Collection>}
* @protected
*/
_getCollection(collectionName?: import("mongodb").MongoClient, givenClient?: import("mongodb").MongoClient): Promise<Collection>;
/**
* @param {string} spanName
* @param {string | Array.<string> }resourceIds
* @param {Function} callback
* @protected
*/
_runWithActiveSpan(spanName: string, resourceIds: string | Array<string>, callback: Function): Promise<any>;
/**
* @typedef {import('mongodb').ClientSession} ClientSession
*/
/**
* @callback WithSessionCallback
* @param {ClientSession} clientSession
*/
/**
* @param {WithSessionCallback} callback
* @protected
*/
_withTransaction(callback: (clientSession: mongodb.ClientSession) => any): Promise<any>;
/**
* @param {object} options
* @returns {object}
* @protected
*/
_passSessionIfTransactionEnabled(options: object): object;
/**
* @returns {object}
* @protected
*/
_createMetadata(): object;
/**
* @returns {object}
* @protected
*/
_createUpdateMetadata(): object;
/**
* @callback AsyncFunction
* @returns {Promise.<any>}
*/
/**
* @param {('create'|'update'|'delete'|'close')} eventName
* @param {AsyncFunction} callback
* @returns {Function} a function to unsubscribe the listener
*/
on(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
/**
* @param {('create'|'update'|'delete'|'close')} eventName
* @param {AsyncFunction} callback
* @returns {Function} a function to unsubscribe the listener
*/
off(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
enableHistory(): void;
disableHistory(): void;
/**
* Closes the database client
*
* @function close
* @returns {void}
*/
close(): void;
};
};
type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
};
type WithMongoClient = {
client: import("mongodb").MongoClient;
};
type Collection = import("mongodb").Collection;
type MongoClient = import("mongodb").MongoClient;
export default _default;
export type ConstructorOptions = any;
export type GetOptions = any;
export type WithMongoClient = any;
export type Collection = import("mongodb").Collection;
export type MongoClient = import("mongodb").MongoClient;
import History from './history.js';
import Emittery from 'emittery';
import * as mongodb from 'mongodb';

@@ -1,17 +0,11 @@

'use strict'
import Emittery from 'emittery'
import * as mongodb from 'mongodb'
import History from './history.js'
import { withActiveSpan } from './tracer.js'
const { Timestamp } = require('mongodb')
const { createTracer } = require('@discue/open-telemetry-tracing')
const { name } = require('../package.json')
const { Timestamp } = mongodb
/**
* @protected
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set

@@ -23,20 +17,20 @@ * @property {string} [databaseName=null] name of the mongodb database

/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @protected
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef WithMongoClient
* @name WithMongoClient
* @property {import('mongodb').MongoClient} client
* @protected
* @typedef WithMongoClient
* @property {import('mongodb').MongoClient} client
*/
/**
* @private
* @typedef {import('mongodb').Collection} Collection
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/

@@ -51,8 +45,8 @@

*/
module.exports = class {
export default (class {
/**
* @param {ConstructorOptions} options
* @returns
* @public
* @param {ConstructorOptions} options
* @returns
*/

@@ -62,15 +56,28 @@ constructor({ client, databaseName, collectionName } = {}) {

/** @private */ this._mongoDbClient = client
} else {
}
else {
throw new Error('Configuration Error. `client` needs to be set.')
}
/** @private */ this._databaseName = process.env.DSQ_MONGODB_RESOURCE_CLIENT_DB_NAME || databaseName
/** @private */ this._collectionName = collectionName
/** @private */ this._transactionsEnabled = process.env.DSQ_MONGOD_ENABLE_TRANSACTIONS === 'true'
/** @private */ this._history = null
/** @private */ this._transactionsEnabled = process.env.DSQ_MONGOD_ENABLE_TRANSACTIONS === 'true'
/** @protected */ this._emitter = new Emittery()
/** @private */ this._eventListenersCount = {
create: 0,
update: 0,
delete: 0
}
this._emitter.on(Emittery.listenerAdded, ({ eventName }) => {
this._eventListenersCount[eventName]++
})
this._emitter.on(Emittery.listenerRemoved, ({ eventName }) => {
this._eventListenersCount[eventName]--
})
}
/**
* @returns {import('mongodb').MongoClient}
* @protected
* @returns {import('mongodb').MongoClient}
*/

@@ -80,7 +87,6 @@ async _getConnectedClient() {

}
/**
* @protected
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {import('mongodb').Db}
* @protected
*/

@@ -93,8 +99,7 @@ async _getDb(givenClient) {

}
/**
* @protected
* @param {import('mongodb').MongoClient} [collectionName]
* @param {import('mongodb').MongoClient} [givenClient]
* @returns {Promise.<Collection>}
* @protected
*/

@@ -111,8 +116,10 @@ async _getCollection(collectionName, givenClient) {

}
/**
* @param {string} spanName
* @param {string | Array.<string> }resourceIds
* @param {Function} callback
* @protected
*/
async _runWithActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
return withActiveSpan(`${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}

@@ -123,3 +130,2 @@

*/
/**

@@ -129,6 +135,5 @@ * @callback WithSessionCallback

*/
/**
* @param {WithSessionCallback} callback
* @protected
* @param {WithSessionCallback} callback
*/

@@ -144,3 +149,4 @@ async _withTransaction(callback) {

})
} catch (e) {
}
catch (e) {
if (session.transaction.isActive && !session.transaction.isCommitted) {

@@ -150,11 +156,11 @@ await session.abortTransaction()

throw e
} finally {
}
finally {
await session.endSession()
}
}
/**
* @param {object} options
* @returns {object}
* @protected
* @param {object} options
* @returns {object}
*/

@@ -167,6 +173,5 @@ _passSessionIfTransactionEnabled(options) {

}
/**
* @returns {object}
* @protected
* @returns {object}
*/

@@ -180,6 +185,5 @@ _createMetadata() {

}
/**
* @returns {object}
* @protected
* @returns {object}
*/

@@ -194,10 +198,44 @@ _createUpdateMetadata() {

/**
* @callback AsyncFunction
* @returns {Promise.<any>}
*/
/**
* @param {('create'|'update'|'delete'|'close')} eventName
* @param {AsyncFunction} callback
* @returns {Function} a function to unsubscribe the listener
*/
on(eventName, callback) {
return this._emitter.on(eventName, callback)
}
/**
* @param {('create'|'update'|'delete'|'close')} eventName
* @param {AsyncFunction} callback
* @returns {Function} a function to unsubscribe the listener
*/
off(eventName, callback) {
return this._emitter.off(eventName, callback)
}
enableHistory() {
this._history = new History({ client: this._mongoDbClient, databaseName: this._databaseName, collectionName: this._collectionName, storage: this })
}
disableHistory() {
this._history.disable()
this._history = null
}
/**
* Closes the database client
*
* @method close
*
* @function close
* @returns {void}
*/
async close() {
await this._emitter.emit('close')
this._emitter.clearListeners()
return this._mongoDbClient.close()
}
}
})

@@ -1,31 +0,47 @@

export = exports;
declare class exports {
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @description Options for class constructor
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {import('./base-storage.js')} storage the target storage object.
* @example
* import { OneToFewResourceStorage } from '@discue/mongodb-resource-client'
*
* const collectionName = 'api_clients'
* const url = 'mongodb://127.0.0.1:27017'
*
* const storage = new OneToFewResourceStorage({
* url,
* collectionName
* })
*
* storage.enableHistory()
*/
/**
* Simple resource class that will listen to storage events of the given storage object
*
* @name ResourceStorageHistory
* @class
*/
export default class _default {
/**
* @public
* @param {ConstructorOptions} options
*/
constructor({ client, databaseName, collectionName, connectTimeout, usageEventPrefix, eventEmitter }?: ConstructorOptions);
_eventEmitter: any;
_collectionName: any;
_usageEventPrefix: any;
_resourceStorage: ResourceStorage;
/**
* Activate listening for storage events to monitor changes of target storage resource to
* populate the history table.
*
* @public
*/
public listenForStorageEvents(): void;
constructor({ client, databaseName, collectionName, connectTimeout, storage }?: ConstructorOptions);
/** @private */ private _collectionName;
/** @private */ private _parentStorage;
/** @private */ private _storage;
/** @private */ private _eventHandlers;
disable(): void;
/**
* @param {string} action
* @param {object} event
* @param {object} event.after
* @param {string | Array.<string>} event.resourceIds
* @param {string} event.collectionName
* @private
* @param {import('node:events').EventEmitter} eventEmitter
*/
private _registerEventHandlers;
/**
* @private
* @param {Object} event
* @param {boolean} event.error
* @param {Object} event.after
* @param {Array.<String>} event.resourceIds
*/
private _eventHandler;

@@ -35,3 +51,3 @@ /**

*
* @method close
* @function close
* @returns {void}

@@ -41,6 +57,2 @@ */

}
declare namespace exports {
export { ConstructorOptions };
}
import ResourceStorage = require("./simple-resource-storage.js");
type ConstructorOptions = any;
export type ConstructorOptions = any;

@@ -1,18 +0,7 @@

'use strict'
import ResourceStorage from './simple-resource-storage.js'
import { withActiveSpan } from './tracer.js'
const { createTracer } = require('@discue/open-telemetry-tracing')
const ResourceStorage = require('./simple-resource-storage.js')
const { name } = require('../package.json')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @description Options for class constructor

@@ -22,30 +11,19 @@ * @property {MongoClient} client configured mongo client to use. Can be null if url is set

* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {string} usageEventPrefix the usageEventPrefix of the target storage module
* @property {import('node:events').EventEmitter} eventEmitter if provided, will trigger events base on resource creation, updates and deletion
*
* @property {import('./base-storage.js')} storage the target storage object.
* @example
* const { EventEmitter } = require('events')
* const { OneToFewResourceStorage, ResourceStorageHistory } = require('@discue/mongodb-resource-client')
* import { OneToFewResourceStorage } from '@discue/mongodb-resource-client'
*
* const eventEmitter = new EventEmitter()
* const collectionName = 'api_clients'
* const url = 'mongodb://127.0.0.1:27017'
*
* const oneToFewResourceStorage = new OneToFewResourceStorage({
* const storage = new OneToFewResourceStorage({
* url,
* collectionName,
* eventEmitter
* collectionName
* })
*
* const history = new ResourceStorageHistory({
* url,
* collectionName,
* usageEventPrefix: oneToFewResourceStorage.usageEventPrefix
* eventEmitter
* })
* history.listenForStorageEvents()
* storage.enableHistory()
*/
/**
* Simple resource class that will listen to storage event of the given **eventEmitter** to populate a history collection / table.
* Simple resource class that will listen to storage events of the given storage object
*

@@ -55,80 +33,69 @@ * @name ResourceStorageHistory

*/
module.exports = class {
export default class {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/
constructor({ client, databaseName, collectionName, connectTimeout = 10_000, usageEventPrefix, eventEmitter } = {}) {
this._eventEmitter = eventEmitter
this._collectionName = collectionName
this._usageEventPrefix = usageEventPrefix
this._resourceStorage = new ResourceStorage({ client, databaseName, collectionName: this._collectionName, connectTimeout })
constructor({ client, databaseName, collectionName, connectTimeout = 10_000, storage } = {}) {
/** @private */ this._collectionName = `${collectionName}_history`
/** @private */ this._parentStorage = storage
/** @private */ this._storage = new ResourceStorage({ client, databaseName, collectionName: this._collectionName, connectTimeout })
/** @private */ this._eventHandlers = {
create: (event) => this._eventHandler('create', event),
update: (event) => this._eventHandler('update', event),
delete: (event) => this._eventHandler('delete', event)
}
this._parentStorage.on('create', this._eventHandlers['create'])
this._parentStorage.on('update', this._eventHandlers['update'])
this._parentStorage.on('delete', this._eventHandlers['delete'])
}
/**
* Activate listening for storage events to monitor changes of target storage resource to
* populate the history table.
*
* @public
*/
listenForStorageEvents() {
this._registerEventHandlers(this._eventEmitter)
disable() {
this._parentStorage.off('create', this._eventHandlers['create'])
this._parentStorage.off('update', this._eventHandlers['update'])
this._parentStorage.off('delete', this._eventHandlers['delete'])
}
/**
* @param {string} action
* @param {object} event
* @param {object} event.after
* @param {string | Array.<string>} event.resourceIds
* @param {string} event.collectionName
* @private
* @param {import('node:events').EventEmitter} eventEmitter
*/
_registerEventHandlers(eventEmitter) {
eventEmitter.on(`${this._usageEventPrefix}.create`, (event) => this._eventHandler('create', event))
eventEmitter.on(`${this._usageEventPrefix}.update`, (event) => this._eventHandler('update', event))
eventEmitter.on(`${this._usageEventPrefix}.delete`, (event) => this._eventHandler('delete', event))
eventEmitter.on(`${this._usageEventPrefix}.close`, () => this.close())
}
/**
* @private
* @param {Object} event
* @param {boolean} event.error
* @param {Object} event.after
* @param {Array.<String>} event.resourceIds
*/
async _eventHandler(action, { error, after, resourceIds, collectionName }) {
if (!error) {
return withActiveSpan(`${name}#handle-history-relevant-event`, { action, resourceIds }, async () => {
if (action === 'create' && collectionName != this._collectionName) {
await this._resourceStorage.create(resourceIds, {
history: [{
async _eventHandler(action, { after, resourceIds, collectionName }) {
return withActiveSpan(`handle-history-relevant-event`, { action, resourceIds }, async () => {
if (action === 'create' && collectionName != this._collectionName) {
await this._storage.create(resourceIds, {
history: [{
timestamp: Date.now(),
action,
resource: after
}]
})
}
else {
await this._storage.update(resourceIds, {
$push: {
history: {
timestamp: Date.now(),
action,
resource: after
}]
})
} else {
await this._resourceStorage.update(resourceIds, {
$push: {
history: {
timestamp: Date.now(),
action,
resource: after
}
}
})
}
})
}
}
})
}
})
}
/**
* Closes the database client
*
* @method close
*
* @function close
* @returns {void}
*/
close() {
return this._resourceStorage.close()
return this._storage.close()
}
}
}

@@ -1,7 +0,8 @@

export const SimpleResourceStorage: typeof import("./simple-resource-storage.js");
export const SimpleTimeseriesStorage: typeof import("./simple-timeseries-storage.js");
export const OneToFewRefStorage: typeof import("./one-to-few-ref-storage.js");
export const OneToFewResourceStorage: typeof import("./one-to-few-resource-storage.js");
export const OneToManyResourceStorage: typeof import("./one-to-many-resource-storage.js");
export const ResourceStorageHistory: typeof import("./history.js");
export const ResourceLock: typeof import("./locks.js");
import oneToFewRefStorage from './one-to-few-ref-storage.js';
import oneToFewResourceStorage from './one-to-few-resource-storage.js';
import oneToManyResourceStorage from './one-to-many-resource-storage.js';
import locks from './locks.js';
import history from './history.js';
import simpleResourceStorage from './simple-resource-storage.js';
import simpleTimeseriesStorage from './simple-timeseries-storage.js';
export { oneToFewRefStorage as OneToFewRefStorage, oneToFewResourceStorage as OneToFewResourceStorage, oneToManyResourceStorage as OneToManyResourceStorage, locks as ResourceLock, history as ResourceStorageHistory, simpleResourceStorage as SimpleResourceStorage, simpleTimeseriesStorage as SimpleTimeseriesStorage };

@@ -1,9 +0,10 @@

'use strict'
import history from './history.js'
import locks from './locks.js'
import oneToFewRefStorage from './one-to-few-ref-storage.js'
import oneToFewResourceStorage from './one-to-few-resource-storage.js'
import oneToManyResourceStorage from './one-to-many-resource-storage.js'
import simpleResourceStorage from './simple-resource-storage.js'
import simpleTimeseriesStorage from './simple-timeseries-storage.js'
module.exports.SimpleResourceStorage = require('./simple-resource-storage.js')
module.exports.SimpleTimeseriesStorage = require('./simple-timeseries-storage.js')
module.exports.OneToFewRefStorage = require('./one-to-few-ref-storage.js')
module.exports.OneToFewResourceStorage = require('./one-to-few-resource-storage.js')
module.exports.OneToManyResourceStorage = require('./one-to-many-resource-storage.js')
module.exports.ResourceStorageHistory = require('./history.js')
module.exports.ResourceLock = require('./locks.js')
export { oneToFewRefStorage as OneToFewRefStorage, oneToFewResourceStorage as OneToFewResourceStorage, oneToManyResourceStorage as OneToManyResourceStorage, locks as ResourceLock, history as ResourceStorageHistory, simpleResourceStorage as SimpleResourceStorage, simpleTimeseriesStorage as SimpleTimeseriesStorage }

@@ -1,6 +0,33 @@

export = exports;
declare class exports {
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {import('mongodb').MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {number} [connectTimeout=10_000] the connect timeout of the mongo db client if client was not passed
* @example
* import { MongoClient } from 'mongodb'
* import { ResourceLock } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const lock = new ResourceLock({
* client
* })
*
* await lock.doWhileLocked([123], () => {
* // do important stuff while lock is being held for 5s by default
* })
*/
/**
* Creates lock documents and allows to execute functions with a lock.
*
* @name ResourceLock
* @class
*/
export default class _default {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -13,2 +40,3 @@ constructor({ client, databaseName, connectTimeout }?: ConstructorOptions);

*
* @name lock
* @param {Array.<string>} resourceIds the resource ids

@@ -22,2 +50,3 @@ */

*
* @name unlock
* @param {Array.<string>} resourceIds the resource ids

@@ -31,8 +60,9 @@ */

*
* @name doWhileLocked
* @param {Array.<string>} resourceIds the resource ids
* @param {Function} callback callback to execute with lock
* @param {Object} options
* @param {Number} [options.lockTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.retryInterval=125] max time to wait in between retries
* @param {object} options
* @param {number} [options.lockTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document

@@ -50,10 +80,10 @@ * @throws {Error} Lock interrupted by timeout - if callback did not return before lockTimeout

*
* @private
* @param {Array.<string>} ids the resource ids
* @param {any} span
* @param {Function} callback callback to execute with lock
* @param {Object} options
* @param {Number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.retryInterval=125] max time to wait in between retries
* @param {object} options
* @param {number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document
* @private
*/

@@ -65,9 +95,9 @@ private _doWhileLocked;

*
* @private
* @param {Array.<string>} ids the resource ids
* @param {any} span
* @param {Object} options
* @param {Number} [options.waitTimeout] max time to wait in milliseconds
* @param {Number} [options.retryInterval] max time to wait in between retries
* @param {object} options
* @param {number} [options.waitTimeout] max time to wait in milliseconds
* @param {number} [options.retryInterval] max time to wait in between retries
* @returns {Promise.<boolean>}
* @private
*/

@@ -78,3 +108,3 @@ private _ensureIsLocked;

*
* @method close
* @function close
* @returns {void}

@@ -84,19 +114,3 @@ */

}
declare namespace exports {
export { ConstructorOptions };
}
import ResourceStorage = require("./simple-resource-storage.js");
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* the connect timeout of the mongo db client if client was not passed
*/
connectTimeout?: number;
};
export type ConstructorOptions = any;
import ResourceStorage from './simple-resource-storage.js';

@@ -1,28 +0,22 @@

'use strict'
import SpanStatusCode from '@discue/open-telemetry-tracing/status-codes'
import ResourceStorage from './simple-resource-storage.js'
import { withActiveSpan } from './tracer.js'
const { createTracer } = require('@discue/open-telemetry-tracing')
const SpanStatusCode = require('@discue/open-telemetry-tracing/status-codes')
const ResourceStorage = require('./simple-resource-storage.js')
const { name } = require('../package.json')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {import('mongodb').MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {number} [connectTimeout=10_000] the connect timeout of the mongo db client if client was not passed
*
* @example
* const { ResourceLock } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { ResourceLock } from '@discue/mongodb-resource-client'
*
* const url = 'mongodb://127.0.0.1:27017'
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const lock = new ResourceLock({
* url
* client
* })

@@ -41,10 +35,9 @@ *

*/
module.exports = class {
export default class {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/
constructor({ client, databaseName, connectTimeout = 10_000 } = {}) {
const collectionName = '_locks'

@@ -60,11 +53,11 @@ this._resourceStorage = new ResourceStorage({ client, databaseName, collectionName, connectTimeout })

}
/**
* Creates an entry in the `locks` collection. The context param is a unique identifier. If context already
* exists, the method will throw.
*
*
* @name lock
* @param {Array.<string>} resourceIds the resource ids
*/
async lock(resourceIds) {
return withActiveSpan(`${name}#lock-resource`, { resourceIds }, async () => {
return withActiveSpan(`lock-resource`, { resourceIds }, async () => {
return this._resourceStorage.create(resourceIds, {

@@ -75,39 +68,37 @@ locked_at: Date.now()

}
/**
* Deletes an entry from the `locks` collection unlocking the document.
* Deletes an entry from the `locks` collection unlocking the document.
* The context param is a unique identifier. If context has already been
* removed, the method will throw.
*
*
* @name unlock
* @param {Array.<string>} resourceIds the resource ids
*/
async unlock(resourceIds) {
return withActiveSpan(`${name}#unlock-resource`, { resourceIds }, async () => {
return withActiveSpan(`unlock-resource`, { resourceIds }, async () => {
return this._resourceStorage._deleteUnsafe(resourceIds)
})
}
/**
* Executes the callback only if the appropriate lock document has been created successfully.
* Unlocks the document either after completion of the callback or after `lockTimeout` millis
* have passed.
*
* have passed.
*
* @name doWhileLocked
* @param {Array.<string>} resourceIds the resource ids
* @param {Function} callback callback to execute with lock
* @param {Object} options
* @param {Number} [options.lockTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document
* @param {object} options
* @param {number} [options.lockTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document
* @throws {Error} Lock interrupted by timeout - if callback did not return before lockTimeout
*/
async doWhileLocked(resourceIds, callback, { lockTimeout = 5_000, waitTimeout = 5_000, retryInterval = 125 } = {}) {
return withActiveSpan(`${name}#do-while-resource-locked`, { resourceIds, lockTimeout, waitTimeout, retryInterval }, async (span) => {
return withActiveSpan(`do-while-resource-locked`, { resourceIds, lockTimeout, waitTimeout, retryInterval }, async (span) => {
return new Promise((resolve, reject) => {
let timeout
// override the callback here. The lock timeout should only start
// after the lock was established
this._doWhileLocked(resourceIds, span, async () => {
timeout = setTimeout(() => {

@@ -117,10 +108,6 @@ this.unlock(resourceIds)

span.addEvent('Timeout waiting for lock')
reject(new Error('Lock interrupted by timeout'))
})
}, lockTimeout)
return callback()
}, { lockTimeout, waitTimeout, retryInterval })

@@ -139,16 +126,14 @@ .then((result) => {

}
/**
* Executes the callback only if the appropriate lock document has been created successfully.
* Unlocks the document either after completion.
*
* @private
*
* @param {Array.<string>} ids the resource ids
* @param {any} span
* @param {Function} callback callback to execute with lock
* @param {Object} options
* @param {Number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {Number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document
* @param {object} options
* @param {number} [options.waitTimeout=5_000] max time to wait in milliseconds
* @param {number} [options.retryInterval=125] max time to wait in between retries
* @throws {Error} Unable to establish lock - if unable to establish lock for a document
* @private
*/

@@ -163,23 +148,21 @@ async _doWhileLocked(ids, span, callback, { waitTimeout = 5_000, retryInterval = 125 } = {}) {

return result
} finally {
}
finally {
await this.unlock(ids)
}
}
/**
* Creates the lock for the given id waiting `waitTimeout` milliseconds and retrying
* Creates the lock for the given id waiting `waitTimeout` milliseconds and retrying
* every `retryInterval` milliseconds.
*
* @private
*
* @param {Array.<string>} ids the resource ids
* @param {any} span
* @param {Object} options
* @param {Number} [options.waitTimeout] max time to wait in milliseconds
* @param {Number} [options.retryInterval] max time to wait in between retries
* @returns {Promise.<boolean>}
* @param {object} options
* @param {number} [options.waitTimeout] max time to wait in milliseconds
* @param {number} [options.retryInterval] max time to wait in between retries
* @returns {Promise.<boolean>}
* @private
*/
async _ensureIsLocked(ids, span, { waitTimeout, retryInterval }) {
const start = Date.now()
while (Date.now() - start <= waitTimeout) {

@@ -189,3 +172,4 @@ try {

return true
} catch (e) {
}
catch (e) {
span.addEvent('Document still locked', {

@@ -201,10 +185,8 @@ waitedFor: Date.now() - start,

}
return false
}
/**
* Closes the database client.
*
* @method close
*
* @function close
* @returns {void}

@@ -215,2 +197,2 @@ */

}
}
}
export function getSingleLookupPipeline({ rootId, childCollectionName, pipeline }: {
rootId: string;
childCollectionName: string;
pipeline: any[];
}): any[];
rootId: any;
childCollectionName: any;
pipeline?: any[];
}): (import("./aggregations.js").Match | import("./aggregations.js").Projection | import("./aggregations.js").Lookup)[];
export function joinAndQueryChildResourcesPipeline({ parentCollectionName, childCollectionName, options }: {
parentCollectionName: string;
childCollectionName: string;
options: GetOptions;
}): any[];
export type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
/**
* MongoDB match object e.g. { id: 0, name: 0 }
*/
match: any;
};
parentCollectionName: any;
childCollectionName: any;
options: any;
}): (import("./aggregations.js").Projection | import("./aggregations.js").Lookup)[];

@@ -1,14 +0,4 @@

'use strict'
import { APPEND_OBJECTS, CONCAT_ARRAYS, EQUALS, EQUALS_ALL, JOIN_STRINGS, LET, LOOKUP, PROJECT, REDUCE, TO_OBJECT } from './aggregations.js'
const { PROJECT, EQUALS, LOOKUP, REDUCE, CONCAT_ARRAYS, TO_OBJECT, LET, APPEND_OBJECTS, JOIN_STRINGS, EQUALS_ALL } = require('./aggregations.js')
/**
*
* @param {object} options
* @param {string} options.rootId the id of the root object
* @param {string} options.childCollectionName the collectionName of the child resource
* @param {Array} options.pipeline an array of other pipelines that should be executed after the lookup
* @returns {Array}
*/
module.exports.getSingleLookupPipeline = function ({ rootId, childCollectionName, pipeline = [] }) {
export const getSingleLookupPipeline = function ({ rootId, childCollectionName, pipeline = [] }) {
return [

@@ -26,20 +16,3 @@ EQUALS('id', rootId),

/**
* @name GetOptions
* @private
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {Object} match MongoDB match object e.g. { id: 0, name: 0 }
*/
/**
*
* @param {object} options
* @param {string} options.parentCollectionName name of the parent collection
* @param {string} options.childCollectionName the collectionName of the child resource
* @param {GetOptions} options.options
* @returns {Array}
*/
module.exports.joinAndQueryChildResourcesPipeline = function ({ parentCollectionName, childCollectionName, options }) {
export const joinAndQueryChildResourcesPipeline = function ({ parentCollectionName, childCollectionName, options }) {
const lookupPipeline = [

@@ -51,11 +24,8 @@ PROJECT({

]
if (options.match) {
lookupPipeline.push(EQUALS_ALL(options.match))
}
if (options.projection) {
lookupPipeline.push(PROJECT(options.projection))
}
const pipeline = [

@@ -65,8 +35,7 @@ PROJECT({

parent: `$${parentCollectionName}`,
[childCollectionName]:
REDUCE({
input: `$${parentCollectionName}.${childCollectionName}`,
initialValue: [],
inExpression: CONCAT_ARRAYS()
})
[childCollectionName]: REDUCE({
input: `$${parentCollectionName}.${childCollectionName}`,
initialValue: [],
inExpression: CONCAT_ARRAYS()
})
}),

@@ -83,27 +52,18 @@ LOOKUP({

input: `$parent`,
inExpression: APPEND_OBJECTS(
LET({
vars: {
parent: '$$this.id',
children: `$$this.${childCollectionName}`
},
inExpression: REDUCE({
input: '$$children',
inExpression:
APPEND_OBJECTS(
TO_OBJECT({
value: JOIN_STRINGS('/',
`${parentCollectionName}`,
'$$parent',
`${childCollectionName}`,
'$$this')
}))
})
inExpression: APPEND_OBJECTS(LET({
vars: {
parent: '$$this.id',
children: `$$this.${childCollectionName}`
},
inExpression: REDUCE({
input: '$$children',
inExpression: APPEND_OBJECTS(TO_OBJECT({
value: JOIN_STRINGS('/', `${parentCollectionName}`, '$$parent', `${childCollectionName}`, '$$this')
}))
})
)
}))
})
})
]
return pipeline
}
}

@@ -1,6 +0,52 @@

export = exports;
declare class exports extends Base {
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {string} resourceName name of the resource e.g. users, customers, topics, shipments
* @example
* import { MongoClient } from 'mongodb'
* import { OneToFewRefstorage } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const oneToFewRefStorage = new OneToFewRefStorage({
* client,
* collectionName: 'api_clients',
* resourceName: 'queues'
* })
*/
/**
* @typedef {import('mongodb').ObjectId} ObjectId
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/
/**
* Similar to @link OneToFewResourceStorage, but allows only managing of
* references. Meaning: Instead of embedding objects in the target array
* this class only stores references to other objects.
*
* <strong>Universities collection</strong>
* ```js
* {
* name: 'University Munich',
* students: [1828391, 9440201, 29930302]
* }
* {
* name: 'University Stuttgart',
* students: [551234, 115235, 4451515]
* }
* ```
*
* @name OneToFewRefStorage
* @class
* @link https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
*/
export default class _default extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -12,59 +58,45 @@ constructor(options: ConstructorOptions);

*
* @method getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {Array.<Object>}
* @function getAll
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {Array.<object>}
*/
getAll(resourceIds: string | Array<string>): Array<any>;
getAll(resourceIds: string | Array<string>): Array<object>;
/**
* @typedef WithMongoClient
* @name WithMongoClient
* @property {import('mongodb').MongoClient} client
* @private
*/
/**
* Add a reference to a collection by ids.
*
* @method create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} ref the resource to be stored
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} ref the resource to be stored
* @param {import('mongodb').UpdateOptions | WithMongoClient} [options=null]
* @returns {Promise.<ObjectId>}
*/
create(resourceIds: string | Array<string>, ref: any, options?: import("mongodb").UpdateOptions | WithMongoClient): Promise<ObjectId>;
create(resourceIds: string | Array<string>, ref: object, options?: import("mongodb").UpdateOptions | any): Promise<ObjectId>;
/**
* Delete a reference
*
* @method find
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<Objecft>} references
* @returns {Promise.<void>}
*/
findReferences(resourceIds: string | Array<string>, references: string | Array<Objecft>): Promise<void>;
* Delete a reference
*
* @function find
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<object>} references
* @returns {Promise.<void>}
*/
findReferences(resourceIds: string | Array<string>, references: string | Array<object>): Promise<void>;
/**
* Delete a reference
*
* @method delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @function delete
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {import('mongodb').FindOneAndUpdateOptions | WithMongoClient} [options=null]
* @returns {Promise.<void>}
*/
delete(resourceIds: string | Array<string>, options?: import("mongodb").FindOneAndUpdateOptions | WithMongoClient): Promise<void>;
delete(resourceIds: string | Array<string>, options?: import("mongodb").FindOneAndUpdateOptions | any): Promise<void>;
}
declare namespace exports {
export { ConstructorOptions, ObjectId, MongoClient };
}
import Base = require("./simple-resource-storage.js");
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
/**
* name of the resource e.g. users, customers, topics, shipments
*/
resourceName: string;
};
type ObjectId = import("mongodb").ObjectId;
type MongoClient = import("mongodb").MongoClient;
export type ConstructorOptions = any;
export type ObjectId = import("mongodb").ObjectId;
export type MongoClient = import("mongodb").MongoClient;
import Base from './simple-resource-storage.js';

@@ -1,19 +0,8 @@

'use strict'
import { EQUALS, EQUALS_ANY_OF } from './aggregations.js'
import { toArrayAndClose } from './safe-cursor.js'
import Base from './simple-resource-storage.js'
const { createTracer } = require('@discue/open-telemetry-tracing')
const { EQUALS_ANY_OF, EQUALS } = require('./aggregations.js')
const Base = require('./simple-resource-storage.js')
const { name } = require('../package.json')
const { toArrayAndClose } = require('./safe-cursor.js')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set

@@ -23,6 +12,5 @@ * @property {string} [databaseName=null] name of the mongodb database

* @property {string} resourceName name of the resource e.g. users, customers, topics, shipments
*
* @example
* const { MongoClient } = require('mongodb')
* const { OneToFewRefstorage } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { OneToFewRefstorage } from '@discue/mongodb-resource-client'
*

@@ -41,5 +29,5 @@ * const client = new MongoClient(url, {

/**
* @private
* @typedef {import('mongodb').ObjectId} ObjectId
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/

@@ -64,11 +52,11 @@

*
* @link https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
* @name OneToFewRefStorage
* @class
* @link https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
*/
module.exports = class extends Base {
export default class extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -79,12 +67,11 @@ constructor(options) {

}
/**
* Returns true if a resource with given ids exists.
*
* @method exists
* @param {String|Array.<String>} resourceIds
*
* @function exists
* @param {string | Array.<string>} resourceIds
* @returns {boolean}
*/
async exists(resourceIds) {
return this._withActiveSpan('exists-one-to-few-ref-by-id', resourceIds, async () => {
return this._runWithActiveSpan('exists-one-to-few-ref-by-id', resourceIds, async () => {
const collection = await this._getCollection()

@@ -99,3 +86,2 @@ const parent = await collection.findOne({

})
return parent != null

@@ -106,17 +92,10 @@ })

/**
* @private
*/
async _withActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}
/**
* Returns all references.
*
* @method getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {Array.<Object>}
*
* @function getAll
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {Array.<object>}
*/
async getAll(resourceIds) {
return this._withActiveSpan('get-all-one-to-few-refs-by-id', resourceIds, async () => {
return this._runWithActiveSpan('get-all-one-to-few-refs-by-id', resourceIds, async () => {
const collection = await this._getCollection()

@@ -126,7 +105,5 @@ const parent = await collection.findOne({

})
if (!parent) {
return []
}
return parent[`${this._resourceName}`]

@@ -137,7 +114,14 @@ })

/**
* @typedef WithMongoClient
* @name WithMongoClient
* @property {import('mongodb').MongoClient} client
* @private
*/
/**
* Add a reference to a collection by ids.
*
* @method create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} ref the resource to be stored
*
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} ref the resource to be stored
* @param {import('mongodb').UpdateOptions | WithMongoClient} [options=null]

@@ -147,3 +131,3 @@ * @returns {Promise.<ObjectId>}

async create(resourceIds, ref, options) {
return this._withActiveSpan('create-one-to-few-ref-by-id', resourceIds, async () => {
return this._runWithActiveSpan('create-one-to-few-ref-by-id', resourceIds, async () => {
const collection = await this._getCollection(options?.client)

@@ -157,3 +141,2 @@ const result = await collection.updateOne({

}, this._passSessionIfTransactionEnabled(options))
const success = result.acknowledged === true && result.matchedCount === 1

@@ -163,19 +146,16 @@ if (!success) {

}
return ref
})
}
/**
* Delete a reference
*
* @method find
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<Objecft>} references
* @returns {Promise.<void>}
*/
* Delete a reference
*
* @function find
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<object>} references
* @returns {Promise.<void>}
*/
async findReferences(resourceIds, references) {
return this._withActiveSpan('find-one-to-few-refs-by-id', resourceIds, async () => {
return this._runWithActiveSpan('find-one-to-few-refs-by-id', resourceIds, async () => {
const collection = await this._getCollection()
const stages = [

@@ -185,3 +165,2 @@ EQUALS('id', resourceIds.at(0)),

]
const cursor = collection.aggregate(stages)

@@ -191,8 +170,7 @@ return toArrayAndClose(cursor)

}
/**
* Delete a reference
*
* @method delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
*
* @function delete
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {import('mongodb').FindOneAndUpdateOptions | WithMongoClient} [options=null]

@@ -202,3 +180,3 @@ * @returns {Promise.<void>}

async delete(resourceIds, options) {
return this._withActiveSpan('delete-one-to-few-ref-by-id', resourceIds, async () => {
return this._runWithActiveSpan('delete-one-to-few-ref-by-id', resourceIds, async () => {
const exists = await this.exists(resourceIds)

@@ -208,3 +186,2 @@ if (!exists) {

}
const collection = await this._getCollection(options?.client)

@@ -218,3 +195,2 @@ const result = await collection.findOneAndUpdate({

}, { includeResultMetadata: true, ...this._passSessionIfTransactionEnabled(options) })
const success = result.ok === 1

@@ -226,2 +202,2 @@ if (!success) {

}
}
}

@@ -1,6 +0,62 @@

export = exports;
declare class exports extends Base {
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {string} resourceName name of the resource e.g. users, customers, topics, shipments
* @example
* import { MongoClient } from 'mongodb'
* import { OneToFewResourceStorage } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const oneToFewResourceStorage = new OneToFewResourceStorage({
* client,
* collectionName: 'api_clients',
* resourceName: 'queues'
* })
*/
/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/
/**
* Allows to manage a list of documents in another document to e.g. store a list of
* students of each school. As the child documents will be embedded, it is easy
* to retrieve them (only one query), but harder to get all students across e.g.
* a country in various schools.
*
* <strong>Universities collection</strong>
* ```js
* {
* name: 'University Munich',
* students: [
* {
* name: 'Stef',
* city: 'Munich
* },
* {
* name: 'Frank',
* city: 'Stuttgart
* }
* ]
* }
* ```
*
* @name OneToFewResourceStorage
* @class
*/
export default class _default extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -13,7 +69,7 @@ constructor(options: ConstructorOptions);

* @name get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Promise.<Object>}
* @returns {Promise.<object>}
*/
get(resourceIds: string | Array<string>, { withMetadata, projection }?: GetOptions): Promise<any>;
get(resourceIds: string | Array<string>, { withMetadata, projection }?: GetOptions): Promise<object>;
/**

@@ -23,7 +79,7 @@ * Returns all resources.

* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Promise.<Array.<Object>>}
* @returns {Promise.<Array.<object>>}
*/
getAll(resourceIds: string | Array<string>, { withMetadata, projection }?: GetOptions): Promise<Array<any>>;
getAll(resourceIds: string | Array<string>, { withMetadata, projection }?: GetOptions): Promise<Array<object>>;
/**

@@ -33,7 +89,7 @@ * Add a resource to a collection by ids.

* @name create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @returns {ObjectId}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @returns {string} the id of the new object
*/
create(resourceIds: string | Array<string>, resource: any): ObjectId;
create(resourceIds: string | Array<string>, resource: object): string;
/**

@@ -43,15 +99,17 @@ * Updates a resource by ids

* @name update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @returns {void}
*/
update(resourceIds: string | Array<string>, update: any): void;
_createUpdateMetadata(): {
[x: string]: Timestamp;
};
update(resourceIds: string | Array<string>, update: object): void;
/**
*
* @private
*/
private _createUpdateMetadata;
/**
* Deletes a resource by ids
*
* @name delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {void}

@@ -61,39 +119,5 @@ */

}
declare namespace exports {
export { ConstructorOptions, GetOptions, MongoClient };
}
import Base = require("./simple-resource-storage.js");
import { Timestamp } from "mongodb";
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
/**
* name of the resource e.g. users, customers, topics, shipments
*/
resourceName: string;
/**
* if provided, will trigger events base on resource creation, updates and deletion
*/
eventEmitter: import("node:events").EventEmitter;
};
type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
};
type MongoClient = import("mongodb").MongoClient;
export type ConstructorOptions = any;
export type GetOptions = any;
export type MongoClient = import("mongodb").MongoClient;
import Base from './simple-resource-storage.js';

@@ -1,21 +0,11 @@

'use strict'
import * as mongodb from 'mongodb'
import { EQUALS, LIMIT, PROJECT } from './aggregations.js'
import { getFirstAndClose, toArrayAndClose } from './safe-cursor.js'
import Base from './simple-resource-storage.js'
const { PROJECT, EQUALS, LIMIT } = require('./aggregations.js')
const eventTrigger = require('./usage-event-trigger.js')
const Base = require('./simple-resource-storage.js')
const { Timestamp } = require('mongodb')
const { createTracer } = require('@discue/open-telemetry-tracing')
const { name } = require('../package.json')
const { getFirstAndClose, toArrayAndClose } = require('./safe-cursor.js')
const { Timestamp } = mongodb
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set

@@ -25,7 +15,5 @@ * @property {string} [databaseName=null] name of the mongodb database

* @property {string} resourceName name of the resource e.g. users, customers, topics, shipments
* @property {import('node:events').EventEmitter} eventEmitter if provided, will trigger events base on resource creation, updates and deletion
*
* @example
* const { MongoClient } = require('mongodb')
* const { OneToFewResourceStorage } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { OneToFewResourceStorage } from '@discue/mongodb-resource-client'
*

@@ -44,11 +32,11 @@ * const client = new MongoClient(url, {

/**
* @typedef GetOptions
* @name GetOptions
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
* @typedef {import('mongodb').MongoClient} MongoClient
*/

@@ -82,29 +70,23 @@

*/
module.exports = class extends Base {
export default class extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/
constructor(options) {
super(options)
/** @private */ this._resourceName = options.resourceName
/** @private */ this._emitUsageEvent = eventTrigger(this.usageEventPrefix, options.resourceName, options.eventEmitter)
}
get usageEventPrefix() {
return `one-to-many-resource.${this._resourceName}`
}
/**
* Returns a resource by ids.
*
*
* @name get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Promise.<Object>}
* @returns {Promise.<object>}
*/
async get(resourceIds, { withMetadata = false, projection } = {}) {
return this._withActiveSpan('get-one-to-few-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('get-one-to-few-resource-by-id', resourceIds, async () => {
const collection = await this._getCollection()

@@ -118,11 +100,8 @@ const aggregationStages = [

]
if (!withMetadata) {
aggregationStages.push(PROJECT({ _meta_data: 0 }))
}
if (projection) {
aggregationStages.push(PROJECT(projection))
}
const cursor = collection.aggregate(aggregationStages)

@@ -134,18 +113,11 @@ return getFirstAndClose(cursor)

/**
* @private
*/
async _withActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}
/**
* Returns all resources.
*
*
* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Promise.<Array.<Object>>}
* @returns {Promise.<Array.<object>>}
*/
async getAll(resourceIds, { withMetadata = false, projection } = {}) {
return this._withActiveSpan('get-all-one-to-few-resources-by-id', resourceIds, async () => {
return this._runWithActiveSpan('get-all-one-to-few-resources-by-id', resourceIds, async () => {
const collection = await this._getCollection()

@@ -157,11 +129,8 @@ const aggregationStages = [

]
if (!withMetadata) {
aggregationStages.push(PROJECT({ _meta_data: 0 }))
}
if (projection) {
aggregationStages.push(PROJECT(projection))
}
const cursor = collection.aggregate(aggregationStages)

@@ -171,13 +140,12 @@ return toArrayAndClose(cursor)

}
/**
* Add a resource to a collection by ids.
*
*
* @name create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @returns {ObjectId}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @returns {string} the id of the new object
*/
async create(resourceIds, resource) {
return this._withActiveSpan('create-one-to-few-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('create-one-to-few-resource-by-id', resourceIds, async () => {
const exists = await this.exists(resourceIds)

@@ -187,6 +155,4 @@ if (exists) {

}
const update = Object.assign({}, resource)
update.id = resourceIds.at(-1)
const collection = await this._getCollection()

@@ -200,3 +166,2 @@ const result = await collection.updateOne({

})
const success = result.acknowledged === true && result.modifiedCount === 1

@@ -206,19 +171,16 @@ if (!success) {

}
this._emitUsageEvent({ context: 'create', after: resource, resourceIds })
this._emitter.emit('create', { after: resource, resourceIds })
return resourceIds.at(-1)
})
}
/**
* Updates a resource by ids
*
*
* @name update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @returns {void}
*/
async update(resourceIds, update) {
return this._withActiveSpan('update-one-to-few-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('update-one-to-few-resource-by-id', resourceIds, async () => {
const beforeResource = await this.get(resourceIds)

@@ -228,3 +190,2 @@ if (!beforeResource) {

}
const hasAtomicOperator = Object.keys(update).find(key => key.startsWith('$'))

@@ -236,3 +197,2 @@ if (!hasAtomicOperator) {

}
const collection = await this._getCollection()

@@ -249,3 +209,2 @@ const result = await collection.findOneAndUpdate({

})
const success = result.ok === 1

@@ -255,10 +214,11 @@ if (!success) {

}
const afterResource = await this.get(resourceIds)
await this._emitUsageEvent({ context: 'update', before: beforeResource, after: afterResource, resourceIds })
await this._emitter.emit('update', { before: beforeResource, after: afterResource, resourceIds })
return result
})
}
/**
*
* @private
*/
_createUpdateMetadata() {

@@ -270,12 +230,11 @@ const timestamp = Timestamp.fromNumber(Date.now())

}
/**
* Deletes a resource by ids
*
*
* @name delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns {void}
*/
async delete(resourceIds) {
return this._withActiveSpan('delete-one-to-few-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('delete-one-to-few-resource-by-id', resourceIds, async () => {
const resource = await this.get(resourceIds)

@@ -285,3 +244,2 @@ if (!resource) {

}
const collection = await this._getCollection()

@@ -297,3 +255,2 @@ const result = await collection.findOneAndUpdate({

}, { includeResultMetadata: true })
const success = result.ok === 1

@@ -303,6 +260,5 @@ if (!success) {

}
await this._emitUsageEvent({ context: 'delete', before: resource, resourceIds })
await this._emitter.emit('delete', { before: resource, resourceIds })
})
}
}
}

@@ -1,6 +0,80 @@

export = exports;
declare class exports extends Base {
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {string} resourceName name of the resource e.g. users, customers, topics, shipments
* @property {string} [resourcePath=resourceName] slash separated path describing the hierarchy e.g. universities/teachers/subjects/exams.
* @property {string} [hiddenResourcePath=null] slash separated path describing which path elements should not be returned to callers
* @property {string} enableTwoWayReferences true if documents should also store references to their parents e.g. student have references to their schools
* @example
* import { MongoClient } from 'mongodb'
* import { OneToManyResourceStorage } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const oneToManyResourceStorage = new OneToManyResourceStorage({
* client,
* collectionName: 'api_clients',
* resourceName: 'listeners'
* enableTwoWayReferences: true
* })
*/
/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {boolean} addDocumentPath true if $path propety should be added to documents e.g. `$path=/countries/1/cities/2/companies`
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/
/**
* Manages relationships between entities in a more decoupled way by keep storing
* entities in separate collections and using references to establish an relationship
* between both. This way students can be queried independently of an university,
* while all studies of a university can still be looked up via the stored reference.
*
* The references between both collections are kept up-to-date. Deleting a document,
* causes the reference to be deleted in the other entity. Adding a document
* causes a reference to be updated, too.
*
* <strong>Students collection</strong>
* ```js
* {
* id: 1828391,
* name: 'Miles Morales',
* },
* {
* id: 4451515,
* name: 'Bryan Jenkins',
* }
* ```
*
* <strong>Universities collection</strong>
* ```js
* {
* name: 'University Munich',
* students: [1828391]
* }
* {
* name: 'University Stuttgart',
* students: [4451515]
* }
* ```
*
* @name OneToManyResourceStorage
* @class
* @link as described here: https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
*/
export default class _default extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -16,3 +90,2 @@ constructor(options: ConstructorOptions);

/** @private */ private _resourceLevel;
/** @private */ private _childEmitUsageEvent;
/**

@@ -22,14 +95,14 @@ * Returns a resource by ids.

* @name get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} options
* @returns {Object}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} options
* @returns {object}
*/
get(resourceIds: string | Array<string>, options: any): any;
get(resourceIds: string | Array<string>, options: object): object;
/**
* @typedef FindOptions
* @name FindOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} match MongoDB match object e.g. { id: 123 }
* @private
* @typedef FindOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {Object} match MongoDB match object e.g. { id: 123 }
*/

@@ -40,43 +113,39 @@ /**

* @name find
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {FindOptions} options
* @returns {Object}
* @returns {object}
*/
find(resourceIds: string | Array<string>, options: {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
/**
* MongoDB match object e.g. { id: 123 }
*/
match: any;
}): any;
find(resourceIds: string | Array<string>, options: any): object;
/**
* Returns a resource without safety checks and tracing
*
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
* @private
*/
_getResourceWithLookup(resourceIds: string | Array<string>, options?: GetOptions): any;
_getParentCollection(): Promise<import("mongodb").Collection<import("bson").Document>>;
private _getResourceWithLookup;
/**
*
* @private
*/
private _getParentCollection;
/**
*
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
* @private
*/
_getNestedLookupStages(resourceIds: string | Array<string>, options: GetOptions): any;
_getResourceIdsForHostStorage(resourceIds: any): any;
private _getNestedLookupStages;
/**
* Returns resources based on return value of {@link findReferences}.
*
* @param {Array.<string>} resourceIds
* @private
*/
private _getResourceIdsForHostStorage;
/**
*
* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options

@@ -89,7 +158,7 @@ */

* @name create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @returns
*/
create(resourceIds: string | Array<string>, resource: any): Promise<any>;
create(resourceIds: string | Array<string>, resource: object): Promise<any>;
/**

@@ -99,7 +168,7 @@ * Updates a resource by ids

* @name update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @returns
*/
update(resourceIds: string | Array<string>, update: any): Promise<any>;
update(resourceIds: string | Array<string>, update: object): Promise<any>;
/**

@@ -112,58 +181,5 @@ * Closes all clients.

}
declare namespace exports {
export { ConstructorOptions, GetOptions, MongoClient };
}
import Base = require("./simple-resource-storage.js");
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes
*/
indexes?: Array<any>;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
/**
* name of the resource e.g. users, customers, topics, shipments
*/
resourceName: string;
/**
* slash separated path describing the hierarchy e.g. universities/teachers/subjects/exams.
*/
resourcePath?: string;
/**
* slash separated path describing which path elements should not be returned to callers
*/
hiddenResourcePath?: string;
/**
* true if documents should also store references to their parents e.g. student have references to their schools
*/
enableTwoWayReferences: string;
/**
* if provided, will trigger events base on resource creation, updates and deletion
*/
eventEmitter: import("node:events").EventEmitter;
};
type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* true if $path propety should be added to documents e.g. `$path=/countries/1/cities/2/companies`
*/
addDocumentPath: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
};
type MongoClient = import("mongodb").MongoClient;
export type ConstructorOptions = any;
export type GetOptions = any;
export type MongoClient = import("mongodb").MongoClient;
import Base from './simple-resource-storage.js';

@@ -1,27 +0,13 @@

'use strict'
import SpanStatusCode from '@discue/open-telemetry-tracing/status-codes'
import { AS_ROOT, EQUALS, EQUALS_ALL, LOOKUP, PROJECT, UNWIND } from './aggregations.js'
import OneToFewStorage from './one-to-few-ref-storage.js'
import { toArrayAndClose } from './safe-cursor.js'
import Base from './simple-resource-storage.js'
const Base = require('./simple-resource-storage.js')
const OneToFewStorage = require('./one-to-few-ref-storage.js')
const { PROJECT, EQUALS, LOOKUP, UNWIND, AS_ROOT, EQUALS_ALL } = require('./aggregations.js')
const usageEventTrigger = require('./usage-event-trigger.js')
const { createTracer } = require('@discue/open-telemetry-tracing')
const { name } = require('../package.json')
const SpanStatusCode = require('@discue/open-telemetry-tracing/status-codes')
const { toArrayAndClose } = require('./safe-cursor.js')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename,
name: 'mongodb-resource-client'
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {Array.<Object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes
* @property {string} collectionName name of the mongodb collection used to store the resources

@@ -32,7 +18,5 @@ * @property {string} resourceName name of the resource e.g. users, customers, topics, shipments

* @property {string} enableTwoWayReferences true if documents should also store references to their parents e.g. student have references to their schools
* @property {import('node:events').EventEmitter} eventEmitter if provided, will trigger events base on resource creation, updates and deletion
*
* @example
* const { MongoClient } = require('mongodb')
* const { OneToManyResourceStorage } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { OneToManyResourceStorage } from '@discue/mongodb-resource-client'
*

@@ -52,12 +36,12 @@ * const client = new MongoClient(url, {

/**
* @typedef GetOptions
* @name GetOptions
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {boolean} addDocumentPath true if $path propety should be added to documents e.g. `$path=/countries/1/cities/2/companies`
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
* @typedef {import('mongodb').MongoClient} MongoClient
*/

@@ -99,11 +83,11 @@

*
* @link as described here: https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
* @name OneToManyResourceStorage
* @class
* @link as described here: https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design
*/
module.exports = class extends Base {
export default class extends Base {
/**
* @param {ConstructorOptions} options
* @public
* @param {ConstructorOptions} options
*/

@@ -113,3 +97,2 @@ constructor(options) {

baseOptions.collectionName = options.resourceName
baseOptions.eventEmitter = null
super(baseOptions)

@@ -120,3 +103,2 @@

/** @private */ this._hostStorage = new OneToFewStorage(hostOptions)
/** @private */ this._enableTwoWayReferences = options.enableTwoWayReferences

@@ -129,20 +111,13 @@ /** @private */ this._resourceName = options.resourceName

/** @private */ this._resourceLevel = this._resourcePath.length
/** @private */ this._emitUsageEventEnabled = options.eventEmitter != null
/** @private */ this._childEmitUsageEvent = usageEventTrigger(this.usageEventPrefix, options.resourceName, options.eventEmitter)
}
get usageEventPrefix() {
return `one-to-many-resource.${this._resourceName}`
}
/**
* Returns true if a resource with given ids exists.
*
*
* @name exists
* @param {String|Array.<String>} resourceIds
* @param {string | Array.<string>} resourceIds
* @returns {boolean}
*/
async exists(resourceIds) {
return this._withActiveSpan('exists-one-to-many-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('exists-one-to-many-resource-by-id', resourceIds, async () => {
const resource = await this.get(resourceIds)

@@ -154,18 +129,11 @@ return resource != null

/**
* @private
*/
async _withActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}
/**
* Returns a resource by ids.
*
*
* @name get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} options
* @returns {Object}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} options
* @returns {object}
*/
async get(resourceIds, options) {
return this._withActiveSpan('get-one-to-many-resource-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('get-one-to-many-resource-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length + 1)) {

@@ -182,22 +150,20 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operation you need to provide resourceIds with length ${this._resourcePath.length + 1}`

}
/**
* @typedef FindOptions
* @name FindOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} match MongoDB match object e.g. { id: 123 }
* @private
* @typedef FindOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {Object} match MongoDB match object e.g. { id: 123 }
*/
/**
* Find a resource by via options.match query.
*
*
* @name find
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {FindOptions} options
* @returns {Object}
* @returns {object}
*/
async find(resourceIds, options) {
return this._withActiveSpan('find-one-to-many-resource-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('find-one-to-many-resource-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length)) {

@@ -214,9 +180,9 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operations you need to provide a resourceId array with length ${this._resourcePath.length}.`

}
/**
* Returns a resource without safety checks and tracing
*
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
*
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
* @private
*/

@@ -230,3 +196,6 @@ async _getResourceWithLookup(resourceIds, options = {}) {

}
/**
*
* @private
*/
async _getParentCollection() {

@@ -236,17 +205,14 @@ const parentCollection = this._resourcePath.at(0)

}
/**
*
*
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
*
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
* @private
*/
_getNestedLookupStages(resourceIds, options) {
const pipeline = []
for (let i = 0, n = resourceIds.length; i < n; i++) {
pipeline.push(EQUALS('id', resourceIds.at(i)))
pipeline.push(PROJECT({ _id: 0, _meta_data: 0 }))
const childCollection = this._resourcePath.at(i + 1) ?? this._resourceName

@@ -256,3 +222,4 @@ const lookupPipeline = []

lookupPipeline.push(EQUALS('id', resourceIds.at(i + 1)))
} else if (options.match) {
}
else if (options.match) {
lookupPipeline.push(EQUALS_ALL(options.match))

@@ -269,3 +236,2 @@ }

pipeline.push(AS_ROOT(childCollection))
if (childCollection === this._resourceName) {

@@ -275,29 +241,29 @@ break

}
if (options.withMetadata) {
pipeline.push(PROJECT({ _id: 0 }))
} else {
}
else {
pipeline.push(PROJECT({ _id: 0, _meta_data: 0 }))
}
if (options.projection) {
pipeline.push(PROJECT(options.projection))
}
return pipeline
}
/**
*
* @param {Array.<string>} resourceIds
* @private
*/
_getResourceIdsForHostStorage(resourceIds) {
return resourceIds.slice(this._resourceLevel - 1)
}
/**
* Returns resources based on return value of {@link findReferences}.
*
*
* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
*/
async getAll(resourceIds, { withMetadata = false, addDocumentPath, projection } = {}) {
return this._withActiveSpan('get-all-one-to-many-resources-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('get-all-one-to-many-resources-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length)) {

@@ -311,3 +277,2 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operations you need to provide a resourceId array with length ${this._resourcePath.length}.`

}
const collection = await this._getParentCollection()

@@ -317,11 +282,8 @@ const aggregationStages = this._getNestedLookupStages(resourceIds, { withMetadata, projection })

const resources = await toArrayAndClose(cursor)
if (!resources || resources.length === 0) {
return []
}
if (addDocumentPath) {
resources.forEach((result) => {
const { id } = result
// not ideal but right now lets only support one reference per document

@@ -340,17 +302,15 @@ // refsArray.forEach((refs) => {

}
return resources
})
}
/**
* Add a resource to a collection by ids.
*
*
* @name create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @returns
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @returns
*/
async create(resourceIds, resource) {
return this._withActiveSpan('create-one-to-many-resource-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('create-one-to-many-resource-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length + 1)) {

@@ -365,5 +325,5 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operations you need to provide a resourceId array with length ${this._resourcePath.length + 1}.`

return this._withTransaction(async (session, client) => {
const newId = await super.create(resourceIds.slice(-1), resource, { session, client })
const newId = resourceIds.at(-1)
await super._createUnsafe(newId, resource, { session, client })
await this._hostStorage.create(this._getResourceIdsForHostStorage(resourceIds), newId, { session, client })
if (this._enableTwoWayReferences) {

@@ -374,6 +334,4 @@ await this._updateUnsafe(newId, {

}
await session.commitTransaction()
await this._childEmitUsageEvent({ context: 'create', after: resource, resourceIds })
await this._emitter.emit('create', { after: resource, resourceIds })
return newId

@@ -383,13 +341,12 @@ })

}
/**
* Updates a resource by ids
*
*
* @name update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @returns
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @returns
*/
async update(resourceIds, update) {
return this._withActiveSpan('update-one-to-many-resource-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('update-one-to-many-resource-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length + 1)) {

@@ -408,11 +365,8 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operation you need to provide resourceIds with length ${this._resourcePath.length + 1}`

}
const updateResult = await this._updateUnsafe(resourceIds.slice(-1), update, { session, client })
await session.commitTransaction()
if (this._emitUsageEventEnabled) {
{
const newResource = await this.get(resourceIds)
await this._childEmitUsageEvent({ context: 'update', before: resource, after: newResource, resourceIds })
await this._emitter.emit('update', { before: resource, after: newResource, resourceIds })
}
return updateResult

@@ -422,12 +376,11 @@ })

}
/**
* Deletes a resource by ids
*
*
* @name delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns
*/
async delete(resourceIds) {
return this._withActiveSpan('delete-one-to-many-resource-by-id', resourceIds, async (span) => {
return this._runWithActiveSpan('delete-one-to-many-resource-by-id', resourceIds, async (span) => {
if (!Array.isArray(resourceIds) || !(resourceIds.length == this._resourcePath.length + 1)) {

@@ -446,9 +399,6 @@ const errorMessage = `Given resourceIds ${resourceIds} and resourcePath ${this._resourcePath} dont match lengths. For this operation you need to provide resourceIds with length ${this._resourcePath.length + 1}`

}
const deleteResult = await this._hostStorage.delete(this._getResourceIdsForHostStorage(resourceIds), { session, client })
await this._deleteUnsafe(resourceIds.slice(-1), resource, { session, client })
await session.commitTransaction()
await this._childEmitUsageEvent({ context: 'delete', before: resource, resourceIds })
await this._emitter.emit('delete', { before: resource, resourceIds })
return deleteResult

@@ -458,6 +408,5 @@ })

}
/**
* Closes all clients.
*
*
* @returns {Promise.<void>}

@@ -474,2 +423,2 @@ */

}
}
}

@@ -1,2 +0,2 @@

export function toArrayAndClose(cursor: import("mongodb").AbstractCursor): Promise<any>;
export function getFirstAndClose(cursor: import("mongodb").AbstractCursor): Promise<any>;
export function toArrayAndClose(cursor: import("mongodb").AbstractCursor): any[];
export function getFirstAndClose(cursor: import("mongodb").AbstractCursor): object;

@@ -1,13 +0,12 @@

'use strict'
/**
*
* @param {import('mongodb').AbstractCursor} cursor
* @returns {Promise}
* @returns {Array}
*/
module.exports.toArrayAndClose = async function (cursor) {
export const toArrayAndClose = async function (cursor) {
try {
const array = await cursor.toArray()
return array
} finally {
}
finally {
await cursor.close()

@@ -20,11 +19,12 @@ }

* @param {import('mongodb').AbstractCursor} cursor
* @returns {Promise}
* @returns {object}
*/
module.exports.getFirstAndClose = async function (cursor) {
export const getFirstAndClose = async function (cursor) {
try {
const first = await cursor.next()
return first
} finally {
}
finally {
await cursor.close()
}
}
}

@@ -1,27 +0,76 @@

export = exports;
declare class exports extends Base {
declare const _default_base: {
new ({ client, databaseName, collectionName }?: import("./base-storage.js").ConstructorOptions): {
_mongoDbClient: any;
_databaseName: any;
_collectionName: any;
_transactionsEnabled: boolean;
_history: import("./history.js").default;
_emitter: import("emittery").default<Record<PropertyKey, any>, Record<PropertyKey, any> & import("emittery").OmnipresentEventData, import("emittery").DatalessEventNames<Record<PropertyKey, any>>>;
_eventListenersCount: {
create: number;
update: number;
delete: number;
};
_getConnectedClient(): import("mongodb").MongoClient;
_getDb(givenClient?: import("mongodb").MongoClient): import("mongodb").Db;
_getCollection(collectionName?: import("mongodb").MongoClient, givenClient?: import("mongodb").MongoClient): Promise<import("./base-storage.js").Collection>;
_runWithActiveSpan(spanName: string, resourceIds: string | Array<string>, callback: Function): Promise<any>;
_withTransaction(callback: (clientSession: import("mongodb").ClientSession) => any): Promise<any>;
_passSessionIfTransactionEnabled(options: object): object;
_createMetadata(): object;
_createUpdateMetadata(): object;
on(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
off(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
enableHistory(): void;
disableHistory(): void;
close(): void;
};
};
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @example
* import { MongoClient } from 'mongodb'
* import { SimpleResourceStorage } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const storage = new SimpleResourceStorage({
* client,
* collectionName: 'api_clients',
* })
*/
/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @private
*/
/**
* @typedef WithMongoClient
* @name WithMongoClient
* @property {import('mongodb').MongoClient} client
* @private
*/
/**
* @typedef {import('mongodb').Collection} Collection
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/
/**
* Simple resource class with crud operation methods to create, update, delete, and
* get stored entities and documents.
*
* @name SimpleResourceStorage
* @class
*/
export default class _default extends _default_base {
/**
* @public
* @param {ConstructorOptions} options
* @returns
*/
constructor({ client, databaseName, collectionName, eventEmitter, indexes }?: ConstructorOptions);
/** @private */ private _eventEmitter;
/** @private */ private _emitUsageEventEnabled;
/** @private */ private _emitUsageEvent;
get usageEventPrefix(): string;
/**
*
* @param {import('node:events').EventEmitter} eventEmitter
*/
enableEventing(eventEmitter: import("node:events").EventEmitter): void;
/**
*
*/
disableEventing(): void;
/**
* @private
*/
private _withActiveSpan;
/**
* @callback WithSessionCallback

@@ -31,5 +80,5 @@ * @param {import('mongodb').ClientSession}

/**
* @private
* @param {Array.<string>} ids
* @returns
* @private
*/

@@ -40,29 +89,16 @@ private _toStringIfArray;

*
* @method get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @function get
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
*/
get(resourceIds: string | Array<string>, { withMetadata, projection }?: {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
/**
* MongoDB match object e.g. { id: 0, name: 0 }
*/
match: any;
}): any;
get(resourceIds: string | Array<string>, { withMetadata, projection }?: any): object;
/**
* Returns a resource by ids.
*
* @function get
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {object}
* @private
* @method get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
*/

@@ -73,32 +109,19 @@ private __getResource;

*
* @method getAll
* @function getAll
* @param {GetOptions} options
* @returns {Array.<Object>}
* @returns {Array.<object>}
*/
getAll({ withMetadata, projection }?: {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
/**
* MongoDB match object e.g. { id: 0, name: 0 }
*/
match: any;
}): Array<any>;
getAll({ withMetadata, projection }?: any): Array<object>;
/**
* @typedef GetOptions
* @name GetChildrenOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} match MongoDB match object e.g. { id: 0, name: 0 }
* @private
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {Object} match MongoDB match object e.g. { id: 0, name: 0 }
*/
/**
* @typedef ChildrenAndResourcePaths
* @property {Array.<Object>} children
* @property {Object} resourcePaths and object mapping child ids to their resource path e.g. { 4: '/queues/123/listeners/4'}
* @property {Array.<object>} children
* @property {object} resourcePaths and object mapping child ids to their resource path e.g. { 4: '/queues/123/listeners/4'}
*/

@@ -111,4 +134,4 @@ /**

* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<String>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {GetChildrenOptions} [options]

@@ -118,7 +141,7 @@ * @returns {Promise.<ChildrenAndResourcePaths>}

getAllChildren(resourceIds: string | Array<string>, childPath: string | Array<string>, { withMetadata, projection, match }?: GetChildrenOptions): Promise<{
children: Array<any>;
children: Array<object>;
/**
* and object mapping child ids to their resource path e.g. { 4: '/queues/123/listeners/4'}
*/
resourcePaths: any;
resourcePaths: object;
}>;

@@ -131,4 +154,4 @@ /**

* @name countAllChildren
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<String>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {GetChildrenOptions} [options]

@@ -141,13 +164,13 @@ * @returns {Promise.<number>}

*
* @function find
* @param {Array.<object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<object>}
* @see {@link README_AGGREGATIONS.md}
* @method find
* @param {Array.<Object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<Object>}
*/
find(aggregations?: Array<any>): Array<any>;
find(aggregations?: Array<object>): Array<object>;
/**
* Returns true if a resource with given ids exists.
*
* @method exists
* @param {String|Array.<String>} resourceIds
* @function exists
* @param {string | Array.<string>} resourceIds
* @returns {boolean}

@@ -159,10 +182,21 @@ */

*
* @method create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
*/
create(resourceIds: string | Array<string>, resource: any, options?: import("mongodb").InsertOneOptions): Promise<any>;
create(resourceIds: string | Array<string>, resource: object, options?: import("mongodb").InsertOneOptions): Promise<any>;
/**
* Adds a resource to a collection without any checks.
*
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
* @protected
*/
protected _createUnsafe(resourceIds: string | Array<string>, resource: object, options?: import("mongodb").InsertOneOptions): Promise<void>;
/**
* @typedef UpdateOptions

@@ -174,9 +208,9 @@ * @property {boolean} [upsert=false]

*
* @method update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @function update
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @param {UpdateOptions} options
* @returns
*/
update(resourceIds: string | Array<string>, update: any, options: {
update(resourceIds: string | Array<string>, update: object, options: {
upsert?: boolean;

@@ -187,7 +221,7 @@ }): Promise<any>;

*
* @private
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
* @private
*/

@@ -198,4 +232,4 @@ private _updateUnsafe;

*
* @method delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @function delete
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns

@@ -207,49 +241,14 @@ */

*
* @private
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
* @private
*/
private _deleteUnsafe;
}
declare namespace exports {
export { ConstructorOptions, GetOptions, WithMongoClient, Collection, MongoClient };
}
import Base = require("./base-storage.js");
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
/**
* indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
*/
indexes?: Array<any>;
/**
* if provided, will trigger events base on resource creation, updates and deletion
*/
eventEmitter: import("node:events").EventEmitter;
};
type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
};
type WithMongoClient = {
client: import("mongodb").MongoClient;
};
type Collection = import("mongodb").Collection;
type MongoClient = import("mongodb").MongoClient;
export type ConstructorOptions = any;
export type GetOptions = any;
export type WithMongoClient = any;
export type Collection = import("mongodb").Collection;
export type MongoClient = import("mongodb").MongoClient;
export {};

@@ -1,30 +0,16 @@

'use strict'
import { EQUALS, LIMIT, PROJECT } from './aggregations.js'
import Base from './base-storage.js'
import { getSingleLookupPipeline, joinAndQueryChildResourcesPipeline } from './lookup-pipeline.js'
import { getFirstAndClose, toArrayAndClose } from './safe-cursor.js'
const Base = require('./base-storage.js')
const { EQUALS, LIMIT, PROJECT } = require('./aggregations.js')
const eventTrigger = require('./usage-event-trigger.js')
const { createTracer } = require('@discue/open-telemetry-tracing')
const { name } = require('../package.json')
const { getSingleLookupPipeline, joinAndQueryChildResourcesPipeline } = require('./lookup-pipeline.js')
const { toArrayAndClose, getFirstAndClose } = require('./safe-cursor.js')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {Array.<Object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @property {import('node:events').EventEmitter} eventEmitter if provided, will trigger events base on resource creation, updates and deletion
*
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @example
* const { MongoClient } = require('mongodb')
* const { SimpleResourceStorage } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { SimpleResourceStorage } from '@discue/mongodb-resource-client'
*

@@ -42,20 +28,20 @@ * const client = new MongoClient(url, {

/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @private
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @typedef WithMongoClient
* @name WithMongoClient
* @property {import('mongodb').MongoClient} client
* @private
* @typedef WithMongoClient
* @property {import('mongodb').MongoClient} client
*/
/**
* @private
* @typedef {import('mongodb').Collection} Collection
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/

@@ -70,17 +56,12 @@

*/
module.exports = class extends Base {
export default class extends Base {
/**
* @param {ConstructorOptions} options
* @returns
* @public
* @param {ConstructorOptions} options
* @returns
*/
constructor({ client, databaseName, collectionName, eventEmitter, indexes } = {}) {
constructor({ client, databaseName, collectionName, indexes } = {}) {
super({ client, databaseName, collectionName })
/** @private */ this._eventEmitter = eventEmitter
/** @private */ this._emitUsageEventEnabled = eventEmitter != null
/** @private */ this._emitUsageEvent = eventTrigger(this.usageEventPrefix, collectionName, eventEmitter)
this._getCollection().then(async collection => {
this._getCollection().then(async (collection) => {
if (indexes?.length > 0) {

@@ -90,3 +71,4 @@ const indexSpecifications = indexes.map((index) => {

return index
} else {
}
else {
return { key: index }

@@ -105,40 +87,10 @@ }

get usageEventPrefix() {
return `simple-resource.${this._collectionName}`
}
/**
*
* @param {import('node:events').EventEmitter} eventEmitter
*/
enableEventing(eventEmitter) {
this._emitUsageEventEnabled = eventEmitter != null
this._emitUsageEvent = eventTrigger(this.usageEventPrefix, this._collectionName, eventEmitter)
}
/**
*
*/
disableEventing() {
this._emitUsageEventEnabled = false
this._emitUsageEvent = null
}
/**
* @private
*/
async _withActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}
/**
* @callback WithSessionCallback
* @param {import('mongodb').ClientSession}
*/
/**
* @param {Array.<string>} ids
* @returns
* @private
* @param {Array.<string>} ids
* @returns
*/

@@ -151,14 +103,14 @@ _toStringIfArray(ids) {

return ids.join('#')
} else {
}
else {
return ids
}
}
/**
* Returns a resource by ids.
*
* @method get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
*
* @function get
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
* @returns {object}
*/

@@ -168,14 +120,13 @@ async get(resourceIds, { withMetadata = false, projection } = {}) {

}
/**
* Returns a resource by ids.
*
*
* @function get
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {object}
* @private
* @method get
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {GetOptions} options
* @returns {Object}
*/
async __getResource(resourceIds, { withMetadata = false, projection } = {}) {
return this._withActiveSpan('get-simple-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('get-simple-resource-by-id', resourceIds, async () => {
const collection = await this._getCollection()

@@ -186,13 +137,11 @@ const aggregationStages = [

]
if (!withMetadata) {
aggregationStages.push(PROJECT({ _id: 0, _meta_data: 0 }))
} else {
}
else {
aggregationStages.push(PROJECT({ _id: 0 }))
}
if (projection) {
aggregationStages.push(PROJECT(projection))
}
const cursor = collection.aggregate(aggregationStages)

@@ -202,25 +151,22 @@ return getFirstAndClose(cursor)

}
/**
* Returns all resources.
*
* @method getAll
*
* @function getAll
* @param {GetOptions} options
* @returns {Array.<Object>}
* @returns {Array.<object>}
*/
async getAll({ withMetadata = false, projection } = {}) {
return this._withActiveSpan('get-all-simple-resources', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
return this._runWithActiveSpan('get-all-simple-resources', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
const collection = await this._getCollection()
const aggregationStages = []
if (!withMetadata) {
aggregationStages.push(PROJECT({ _id: 0, _meta_data: 0 }))
} else {
}
else {
aggregationStages.push(PROJECT({ _id: 0 }))
}
if (projection) {
aggregationStages.push(PROJECT(projection))
}
const cursor = collection.aggregate(aggregationStages)

@@ -230,26 +176,23 @@ return toArrayAndClose(cursor)

}
/**
* @typedef GetOptions
* @name GetChildrenOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {object} match MongoDB match object e.g. { id: 0, name: 0 }
* @private
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @property {Object} match MongoDB match object e.g. { id: 0, name: 0 }
*/
/**
* @typedef ChildrenAndResourcePaths
* @property {Array.<Object>} children
* @property {Object} resourcePaths and object mapping child ids to their resource path e.g. { 4: '/queues/123/listeners/4'}
* @property {Array.<object>} children
* @property {object} resourcePaths and object mapping child ids to their resource path e.g. { 4: '/queues/123/listeners/4'}
*/
/**
* Returns all children of a certain type/collection. Imagine this method walking a tree and returning all leaves at a certain level.
*
*
* Currently only supports trees with three levels.
*
*
* @name getAll
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<String>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {GetChildrenOptions} [options]

@@ -259,3 +202,3 @@ * @returns {Promise.<ChildrenAndResourcePaths>}

async getAllChildren(resourceIds, childPath, { withMetadata = false, projection, match } = {}) {
return this._withActiveSpan('get-all-simple-resource-children', resourceIds, async () => {
return this._runWithActiveSpan('get-all-simple-resource-children', resourceIds, async () => {
if (childPath.startsWith('/')) {

@@ -268,9 +211,6 @@ childPath = childPath.substring(1)

}
const lookupPipeline = getSingleLookupPipeline({ rootId: this._toStringIfArray(resourceIds), childCollectionName: parent })
const childResourcesPipeline = joinAndQueryChildResourcesPipeline({ parentCollectionName: parent, childCollectionName: child, options: { withMetadata, projection, match } })
lookupPipeline.push(...childResourcesPipeline)
const collection = await this._getCollection()
const cursor = collection.aggregate(lookupPipeline)

@@ -281,18 +221,15 @@ const result = await toArrayAndClose(cursor)

}
const children = result.at(0).children ?? []
const resourcePaths = result.at(0).resource_paths ?? []
return { children, resourcePaths }
})
}
/**
* Returns the count of all children of a certain type/collection.
*
* Returns the count of all children of a certain type/collection.
*
* Currently only supports trees with three levels.
*
*
* @name countAllChildren
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {String|Array.<String>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {string | Array.<string>} childPath the path of the children to query e.g. /api_clients/queues/messages
* @param {GetChildrenOptions} [options]

@@ -302,3 +239,3 @@ * @returns {Promise.<number>}

async countAllChildren(resourceIds, childPath, { withMetadata = false, projection, match } = {}) {
return this._withActiveSpan('count-all-simple-resource-children', resourceIds, async () => {
return this._runWithActiveSpan('count-all-simple-resource-children', resourceIds, async () => {
if (childPath.startsWith('/')) {

@@ -311,3 +248,2 @@ childPath = childPath.substring(1)

}
const lookupPipeline = getSingleLookupPipeline({ rootId: this._toStringIfArray(resourceIds), childCollectionName: parent })

@@ -317,5 +253,3 @@ const childResourcesPipeline = joinAndQueryChildResourcesPipeline({ parentCollectionName: parent, childCollectionName: child, options: { withMetadata, projection, match } })

lookupPipeline.push(PROJECT({ count: { $size: '$children' } }))
const collection = await this._getCollection()
const cursor = collection.aggregate(lookupPipeline)

@@ -326,19 +260,16 @@ const result = await toArrayAndClose(cursor)

}
return result.at(0).count
})
}
/**
* Returns all resources that pass the given aggregation stages.
*
*
* @function find
* @param {Array.<object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<object>}
* @see {@link README_AGGREGATIONS.md}
* @method find
* @param {Array.<Object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<Object>}
*/
async find(aggregations = []) {
return this._withActiveSpan('find-simple-resources', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
return this._runWithActiveSpan('find-simple-resources', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
const collection = await this._getCollection()
const cursor = collection.aggregate(aggregations)

@@ -348,12 +279,11 @@ return toArrayAndClose(cursor)

}
/**
* Returns true if a resource with given ids exists.
*
* @method exists
* @param {String|Array.<String>} resourceIds
*
* @function exists
* @param {string | Array.<string>} resourceIds
* @returns {boolean}
*/
async exists(resourceIds) {
return this._withActiveSpan('exists-simple-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('exists-simple-resource-by-id', resourceIds, async () => {
const resource = await this.get(resourceIds)

@@ -363,29 +293,15 @@ return resource != undefined

}
/**
* Adds a resource to a collection by ids.
*
* @method create
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} resource the resource to be stored
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
* @returns
*/
async create(resourceIds, resource, options) {
return this._withActiveSpan('create-simple-resource-by-id', resourceIds, async () => {
const collection = await this._getCollection()
const result = await collection.insertOne(Object.assign(resource, {
id: this._toStringIfArray(resourceIds),
_meta_data: this._createMetadata()
}), this._passSessionIfTransactionEnabled(options))
const success = result.acknowledged === true
if (!success) {
throw new Error(`Was not able to insert ${resourceIds} with resource ${resource}`)
}
if (this._emitUsageEventEnabled) {
await this._emitUsageEvent({ context: 'create', after: resource, resourceIds })
}
return this._runWithActiveSpan('create-simple-resource-by-id', resourceIds, async () => {
await this._createUnsafe(resourceIds, resource, options)
await this._emitter.emit('create', { after: resource, resourceIds })
return this._toStringIfArray(resourceIds)

@@ -396,17 +312,38 @@ })

/**
* Adds a resource to a collection without any checks.
*
* @function create
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} resource the resource to be stored
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
* @protected
*/
async _createUnsafe(resourceIds, resource, options) {
const collection = await this._getCollection()
const result = await collection.insertOne(Object.assign(resource, {
id: this._toStringIfArray(resourceIds),
_meta_data: this._createMetadata()
}), this._passSessionIfTransactionEnabled(options))
const success = result.acknowledged === true
if (!success) {
throw new Error(`Was not able to insert ${resourceIds} with resource ${resource}`)
}
}
/**
* @typedef UpdateOptions
* @property {boolean} [upsert=false]
*/
/**
* Updates a resource by ids.
*
* @method update
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
*
* @function update
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @param {UpdateOptions} options
* @returns
* @returns
*/
async update(resourceIds, update, options) {
return this._withActiveSpan('update-simple-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('update-simple-resource-by-id', resourceIds, async () => {
return this._withTransaction(async (session, client) => {

@@ -420,11 +357,6 @@ let resource = null

}
const updateResult = await this._updateUnsafe(resourceIds, update, { session, client, upsert: options?.upsert })
await session.commitTransaction()
if (this._emitUsageEventEnabled) {
const newResource = await this.get(resourceIds)
await this._emitUsageEvent({ context: 'update', before: resource, after: newResource, resourceIds })
}
const newResource = await this.get(resourceIds)
await this._emitter.emit('update', { before: resource, after: newResource, resourceIds })
return updateResult

@@ -434,11 +366,10 @@ })

}
/**
* Updates a resource by ids without checking its existence
*
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {object} update values that should be updated
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
* @private
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {Object} update values that should be updated
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
*/

@@ -453,3 +384,2 @@ async _updateUnsafe(resourceIds, update, options) {

}
const objectKeys = Object.keys(update)

@@ -461,3 +391,4 @@ const isSetOperation = objectKeys.at(0) === '$set'

}
} else {
}
else {
// let's not generically interfere with updates and in this case

@@ -467,3 +398,2 @@ // update meta data with another operation

}
const collection = await this._getCollection(options.client)

@@ -473,3 +403,2 @@ const result = await collection.updateOne({

}, update, this._passSessionIfTransactionEnabled(options))
const success = result.acknowledged === true

@@ -479,3 +408,2 @@ if (!success) {

}
if (updateMetadataSeparately) {

@@ -489,12 +417,11 @@ await collection.updateOne({

}
/**
* Deletes a resource by ids.
*
* @method delete
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns
*
* @function delete
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @returns
*/
async delete(resourceIds) {
return this._withActiveSpan('delete-simple-resource-by-id', resourceIds, async () => {
return this._runWithActiveSpan('delete-simple-resource-by-id', resourceIds, async () => {
return this._withTransaction(async (session, client) => {

@@ -505,10 +432,5 @@ const resource = await this.get(resourceIds)

}
const deleteResult = await this._deleteUnsafe(resourceIds, resource, { session, client })
await session.commitTransaction()
if (this._emitUsageEventEnabled) {
await this._emitUsageEvent({ context: 'delete', before: resource, resourceIds })
}
await this._emitter.emit('delete', { before: resource, resourceIds })
return deleteResult

@@ -518,10 +440,9 @@ })

}
/**
* Deletes a resource by ids without checking its existence.
*
*
* @param {string | Array.<string>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
* @private
* @param {String|Array.<String>} resourceIds resource ids that will added to the resource path i.e. /users/${id}/documents/${id}
* @param {import('mongodb').DeleteOptions | WithMongoClient} options
* @returns
*/

@@ -533,3 +454,2 @@ async _deleteUnsafe(resourceIds, options) {

}, this._passSessionIfTransactionEnabled(options))
const success = result.acknowledged === true && result.deletedCount > 0

@@ -540,15 +460,2 @@ if (!success) {

}
/**
* Closes the database client
*
* @method close
* @returns {void}
*/
async close() {
if (this._emitUsageEventEnabled) {
await this._emitUsageEvent({ context: 'close' })
}
return super.close()
}
}
}

@@ -1,74 +0,92 @@

export = exports;
declare class exports extends BaseStorage {
/**
* @public
* @param {ConstructorOptions} options
* @returns
*/
constructor({ client, databaseName, collectionName, eventEmitter, timeseries: { timeField, metaField, granularity } }?: ConstructorOptions);
declare const _default_base: {
new ({ client, databaseName, collectionName }?: import("./base-storage.js").ConstructorOptions): {
_mongoDbClient: any;
_databaseName: any;
_collectionName: any;
_transactionsEnabled: boolean;
_history: import("./history.js").default;
_emitter: import("emittery").default<Record<PropertyKey, any>, Record<PropertyKey, any> & import("emittery").OmnipresentEventData, import("emittery").DatalessEventNames<Record<PropertyKey, any>>>;
_eventListenersCount: {
create: number;
update: number;
delete: number;
};
_getConnectedClient(): import("mongodb").MongoClient;
_getDb(givenClient?: import("mongodb").MongoClient): import("mongodb").Db;
_getCollection(collectionName?: import("mongodb").MongoClient, givenClient?: import("mongodb").MongoClient): Promise<import("./base-storage.js").Collection>;
_runWithActiveSpan(spanName: string, resourceIds: string | Array<string>, callback: Function): Promise<any>;
_withTransaction(callback: (clientSession: import("mongodb").ClientSession) => any): Promise<any>;
_passSessionIfTransactionEnabled(options: object): object;
_createMetadata(): object;
_createUpdateMetadata(): object;
on(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
off(eventName: ("create" | "update" | "delete" | "close"), callback: () => Promise<any>): Function;
enableHistory(): void;
disableHistory(): void;
close(): void;
};
};
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @example
* import { MongoClient } from 'mongodb'
* import { SimpleTimeseriesStorage } from '@discue/mongodb-resource-client'
*
* const client = new MongoClient(url, {
* serverApi: { version: '1', strict: true, deprecationErrors: true }, // https://www.mongodb.com/docs/manual/reference/stable-api/
* })
*
* const storage = new SimpleTimeseriesStorage({
* client,
* collectionName: 'api_access',
* })
*/
/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @private
*/
/**
* @typedef {import('mongodb').Collection} Collection
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/
/**
* Simple resource class that allows appending to Timeseries
*
* @name SimpleTimeseriesStorage
* @class
*/
export default class _default extends _default_base {
/** @private */ private _timeField;
/** @private */ private _eventEmitter;
/** @private */ private _emitUsageEventEnabled;
/** @private */ private _emitUsageEvent;
get usageEventPrefix(): string;
/**
* @private
*/
private _withActiveSpan;
/**
* Returns all resources that pass the given aggregation stages.
*
* @function find
* @param {Array.<object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<object>}
* @see {@link README_AGGREGATIONS.md}
* @method find
* @param {Array.<Object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<Object>}
*/
find(aggregations?: Array<any>): Array<any>;
find(aggregations?: Array<object>): Array<object>;
/**
* Adds a resource to the timeseries.
*
* @method create
* @param {Object} resource the resource to be stored. If timeField is missing, current timestamp will be added
* @function create
* @param {object} resource the resource to be stored. If timeField is missing, current timestamp will be added
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
*/
create(resource: any, options?: import("mongodb").InsertOneOptions): Promise<any>;
create(resource: object, options?: import("mongodb").InsertOneOptions): Promise<any>;
}
declare namespace exports {
export { ConstructorOptions, GetOptions, Collection, MongoClient };
}
import BaseStorage = require("./base-storage.js");
type ConstructorOptions = {
/**
* configured mongo client to use. Can be null if url is set
*/
client: MongoClient;
/**
* name of the mongodb database
*/
databaseName?: string;
/**
* name of the mongodb collection used to store the resources
*/
collectionName: string;
/**
* indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
*/
indexes?: Array<any>;
/**
* if provided, will trigger events base on resource creation, updates and deletion
*/
eventEmitter: import("node:events").EventEmitter;
};
type GetOptions = {
/**
* true if also meta data should be returned
*/
withMetadata: boolean;
/**
* MongoDB projection object e.g. { id: 0, name: 0 }
*/
projection: any;
};
type Collection = import("mongodb").Collection;
type MongoClient = import("mongodb").MongoClient;
export type ConstructorOptions = any;
export type GetOptions = any;
export type Collection = import("mongodb").Collection;
export type MongoClient = import("mongodb").MongoClient;
export {};

@@ -1,28 +0,15 @@

'use strict'
import BaseStorage from './base-storage.js'
import { toArrayAndClose } from './safe-cursor.js'
const BaseStorage = require('./base-storage.js')
const { createTracer } = require('@discue/open-telemetry-tracing')
const { name } = require('../package.json')
const { toArrayAndClose } = require('./safe-cursor.js')
const eventTrigger = require('./usage-event-trigger.js')
/**
* @private
*/
const { withActiveSpan } = createTracer({
filepath: __filename
})
/**
* @typedef ConstructorOptions
* @name ConstructorOptions
* @typedef ConstructorOptions
* @property {MongoClient} client configured mongo client to use. Can be null if url is set
* @property {string} [databaseName=null] name of the mongodb database
* @property {string} collectionName name of the mongodb collection used to store the resources
* @property {Array.<Object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @property {import('node:events').EventEmitter} eventEmitter if provided, will trigger events base on resource creation, updates and deletion
*
* @property {Array.<object>} [indexes=null] indexes to be created on instantiation. Use format {key:1} for single indexes and {key1: 1, key:2} for compound indexes. See https://www.mongodb.com/docs/manual/reference/command/createIndexes/#command-fields
* @example
* const { MongoClient } = require('mongodb')
* const { SimpleTimeseriesStorage } = require('@discue/mongodb-resource-client')
* import { MongoClient } from 'mongodb'
* import { SimpleTimeseriesStorage } from '@discue/mongodb-resource-client'
*

@@ -40,13 +27,13 @@ * const client = new MongoClient(url, {

/**
* @typedef GetOptions
* @name GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {object} projection MongoDB projection object e.g. { id: 0, name: 0 }
* @private
* @typedef GetOptions
* @property {boolean} withMetadata true if also meta data should be returned
* @property {Object} projection MongoDB projection object e.g. { id: 0, name: 0 }
*/
/**
* @private
* @typedef {import('mongodb').Collection} Collection
* @typedef {import('mongodb').MongoClient} MongoClient
* @private
*/

@@ -60,24 +47,13 @@

*/
module.exports = class extends BaseStorage {
export default class extends BaseStorage {
/**
* @param {ConstructorOptions} options
* @returns
* @public
* @param {ConstructorOptions} options
* @returns
*/
constructor(
{ client, databaseName, collectionName, eventEmitter, timeseries: {
timeField = 'timestamp',
metaField = 'metadata',
granularity = 'seconds'
} = {}
} = {}) {
constructor({ client, databaseName, collectionName, timeseries: { timeField = 'timestamp', metaField = 'metadata', granularity = 'seconds' } = {} } = {}) {
super({ client, databaseName, collectionName })
/** @private */ this._timeField = timeField
/** @private */ this._eventEmitter = eventEmitter
/** @private */ this._emitUsageEventEnabled = eventEmitter != null
/** @private */ this._emitUsageEvent = eventTrigger(this.usageEventPrefix, collectionName, eventEmitter)
super._getDb().then((db) => {

@@ -88,25 +64,13 @@ db.createCollection(collectionName, { timeseries: { timeField, metaField, granularity } }).catch(() => { })

get usageEventPrefix() {
return `simple-timeseries.${this._collectionName}`
}
/**
* @private
*/
async _withActiveSpan(spanName, resourceIds, callback) {
return withActiveSpan(`${name}#${spanName}`, { 'peer.service': 'resource-client', resourceIds, resourceName: this._collectionName, databaseName: this._databaseName }, callback)
}
/**
* Returns all resources that pass the given aggregation stages.
*
*
* @function find
* @param {Array.<object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<object>}
* @see {@link README_AGGREGATIONS.md}
* @method find
* @param {Array.<Object>} [aggregations=[]] a list of valid aggregation objects
* @returns {Array.<Object>}
*/
async find(aggregations = []) {
return this._withActiveSpan('find-timeseries-data', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
return this._runWithActiveSpan('find-timeseries-data', { resourceName: this._collectionName, databaseName: this._databaseName }, async () => {
const collection = await super._getCollection()
const cursor = collection.aggregate(aggregations)

@@ -116,20 +80,17 @@ return toArrayAndClose(cursor)

}
/**
* Adds a resource to the timeseries.
*
* @method create
* @param {Object} resource the resource to be stored. If timeField is missing, current timestamp will be added
* @function create
* @param {object} resource the resource to be stored. If timeField is missing, current timestamp will be added
* @param {import('mongodb').InsertOneOptions} [options=null]
* @returns
* @returns
*/
async create(resource, options) {
return this._withActiveSpan('add-timeseries-data', null, async () => {
return this._runWithActiveSpan('add-timeseries-data', null, async () => {
if (!resource[this._timeField]) {
resource[this._timeField] = new Date()
}
const collection = await super._getCollection()
const result = await collection.insertOne(resource, options)
const success = result.acknowledged === true

@@ -141,2 +102,2 @@ if (!success) {

}
}
}

@@ -1,2 +0,2 @@

export { Timestamp };
import { Timestamp } from "mongodb";
export const Timestamp: typeof mongodb.BSON.Timestamp;
import * as mongodb from 'mongodb';

@@ -1,5 +0,5 @@

'use strict'
import * as mongodb from 'mongodb'
const { Timestamp } = require('mongodb')
const { Timestamp } = mongodb
export { Timestamp }
module.exports.Timestamp = Timestamp

@@ -5,5 +5,6 @@ {

"license": "MIT",
"version": "0.40.0",
"version": "1.0.0",
"description": "Simple wrapper around mongodb client allowing easier managing of resources",
"main": "lib/index",
"type": "module",
"files": [

@@ -57,14 +58,19 @@ "lib"

"devDependencies": {
"@discue/open-telemetry-tracing": "^1.0.0",
"@discue/open-telemetry-tracing": "^1.1.0",
"@stylistic/eslint-plugin": "^2.6.2",
"@types/chai": "^4.3.14",
"@types/mocha": "^10.0.7",
"@types/node": "^20.14.10",
"@types/node": "^22.0.2",
"chai": "4",
"documentation": "^14.0.3",
"eslint": "^9.6.0",
"mocha": "^10.6.0",
"eslint": "^9.8.0",
"eslint-plugin-jsdoc": "^50.0.1",
"mocha": "^10.7.0",
"mongodb": "^6.8.0",
"standard-version": "^9.5.0",
"typescript": "^5.5.3"
"typescript": "^5.5.4"
},
"dependencies": {
"emittery": "^1.0.3"
}
}

@@ -52,11 +52,8 @@

## History / Auditing
The module provides support for history / auditing tables to keep track of changes made to documents. The `ResourceStorageHistory` component can be used as an extension
of a storage instance e.g. `SimpleResourceStorage`. An instance of `ResourceStorageHistory` can listen to storage events of another storage instance and populate a `${resourceName}_history` collection with timestamp, change type, and the full resource state.
The module provides support for history / auditing tables to keep track of changes made to documents by calling the `enableHistory` method.
```javascript
const { MongoClient } = require('mongodb')
const { EventEmiter } = require('events')
const { OneToFewResourceStorage, ResourceStorageHistory } = require('@discue/mongodb-resource-client')
import { MongoClient } from ('mongodb')
import { OneToFewResourceStorage, ResourceStorageHistory } from ('@discue/mongodb-resource-client')
const eventEmitter = new EventEmitter()
const collectionName = 'api_clients'

@@ -75,9 +72,3 @@ const url = 'mongodb://127.0.0.1:27017'

const history = new ResourceStorageHistory({
client,
collectionName,
usageEventPrefix: oneToFewResourceStorage.usageEventPrefix
eventEmitter
})
history.listenForStorageEvents()
oneToFewResourceStorage.enableHistory()
```

@@ -84,0 +75,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc