mongodb-data-api-odm
Advanced tools
Comparing version 1.0.1 to 1.0.2-16
@@ -5,3 +5,3 @@ /// <reference path="./typings/lib/index.d.ts" /> | ||
const Collection: (name: string) => <L>(target: Collection.EntityConstructor<L>) => void; | ||
const Field: (config?: Collection.EntityField) => (target: any, key: string) => void; | ||
const Field: (config?: Partial<Collection.EntityField>) => (target: any, key: string) => void; | ||
} |
{ | ||
"name": "mongodb-data-api-odm", | ||
"version": "1.0.1", | ||
"version": "1.0.2-16", | ||
"description": "An ODM implementation for Atlas MongoDB Data API", | ||
@@ -9,3 +9,3 @@ "main": "src/index.js", | ||
"scripts": { | ||
"build": "rm -rf dist; tsc --build;npm run copy-files", | ||
"build": "rm -rf dist; tsc -p ./tsconfig-build.json;npm run copy-files", | ||
"copy-files": "cp -r ./typings dist; cp index.d.ts README.md LICENSE dist", | ||
@@ -26,3 +26,6 @@ "test": "jest" | ||
"devDependencies": { | ||
"@types/jest": "^29.5.4", | ||
"@types/node": "^20.4.9", | ||
"jest": "^29.6.4", | ||
"ts-jest": "^29.1.1", | ||
"typescript": "^5.1.6" | ||
@@ -29,0 +32,0 @@ }, |
@@ -5,4 +5,7 @@ "use strict"; | ||
const Collection = (name) => (target) => { | ||
target.__entity__ = { ...target.__entity__, name }; | ||
target.__entity__ = { | ||
...Object.getOwnPropertyDescriptors(target).prototype.value.__entity__, | ||
name | ||
}; | ||
}; | ||
exports.Collection = Collection; |
@@ -5,8 +5,9 @@ "use strict"; | ||
const Field = (config) => (target, key) => { | ||
target.__proto__.__entity__ = { | ||
...target.__proto__.__entity__, | ||
target.__entity__ = { | ||
...target.__entity__, | ||
fields: { | ||
...target.__proto__.__entity__?.fields, | ||
[key]: config || { | ||
required: false, | ||
...target.__entity__?.fields, | ||
[key]: { | ||
name: config?.name || key, | ||
required: !!config?.required, | ||
} | ||
@@ -13,0 +14,0 @@ } |
@@ -5,29 +5,71 @@ "use strict"; | ||
class CollectionDocument { | ||
target; | ||
entity; | ||
constructor(target) { | ||
this.target = target; | ||
this.entity = target.__entity__; | ||
} | ||
/** | ||
* | ||
* @param data | ||
* @returns | ||
*/ | ||
parse(data) { | ||
if (Array.isArray(data)) { | ||
return data.map(data => this.parse(data)); | ||
} | ||
const doc = new this.target(); | ||
Object.keys(this.entity.fields).forEach(field => { | ||
const value = data[field]; | ||
const config = this.entity.fields[field]; | ||
if (!value && config.required) { | ||
throw new Error(`${this.entity.name}.${field} is required`); | ||
} | ||
Object.defineProperty(doc, field, { value, writable: false }); | ||
static create(target, data) { | ||
const fields = this.parseFields(target, data); | ||
const doc = new target(); | ||
Object.keys(fields).forEach(field => { | ||
Object.defineProperty(doc, field, { value: fields[field], writable: false }); | ||
}); | ||
// const entity = target.__entity__!; | ||
// Object.keys(entity.fields).forEach(field => { | ||
// const { name, required } = entity.fields[field]; | ||
// const value = (data as never)[name]; | ||
// if (!value && required) { | ||
// throw new Error(`${entity.name}.${field} is required`); | ||
// } | ||
// Object.defineProperty(doc, field, { value, writable: false }); | ||
// }); | ||
Object.defineProperties(doc, { | ||
toObject: { | ||
writable: false, | ||
enumerable: true, | ||
value({ getters } = {}) { | ||
const data = {}; | ||
Object.keys(doc).forEach(key => { | ||
if (typeof doc[key] !== 'function') { | ||
data[key] = doc[key]; | ||
} | ||
}); | ||
if (getters) { | ||
const proto = Object.getPrototypeOf(doc); | ||
Object.getOwnPropertyNames(proto).forEach(key => { | ||
const desc = Object.getOwnPropertyDescriptor(proto, key); | ||
if (typeof desc?.get === 'function') { | ||
data[key] = desc.get.apply(doc); | ||
} | ||
}); | ||
} | ||
return data; | ||
} | ||
}, | ||
toOriginal: { | ||
writable: false, | ||
enumerable: true, | ||
value() { | ||
return data; | ||
} | ||
}, | ||
}); | ||
return doc; | ||
} | ||
static parseFields(target, data, insertion = false) { | ||
const doc = {}; | ||
const { __entity__: entity } = target; | ||
return !entity?.fields ? doc : Object.keys(entity.fields).reduce((doc, field) => { | ||
const { name, required } = entity.fields[field]; | ||
const valueKey = insertion ? field : name; | ||
const fieldKey = insertion ? name : field; | ||
const value = data[valueKey]; | ||
if (!value && required) { | ||
throw new Error(`${entity.name}.${field} is required`); | ||
} | ||
if (fieldKey === '_id' && insertion && !value) { | ||
return doc; | ||
} | ||
return { | ||
...doc, | ||
[fieldKey]: value, | ||
}; | ||
}, doc); | ||
} | ||
} | ||
exports.CollectionDocument = CollectionDocument; |
@@ -6,5 +6,4 @@ "use strict"; | ||
class Collection { | ||
target; | ||
api; | ||
name; | ||
document; | ||
/** | ||
@@ -16,9 +15,9 @@ * | ||
constructor(target, api) { | ||
this.target = target; | ||
this.api = api; | ||
this.name = target.__entity__?.name; | ||
this.document = new document_1.CollectionDocument(target); | ||
} | ||
async insert(document) { | ||
async insert(data) { | ||
const document = this.getInsertionDoc(data); | ||
const response = await this.api.insert(this.getActionPayload({ document })); | ||
return this.document.parse({ ...document, _id: response.insertedId }); | ||
return this.getDoc({ ...document, _id: response.insertedId }); | ||
} | ||
@@ -28,13 +27,14 @@ async create(data) { | ||
} | ||
async insertMany(documents) { | ||
async insertMany(data) { | ||
const documents = data.map(doc => this.getInsertionDoc(doc)); | ||
const response = await this.api.insertMany(this.getActionPayload({ documents })); | ||
return response.insertedIds.map((_id, index) => this.document.parse({ _id, ...documents[index] })); | ||
return response.insertedIds.map((_id, index) => this.getDoc({ _id, ...documents[index] })); | ||
} | ||
async find(filter, projection, options) { | ||
const response = await this.api.find(this.getActionPayload({ filter, projection, ...options })); | ||
return this.document.parse(response.documents); | ||
return response.documents.map(doc => this.getDoc(doc)); | ||
} | ||
async findOne(filter, projection) { | ||
const response = await this.api.findOne(this.getActionPayload({ filter, projection })); | ||
return response.document ? this.document.parse(response.document) : null; | ||
return response.document ? this.getDoc(response.document) : null; | ||
} | ||
@@ -61,3 +61,3 @@ async findById($oid, projection) { | ||
const doc = await this.findOne(filter, { _id: true }); | ||
return this.findByIdAndUpdate(doc._id, data); | ||
return doc ? this.findByIdAndUpdate(doc.toOriginal()._id, data) : null; | ||
} | ||
@@ -82,7 +82,26 @@ async deleteOne(filter) { | ||
const data = await this.findOne(filter, { _id: true }); | ||
const deletion = data ? await this.deleteById(data._id) : null; | ||
const deletion = data ? await this.deleteById(data.toOriginal()._id) : null; | ||
return deletion ? data : null; | ||
} | ||
/** | ||
* Construct the document result instance | ||
* | ||
* @param docuemnt | ||
* @returns | ||
*/ | ||
getDoc(docuemnt) { | ||
return document_1.CollectionDocument.create(this.target, docuemnt); | ||
} | ||
/** | ||
* Return document fields for insertion | ||
* | ||
* @param docuemnt | ||
* @returns | ||
*/ | ||
getInsertionDoc(docuemnt) { | ||
return document_1.CollectionDocument.parseFields(this.target, docuemnt, true); | ||
} | ||
/** | ||
* Build the a action payload object | ||
* | ||
* @param name | ||
@@ -93,5 +112,5 @@ * @param args | ||
getActionPayload(custom) { | ||
return { collection: this.name, ...custom, }; | ||
return { collection: this.target.__entity__.name, ...custom, }; | ||
} | ||
} | ||
exports.Collection = Collection; |
@@ -28,2 +28,3 @@ type BaseTypes = string | boolean | number | BigInt; | ||
$nor?: OperationFilters<T>[]; | ||
[k: string]: Operators | NotExpression | BaseTypes | ||
}; | ||
@@ -92,3 +93,5 @@ | ||
type Doc<T> = T & { _id: string }; | ||
type Doc<T> = T & { | ||
_id: string; | ||
}; | ||
@@ -95,0 +98,0 @@ interface IFind<T> { |
declare namespace Collection { | ||
interface EntityField { | ||
name: string; | ||
required: boolean; | ||
@@ -17,5 +18,9 @@ } | ||
type Doc<T> = Omit<T, '_id'> & { _id: string }; | ||
type DocIn<T> = Omit<T, '_id'>; | ||
type Doc<T> = T & { | ||
toObject(options?: { getters?: boolean }): T; | ||
toOriginal<D = { _id: string }>(): D; | ||
}; | ||
type DocIn<T> = Omit<Partial<T>, '_id'>; | ||
interface IConstructor<T> { | ||
@@ -49,3 +54,3 @@ new(target: Collection.EntityConstructor<T>, api: Api.IInstance): IInstance<T>; | ||
findOneAndDelete(filter: ApiActions.Filter<T>): Promise<Doc<T> | null>; | ||
findByIdAndDelete(id: string): Promise<T | null>; | ||
findByIdAndDelete(id: string): Promise<Doc<T> | null>; | ||
@@ -52,0 +57,0 @@ } |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
25749
557
5
1