├── .github
└── pull_request_template.md
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── assets
├── dangoDB_logo_long_midboi.png
└── dango_deno.png
├── deps.ts
├── lib
├── connections.ts
├── dango.ts
├── datatypes.ts
├── model.ts
├── query.ts
└── schema.ts
├── mod.ts
└── tests
├── test_connections.ts
├── test_datatypes.ts
├── test_query.ts
└── test_schema.ts
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Checklist
2 |
3 | - [ ] Bugfix
4 | - [ ] New feature
5 | - [ ] Refactor
6 |
7 | # Related Issue
8 |
9 | - the problem you are solving goes here.
10 |
11 | # Solution
12 |
13 | - solution to the problem goes here here. Why did you solve this problem the way you did?
14 |
15 | # Additional Info
16 |
17 | - Any additional information or context
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | internal/*
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "deno.enable": true,
3 | "deno.lint": true,
4 | "deno.unstable": true
5 | }
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | dangoDB
6 |
7 | A MongoDB ODM for Deno
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## Table of Contents
15 |
16 | 1. [Description](#description)
17 | 2. [Getting Started](#get-started)
18 | 3. [Query](#query)
19 | 4. [Schema](#schema)
20 | 5. [Authors](#authors)
21 | 6. [License](#license)
22 |
23 | ## Description
24 |
25 | dangoDB is a light-weight MongoDB Object Document Mapper (ODM) library built for the Deno runtime. It provides the core functionality and familiar look and feel of established Node-based libraries. With dangoDB, developers can construct schemas and models, and they can enforce strict type-casting and schema validation to structure their databases. The query functions available from the deno_mongo driver can all be accessed with ease.
26 |
27 | In addition, we built a user-friendly web-based [GUI](https://dangodb.land/#/schema) that auto-generates schema for users to copy and paste directly into their code.
28 |
29 |
30 | ## Getting Started
31 |
32 | First, be sure that you have [Deno](https://deno.land) runtime installed and configured.
33 |
34 | ### Quick Start
35 |
36 | In your application, import the dangoDB module from the deno.land [module](https://deno.land/x/dangodb).
37 |
38 | ```javascript
39 | import { dango } from "https://deno.land/x/dangodb@v1.0.2/mod.ts";
40 | ```
41 |
42 | ### Connect to your Database
43 |
44 | Next, open a connection to your MongoDB database using your URI string.
45 |
46 | ```javascript
47 | await dango.connect('URI_STRING');
48 |
49 | await dango.disconnect();
50 | ```
51 |
52 | ### Define Your Schema
53 |
54 | Now, you can define a schema and create a reference to it as illustrated below.
55 |
56 | ```javascript
57 | const dinosaurSchema = dango.schema(
58 | {
59 | name: // A property accepts the below options. Only type is required and must be a valid selection.
60 | {
61 | type: 'string', // Valid data types listed below.
62 | required: true, // A boolean to indicate if the inserted property must have a value specified. Defaults to false.
63 | unique: true, // A boolean to indicate if the inserted property much be unique in its value in the collection. Defaults to false.
64 | default: 'T-Rex', // A value to default to if none specified and required = false. Defaults to null.
65 | validator: null // A user provided validation function that must return true with the casted value as input for the data to pass schema validation. Defaults to null.
66 | },
67 | age: 'number', // A property can also accept a schema value with only a type indicated.
68 | }
69 | );
70 |
71 | /**
72 | * Valid datatypes:
73 | * - 'number'
74 | * - 'decimal128'
75 | * - 'string'
76 | * - 'boolean'
77 | * - 'objectid'
78 | * - 'uuid'
79 | * - 'date'
80 | * - userSchema // User defined schema.
81 | * - array ** // In progress - Not yet implemented.
82 | *
83 | */
84 |
85 | ```
86 |
87 | ### Create Your Model
88 |
89 | Great! Now you have a schema with one property, name, which will be a 'string.' The next step is compiling our schema into a Model.
90 |
91 | ```javascript
92 | const Dinosaur = dango.model('Dinosaur', dinosaurSchema);
93 | ```
94 |
95 | ### Make a Query
96 |
97 | Now, let's insert a document into the Dinosaur model.
98 |
99 | ```javascript
100 | await Dinosaur.insertOne({ name: 'Stegosaurus' });
101 | ```
102 |
103 | Now, let's say you wanted to display all the dinosaurs in our collection. You can access all of the dinosaur documents through our Dinosaur model.
104 |
105 | ```javascript
106 | const dinosaurs = await Dinosaur.find({ });
107 | console.log(dinosaurs);
108 | // [ { name: 'Triceratops', age: 70,000,000 }, { name: 'Brontosaurus', age: 150,000,000 }, { name: 'Stegosaurus', age: null }];
109 | ```
110 |
111 | Now you've successfully inserted a document into the Dinosaur collection at your MongoDB database.
112 |
113 | Congratulations! That's the end of the quick start. You've successfully imported dangoDB, opened up a connection, created a schema, inserted a document for Stegosaurus, and
114 | queried all the dinosaurs in your Dinosaur model using dangoDB. Explore the rest of the readme.MD for more detailed instructions on how to use dangoDB.
115 |
116 |
117 |
118 | ## Query
119 |
120 | All queries in dangoDB are performed using models. The queries are built on the [deno_mongo](https://deno.land/x/mongo@v0.29.4) drivers. The documentation there may help guide query options. Listed below are all the query functions available in the dangoDB library.
121 |
122 | #### Create, Read, Update, Delete Operations
123 |
124 | - Model.deleteMany()
125 | ```javascript
126 | Model.deleteMany()
127 | /**
128 | * @description Deletes all of the documents that match the conditions from the DB collection. It returns an object with
129 | * the property deletedCount, indicating how many documents were deleted.
130 | * @param queryObject - Query to specify which documents to delete.
131 | * @param options - [optional]
132 | * @param callback - [callback]
133 | * @returns object with property deletedCount, value number.
134 | */
135 | ```
136 |
137 | - Model.deleteOne()
138 | ```javascript
139 | Model.deleteOne()
140 | /**
141 | * @description Deletes the first document that matches the conditions from the DB collection. It returns an object
142 | * with the property deletedCount, indicating how many documents were deleted.
143 | * @param queryObject - The query used to find matching document.
144 | * @param options [optional]
145 | * @param callback [optional]
146 | * @returns object with property deletedCount, value number.
147 | */
148 | ```
149 | - Model.find()
150 | ```javascript
151 | Model.find()
152 | /**
153 | * @ description Returns all documents that satisfy the specified query criteria on the collection or view.
154 | * @param queryObject - The query used to find matching documents.
155 | * @param options - [optional] Additional options for the operation (e.g. lean, populate, projection)
156 | * @param callback - [optional]
157 | * @returns All matching documents in an array.
158 | */
159 | ```
160 |
161 | - Model.findById()
162 | ```javascript
163 | Model.findById()
164 | /**
165 | * @description Returns document that matches user provided ObjectId.
166 | * @param queryObject - The query used to find matching document, using id.
167 | * @param options - [optional] - Additional options for the operation (e.g. lean, populate, projection)
168 | * @param callback - [optional]
169 | * @returns Matching document.
170 | */
171 | ```
172 |
173 | - Model.findByIdAndDelete()
174 | ```javascript
175 | Model.findByIdAndDelete()
176 | /**
177 | * @description Deletes the first document that matches the id from the DB collection. It returns the document
178 | * with the matched property.
179 | * @param queryObject - The query used to find matching document, using id.
180 | * @param options [optional]
181 | * @param callback [optional]
182 | * @returns the deleted document.
183 | */
184 | ```
185 |
186 | - Model.findByIdAndRemove()
187 | ```javascript
188 | Model.findByIdAndRemove()
189 | /**
190 | * @description Deletes the first document that matches the id from the DB collection. It returns the document
191 | * with the matched property.
192 | * @param queryObject - The query used to find matching document, using id.
193 | * @param options [optional]
194 | * @param callback [optional]
195 | * @returns the document matched and removed.
196 | */
197 | ```
198 |
199 | - Model.findByIdAndUpdate()
200 | ```javascript
201 | Model.findByIdAndUpdate()
202 | /**
203 | * @description Updates the first document that matches the id from the DB collection. It returns the document
204 | * with the matched property.
205 | * @param filter - id used to find matching document.
206 | * @param replace - User document to replace matching document at database.
207 | * @param callback [optional]
208 | * @returns the document matched.
209 | */
210 | ```
211 |
212 | - Model.findOne()
213 | ```javascript
214 | Model.findOne()
215 | /**
216 | * @description Returns first document that matches query.
217 | * @param queryObject - Query used to find matching document.
218 | * @param options - [optional]
219 | * @param callback - [optional]
220 | * @returns Matching document.
221 | */
222 | ```
223 |
224 | - Model.findOneAndDelete()
225 | ```javascript
226 | Model.findOneAndDelete()
227 | /**
228 | * @description Deletes the first document that matches the filter from the DB collection. It returns the document
229 | * with the matched property.
230 | * @param queryObject - The query used to find matching document.
231 | * @param options [optional]
232 | * @param callback [optional]
233 | * @returns the deleted document.
234 | */
235 | ```
236 |
237 | - Model.findOneAndRemove()
238 | ```javascript
239 | Model.findOneAndRemove()
240 | /**
241 | * @description Deletes the first document that matches the filter from the DB collection. It returns the document
242 | * with the matched property.
243 | * @param queryObject - The query used to find matching document.
244 | * @param options [optional]
245 | * @param callback [optional]
246 | * @returns the deleted document.
247 | */
248 | ```
249 |
250 | - Model.findOneAndReplace()
251 | ```javascript
252 | Model.findOneAndReplace()
253 | /**
254 | * @description Finds a matching document, removes it, and passes in user's document. Replacement document retains same ObjectId as original document.
255 | * @param filter - Query used to find matching document.
256 | * @param replace - User document to replace matching document at database.
257 | * @param options - [optional]
258 | * @param callback - [optional]
259 | * @returns object displaying count for how many documents were upserted, matching, modified.
260 | */
261 | ```
262 |
263 | - Model.findOneAndUpdate()
264 | ```javascript
265 | Model.findOneAndUpdate()
266 | /**
267 | * @description Updates the first document that matches filter from the DB collection. It returns the document
268 | * with the matched property.
269 | * @param filter - id used to find matching document.
270 | * @param replace - User document to replace matching document at database.
271 | * @param callback [optional]
272 | * @returns the document matched.
273 | */
274 | ```
275 |
276 | - Model.insertOne()
277 | ```javascript
278 | Model.insertOne()
279 | /**
280 | * @description Inserts one document into database collection.
281 | * @param document - User provided object to be inserted into the database.
282 | * @returns ObjectId of inserted document.
283 | */
284 | ```
285 |
286 | - Model.insertMany()
287 | ```javascript
288 | Model.insertMany()
289 | /**
290 | * @description Insert multiple documents into database.
291 | * @param document - Array of document(s) to be inserted into database.
292 | * @param options - [optional]
293 | * @param callback - [optional]
294 | * @returns documents that passed validation.
295 | */
296 | ```
297 |
298 | - Model.replaceOne()
299 | ```javascript
300 | Model.replaceOne()
301 | /**
302 | * @description Finds a matching document, removes it, and passes in user's document. Replacement document retains same ObjectId as original document.
303 | * @param filter - Query used to find matching document.
304 | * @param replace - User document to replace matching document at database.
305 | * @param options - [optional]
306 | * @param callback - [optional]
307 | * @returns The updated object.
308 | */
309 | ```
310 |
311 | - Model.updateMany()
312 | ```javascript
313 | Model.updateMany()
314 | /**Parameters:
315 | * @description Updates all documents that match queryObject. The matching fields in the DB collection will be set to the values in the updateObject.
316 | * @param document - query used to find document(s) to update.
317 | * @param update - object containing field(s) and values to set them to.
318 | * @param options - [optional]
319 | * @param callback - [optional]
320 | * @returns object with properties upsertedId, upsertedCount, matchedCount, modifiedCount.
321 | */
322 | ```
323 |
324 | - Model.updateOne()
325 | ```javascript
326 | Model.updateOne()
327 | /**
328 | * @description Updates one document. The fields in the updateObject will be set to their respective values.
329 | * @param document - query used to find document to update.
330 | * @param update - object containing field(s) and values to set them to.
331 | * @param options - [optional]
332 | * @param callback - [optional]
333 | * @returns object with properties upsertedId, upsertedCount, matchedCount, modifiedCount.
334 | */
335 | ```
336 |
337 | #### Other Operations
338 |
339 | - Model.aggregate()
340 | ```javascript
341 | Model.aggregate()
342 | /**
343 | * @ description Aggregation operations process multiple documents and return computed results. You can use aggregation operations to:
344 | * Group values from multiple documents together.
345 | * Perform operations on the grouped data to return a single result.
346 | * Analyze data changes over time.
347 | * @param Aggregation pipeline as an array of objects.
348 | * @returns Documents returned are plain javascript documents;
349 | */
350 | ```
351 |
352 | - Model.countDocuments()
353 | ```javascript
354 | /**
355 | Model.countDocuments()
356 | * @description Counts number of documents matching filter in a database collection.
357 | * @param document - query used to find document to update.
358 | * @param options - [optional]
359 | * @param callback - [optional]
360 | * @returns a count (number);
361 | * example: query.countDocuments({ username: 'test' });
362 | */
363 | ```
364 |
365 | - Model.dropCollection()
366 | ```javascript
367 | Model.dropCollection()
368 | /**
369 | * @description DropCollection drops current model/collection that user is connected to.
370 | * @returns undefined
371 | */
372 | ```
373 |
374 | - Model.estimatedDocumentCount()
375 | ```javascript
376 | Model.estimatedDocumentCount()
377 | /**
378 | * @description Returns the count of all documents in a collection or view. The method wraps the count command.
379 | * @param options - [optional]
380 | * @param callback - [optional]
381 | * @returns a count (number);
382 | */
383 | ```
384 |
385 | ## Schema
386 |
387 | When creating a schema, either a type can be assigned to each property in string format, or an object with schema options properties.
388 |
389 | #### Schema Options
390 | - type - Number, decimal128, string, boolean, objectid, UUID, date, object. Specified as a lowercase string.
391 | - required - Boolean, specifies whether a value is required in an inserted document or replacement document.
392 | - unique - Boolean, specifies whether a value is designated as unique for the property.
393 | - default - Default value if no value is provided by user. Defaults to null.
394 | - validator - User can provide a function test which the values to be inserted/updated need to pass before being inserted. Defaults to null.
395 |
396 | To set the type for an embedded object, create a schema for that object, and assign that schema to the property that corresponds with the object.
397 |
398 | ```javascript
399 | addressSchema = dango.schema(
400 | {
401 | house_number: 'number',
402 | unit: 'string',
403 | street: 'string',
404 | city: 'string'
405 | }
406 | );
407 |
408 | personSchema = dango.schema(
409 | {
410 | name: 'string',
411 | address: addressSchema
412 | }
413 | );
414 | ```
415 |
416 |
417 | ## Authors
418 |
419 | - [Bill Greco](https://github.com/wgreco13)
420 | - [Steve Jue](https://github.com/kaizenjoo)
421 | - [Celeste Knopf](https://github.com/DHolliday1881)
422 | - [Emilia Yoffie](https://github.com/emiliayoffie)
423 |
424 | ## License
425 |
426 | This product is licensed under the MIT License - see the LICENSE file for details.
427 |
428 | This is an open source product.
429 |
430 | This product is accelerated by OS Labs.
431 |
432 |
--------------------------------------------------------------------------------
/assets/dangoDB_logo_long_midboi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/dangoDB/90528aa435b17243dc1402acc3d247452571df0d/assets/dangoDB_logo_long_midboi.png
--------------------------------------------------------------------------------
/assets/dango_deno.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/dangoDB/90528aa435b17243dc1402acc3d247452571df0d/assets/dango_deno.png
--------------------------------------------------------------------------------
/deps.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description Dependency file imports dependencies for use by local modules.
3 | * To add additional dependencies, add an export statement.
4 | */
5 |
6 | export {
7 | MongoClient,
8 | Database,
9 | Collection,
10 | Bson
11 | } from "https://deno.land/x/mongo@v0.29.4/mod.ts";
12 |
13 | export type {
14 | CountOptions,
15 | InsertOptions,
16 | UpdateOptions,
17 | FindAndModifyOptions,
18 | DropOptions,
19 | AggregateOptions,
20 | FindOptions,
21 | DeleteOptions,
22 | } from 'https://deno.land/x/mongo@v0.29.4/src/types.ts';
23 |
24 | export {
25 | afterAll,
26 | afterEach,
27 | beforeAll,
28 | beforeEach,
29 | describe,
30 | it,
31 | } from 'https://deno.land/std@0.138.0/testing/bdd.ts';
32 |
33 | export {
34 | assert,
35 | assertAlmostEquals,
36 | assertArrayIncludes,
37 | assertEquals,
38 | assertExists,
39 | assertFalse,
40 | assertInstanceOf,
41 | assertIsError,
42 | assertMatch,
43 | assertNotEquals,
44 | assertNotMatch,
45 | assertNotStrictEquals,
46 | assertObjectMatch,
47 | assertRejects,
48 | assertStrictEquals,
49 | assertStringIncludes,
50 | assertThrows,
51 | equal,
52 | fail,
53 | unimplemented,
54 | unreachable,
55 | } from 'https://deno.land/std@0.138.0/testing/asserts.ts';
56 |
57 | import * as dotenv from "https://deno.land/x/dotenv@v3.2.0/mod.ts";
58 | export { dotenv }
59 |
--------------------------------------------------------------------------------
/lib/connections.ts:
--------------------------------------------------------------------------------
1 | import { MongoClient, Database } from '../deps.ts';
2 |
3 | /**
4 | * Counts number of documents matching filter in a database collection.
5 | * @param connectionString A URI string from the user.
6 | * @returns A connection object.
7 | * example: new Connection(''mongodb+srv://example-uri');
8 | */
9 | class Connection {
10 | private client!: MongoClient;
11 | public connected: boolean;
12 | public db: Database | boolean;
13 |
14 | constructor(private connectionString: string) {
15 | if (!connectionString)
16 | throw new Error('Connect method requires at least one argument');
17 | this.connected = false;
18 | this.connectionString = connectionString;
19 | this.db = false;
20 | }
21 |
22 | /**
23 | * Establishes connection to the database.
24 | * Reassigns Connection class properties of connected to true and db to the connected database.
25 | */
26 | public async connect() {
27 | try {
28 | this.client = new MongoClient();
29 | const db = await this.client.connect(this.connectionString);
30 | this.connected = true;
31 | console.log('Connected to Database.');
32 | this.db = db;
33 | return this.db;
34 | } catch (error) {
35 | throw new Error(`Could not connect to database. ${error}`);
36 | }
37 | }
38 |
39 | /**
40 | * Closes connection to the database.
41 | * Reassigns Connection class properties of connected to false and db to false.
42 | */
43 | public disconnect() {
44 | if (this.connected) {
45 | this.client.close();
46 | this.connected = false;
47 | this.db = false;
48 | console.log('Disconnected from Database.');
49 | } else {
50 | throw new Error(`No connection established to disconnect.`);
51 | }
52 | }
53 | }
54 |
55 | export { Connection };
56 |
--------------------------------------------------------------------------------
/lib/dango.ts:
--------------------------------------------------------------------------------
1 | // deno-lint-ignore-file no-explicit-any
2 |
3 | /**
4 | *
5 | * @description This file exports the main dango object.
6 | *
7 | */
8 |
9 | import { Connection } from './connections.ts';
10 | import { model } from './model.ts';
11 | import { Schema } from './schema.ts';
12 | import {
13 | SchemaNumber,
14 | SchemaDecimal128,
15 | SchemaString,
16 | SchemaBoolean,
17 | SchemaObjectId,
18 | SchemaUUID,
19 | SchemaDate,
20 | SchemaObject,
21 | } from './datatypes.ts';
22 |
23 | /**
24 | * Class definition of Dango..
25 | * @returns An object of class SchemaOptions.
26 | */
27 | class Dango {
28 | currentConnection: boolean | Connection;
29 | model: typeof model;
30 | types: Record;
31 |
32 | constructor() {
33 | this.currentConnection = false;
34 | this.model = model;
35 | this.types = {
36 | number: SchemaNumber,
37 | decimal128: SchemaDecimal128,
38 | string: SchemaString,
39 | boolean: SchemaBoolean,
40 | objectid: SchemaObjectId,
41 | UUID: SchemaUUID,
42 | date: SchemaDate,
43 | object: SchemaObject,
44 | };
45 | }
46 |
47 | /**
48 | * Establishes a new connection to a database. Invokes the connect method of a Connection object.
49 | * @param connectionString A database URI
50 | *
51 | * @returns The connection object.
52 | */
53 | async connect(connectionString: string) {
54 | this.currentConnection = new Connection(connectionString);
55 | await this.currentConnection.connect();
56 | return this.currentConnection;
57 | }
58 |
59 | /**
60 | * Disconnects an existing connection to a database. Invokes the disconnect method of a Connection object.
61 | *
62 | * @returns undefined
63 | */
64 | async disconnect() {
65 | if (typeof this.currentConnection === 'boolean') {
66 | if (this.currentConnection === false) {
67 | throw new Error('No database connection exists to disconnect.');
68 | }
69 | } else {
70 | await this.currentConnection.disconnect();
71 | this.currentConnection = false;
72 | return;
73 | }
74 | }
75 |
76 | /**
77 | * Creates a new instance of a Schema object.
78 | * @param schemaObj A user-defined schema.
79 | *
80 | * @returns A schema object
81 | */
82 | schema(schemaObj: Record) {
83 | return new Schema(schemaObj);
84 | }
85 | }
86 |
87 | const dango: Dango = new Dango();
88 |
89 | export { dango };
90 |
--------------------------------------------------------------------------------
/lib/datatypes.ts:
--------------------------------------------------------------------------------
1 | // deno-lint-ignore-file no-explicit-any
2 |
3 | /**
4 | *
5 | * @description This file defines valid data types and associated methods
6 | *
7 | */
8 |
9 | import { Bson } from '../deps.ts';
10 |
11 | /**
12 | * Class definition of Number Schema datatype
13 | * @param value Raw value input from the user.
14 | * @returns An object of class SchemaNumber.
15 | */
16 | export class SchemaNumber {
17 | public value: any;
18 | public valid: boolean | undefined;
19 | public convertedValue: number | Bson.Double | null | undefined;
20 |
21 | constructor(value: any) {
22 | if (value === undefined) {
23 | throw new Error('A value is required.');
24 | }
25 | this.value = value;
26 | this.convertedValue;
27 | this.valid;
28 | }
29 |
30 | /**
31 | * Attempts to convert raw value to the given data type.
32 | * Sets convertedValue property to the casted input if possible, or undefined if not.
33 | */
34 | convertType() {
35 | if (this.value === null) {
36 | this.convertedValue = this.value;
37 | } else if (typeof this.value === 'object') {
38 | if (this.value instanceof Bson.Double) {
39 | this.convertedValue = this.value;
40 | }
41 | } else if (
42 | typeof this.value !== 'number' &&
43 | typeof this.value !== 'string'
44 | ) {
45 | return;
46 | } else if (typeof this.value === 'string') {
47 | const stringNum: number = parseFloat(this.value);
48 | if (isNaN(stringNum)) {
49 | return;
50 | }
51 | const bsonNumber = new Bson.Double(stringNum);
52 | this.convertedValue = bsonNumber;
53 | } else if (typeof this.value === 'number') {
54 | const bsonNumber = new Bson.Double(this.value);
55 | this.convertedValue = bsonNumber;
56 | }
57 |
58 | return this.convertedValue;
59 | }
60 |
61 | /**
62 | * Checks whether the value was casted to the correct data type.
63 | * Sets valid property to true or false.
64 | */
65 | validateType() {
66 | this.valid = this.convertedValue === undefined ? false : true;
67 | return this.valid;
68 | }
69 | }
70 |
71 | /**
72 | * Class definition of Decimal128 Schema datatype
73 | * @param value Raw value input from the user.
74 | * @returns An object of class SchemaDecimal128.
75 | */
76 | export class SchemaDecimal128 {
77 | public value: any;
78 | public valid: boolean | undefined;
79 | public convertedValue: Bson.Decimal128 | null | undefined;
80 |
81 | constructor(value: any) {
82 | if (value === undefined) {
83 | throw new Error('A value is required.');
84 | }
85 | this.value = value;
86 | this.convertedValue;
87 | this.valid;
88 | }
89 |
90 | /**
91 | * Attempts to convert raw value to the given data type.
92 | * Sets convertedValue property to the casted input if possible, or undefined if not.
93 | */
94 | convertType() {
95 | if (this.value === null) {
96 | this.convertedValue = this.value;
97 | } else if (typeof this.value === 'object') {
98 | if (this.value instanceof Bson.Decimal128) {
99 | this.convertedValue = this.value;
100 | }
101 | } else if (
102 | typeof this.value !== 'number' &&
103 | typeof this.value !== 'string'
104 | ) {
105 | return;
106 | } else if (typeof this.value === 'string') {
107 | const stringNum: number = parseFloat(this.value);
108 | if (isNaN(stringNum)) {
109 | return;
110 | }
111 | const decimal128Number = new Bson.Decimal128(this.value);
112 | this.convertedValue = decimal128Number;
113 | } else if (typeof this.value === 'number') {
114 | const decimal128Number = new Bson.Decimal128(this.value.toString());
115 | this.convertedValue = decimal128Number;
116 | }
117 |
118 | return this.convertedValue;
119 | }
120 |
121 | /**
122 | * Checks whether the value was casted to the correct data type.
123 | * Sets valid property to true or false.
124 | */
125 | validateType() {
126 | this.valid = this.convertedValue === undefined ? false : true;
127 | return this.valid;
128 | }
129 | }
130 |
131 | /**
132 | * Class definition of String Schema datatype
133 | * @param value Raw value input from the user.
134 | * @returns An object of class SchemaString.
135 | */
136 | export class SchemaString {
137 | public value: any;
138 | public valid: boolean | undefined;
139 | public convertedValue: string | String | null | undefined;
140 |
141 | constructor(value: any) {
142 | if (value === undefined) {
143 | throw new Error('A value is required.');
144 | }
145 | this.value = value;
146 | this.convertedValue;
147 | this.valid;
148 | }
149 |
150 | /**
151 | * Attempts to convert raw value to the given data type.
152 | * Sets convertedValue property to the casted input if possible, or undefined if not.
153 | */
154 | convertType() {
155 | if (this.value === null) {
156 | this.convertedValue = this.value;
157 | } else if (typeof this.value === 'object') {
158 | if (this.value instanceof String) {
159 | this.convertedValue = this.value;
160 | }
161 | } else if (
162 | typeof this.value !== 'number' &&
163 | typeof this.value !== 'string' &&
164 | typeof this.value !== 'boolean'
165 | ) {
166 | return;
167 | } else if (typeof this.value === 'string') {
168 | this.convertedValue = this.value;
169 | } else if (typeof this.value === 'number') {
170 | this.convertedValue = this.value.toString();
171 | } else if (typeof this.value === 'boolean') {
172 | this.convertedValue = this.value ? 'true' : 'false';
173 | }
174 |
175 | return this.convertedValue;
176 | }
177 |
178 | /**
179 | * Checks whether the value was casted to the correct data type.
180 | * Sets valid property to true or false.
181 | */
182 | validateType() {
183 | this.valid = this.convertedValue === undefined ? false : true;
184 | return this.valid;
185 | }
186 | }
187 |
188 | /**
189 | * Class definition of Boolean Schema datatype
190 | * @param value Raw value input from the user.
191 | * @returns An object of class SchemaBoolean.
192 | */
193 | export class SchemaBoolean {
194 | public value: any;
195 | public valid: boolean | undefined;
196 | public convertedValue: boolean | Boolean | null | undefined;
197 |
198 | constructor(value: any) {
199 | if (value === undefined) {
200 | throw new Error('A value is required.');
201 | }
202 | this.value = value;
203 | this.convertedValue;
204 | this.valid;
205 | }
206 |
207 | /**
208 | * Attempts to convert raw value to the given data type.
209 | * Sets convertedValue property to the casted input if possible, or undefined if not.
210 | */
211 | convertType() {
212 | if (this.value === null) {
213 | this.convertedValue = this.value;
214 | } else if (typeof this.value === 'object') {
215 | if (this.value instanceof Boolean) {
216 | this.convertedValue = this.value;
217 | }
218 | } else if (
219 | typeof this.value !== 'number' &&
220 | typeof this.value !== 'string' &&
221 | typeof this.value !== 'boolean'
222 | ) {
223 | return;
224 | } else if (typeof this.value === 'string') {
225 | if (this.value.toLowerCase() === 'true') this.convertedValue = true;
226 | else if (this.value.toLowerCase() === 'false')
227 | this.convertedValue = false;
228 | } else if (typeof this.value === 'number') {
229 | if (this.value === 1) this.convertedValue = true;
230 | else if (this.value === 0) this.convertedValue = false;
231 | } else if (typeof this.value === 'boolean') {
232 | this.convertedValue = this.value;
233 | }
234 |
235 | return this.convertedValue;
236 | }
237 |
238 | /**
239 | * Checks whether the value was casted to the correct data type..
240 | * Sets valid property to true or false.
241 | */
242 | validateType() {
243 | this.valid = this.convertedValue === undefined ? false : true;
244 | return this.valid;
245 | }
246 | }
247 |
248 | /**
249 | * Class definition of ObjectId Schema datatype
250 | * @param value Raw value input from the user.
251 | * @returns An object of class SchemaObjectId.
252 | */
253 | export class SchemaObjectId {
254 | public value: any;
255 | public valid: boolean | undefined;
256 | public convertedValue: Bson.ObjectId | null | undefined;
257 |
258 | constructor(value: any) {
259 | if (value === undefined) {
260 | throw new Error('A value is required.');
261 | }
262 | this.value = value;
263 | this.convertedValue;
264 | this.valid;
265 | }
266 |
267 | /**
268 | * Attempts to convert raw value to the given data type.
269 | * Sets convertedValue property to the casted input if possible, or undefined if not.
270 | */
271 | convertType() {
272 | if (this.value === null) {
273 | this.convertedValue = this.value;
274 | } else if (typeof this.value === 'object') {
275 | if (this.value instanceof Bson.ObjectId) {
276 | this.convertedValue = this.value;
277 | }
278 | } else if (Bson.ObjectId.isValid(this.value)) {
279 | this.convertedValue = new Bson.ObjectId(this.value);
280 | }
281 |
282 | return this.convertedValue;
283 | }
284 |
285 | /**
286 | * Checks whether the value was casted to the correct data type..
287 | * Sets valid property to true or false.
288 | */
289 | validateType() {
290 | this.valid = this.convertedValue === undefined ? false : true;
291 | return this.valid;
292 | }
293 | }
294 |
295 | /**
296 | * Class definition of UUID Schema datatype
297 | * @param value Raw value input from the user.
298 | * @returns An object of class SchemaUUID.
299 | */
300 | export class SchemaUUID {
301 | public value: any;
302 | public valid: boolean | undefined;
303 | public convertedValue: Bson.UUID | null | undefined;
304 |
305 | constructor(value: any) {
306 | if (value === undefined) {
307 | throw new Error('A value is required.');
308 | }
309 | this.value = value;
310 | this.convertedValue;
311 | this.valid;
312 | }
313 |
314 | /**
315 | * Attempts to convert raw value to the given data type.
316 | * Sets convertedValue property to the casted input if possible, or undefined if not.
317 | */
318 | convertType() {
319 | if (this.value === null) {
320 | this.convertedValue = this.value;
321 | } else if (typeof this.value === 'object') {
322 | if (this.value instanceof Bson.UUID) {
323 | this.convertedValue = this.value;
324 | }
325 | } else if (Bson.UUID.isValid(this.value)) {
326 | this.convertedValue = new Bson.UUID(this.value);
327 | }
328 |
329 | return this.convertedValue;
330 | }
331 |
332 | /**
333 | * Checks whether the value was casted to the correct data type..
334 | * Sets valid property to true or false.
335 | */
336 | validateType() {
337 | this.valid = this.convertedValue === undefined ? false : true;
338 | return this.valid;
339 | }
340 | }
341 |
342 | /**
343 | * Class definition of Date Schema datatype
344 | * @param value Raw value input from the user.
345 | * @returns An object of class SchemaDate.
346 | */
347 | export class SchemaDate {
348 | public value: any;
349 | public valid: boolean | undefined;
350 | public convertedValue: Date | null | undefined;
351 |
352 | constructor(value: any) {
353 | if (value === undefined) {
354 | throw new Error('A value is required.');
355 | }
356 | this.value = value;
357 | this.convertedValue;
358 | this.valid;
359 | }
360 |
361 | /**
362 | * Attempts to convert raw value to the given data type.
363 | * Sets convertedValue property to the casted input if possible, or undefined if not.
364 | */
365 | convertType() {
366 | if (this.value === null) {
367 | this.convertedValue = this.value;
368 | } else if (typeof this.value === 'object') {
369 | if (this.value instanceof Date) {
370 | this.convertedValue = this.value;
371 | }
372 | } else if (
373 | typeof this.value !== 'number' &&
374 | typeof this.value !== 'string'
375 | ) {
376 | return;
377 | } else if (typeof this.value === 'string') {
378 | const convertedDate: number | Date = Date.parse(this.value);
379 | if (typeof convertedDate === 'number') {
380 | if (isNaN(convertedDate)) {
381 | return;
382 | } else {
383 | this.convertedValue = new Date(convertedDate);
384 | }
385 | }
386 | } else if (typeof this.value === 'number') {
387 | this.convertedValue = new Date(this.value);
388 | }
389 |
390 | return this.convertedValue;
391 | }
392 |
393 | /**
394 | * Checks whether the value was casted to the correct data type..
395 | * Sets valid property to true or false.
396 | */
397 | validateType() {
398 | this.valid = this.convertedValue === undefined ? false : true;
399 | return this.valid;
400 | }
401 | }
402 |
403 | // SCHEMA OBJ
404 |
405 | export class SchemaObject {
406 | public value: any;
407 | public valid: boolean | undefined;
408 | public convertedValue: Record | null | undefined;
409 |
410 | constructor(value: any) {
411 | if (value === undefined) {
412 | throw new Error('A value is required.');
413 | }
414 | this.value = value;
415 | this.convertedValue;
416 | this.valid;
417 | }
418 |
419 | /**
420 | * Attempts to convert raw value to the given data type.
421 | * Sets convertedValue property to the casted input if possible, or undefined if not.
422 | */
423 | convertType() {
424 | if (this.value === null) {
425 | this.convertedValue = this.value;
426 | } else if (typeof this.value === 'object') {
427 | this.convertedValue = this.value;
428 | // }
429 | } else if (typeof this.value !== 'object') {
430 | return;
431 | }
432 |
433 | return this.convertedValue;
434 | }
435 |
436 | /**
437 | * Checks whether the value was casted to the correct data type..
438 | * Sets valid property to true or false.
439 | */
440 | validateType() {
441 | this.valid = this.convertedValue === undefined ? false : true;
442 | return this.valid;
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/lib/model.ts:
--------------------------------------------------------------------------------
1 | // deno-lint-ignore-file no-explicit-any
2 |
3 | import { Query } from './query.ts';
4 | import { Schema } from './schema.ts';
5 |
6 | /**
7 | * @description Exports a function model that returns a new Model object.
8 | * @param collectionName The collection name to apply the schema to
9 | * @param schema The created instance of the Schema class
10 | *
11 | * @returns A new model object
12 | */
13 | export function model(collectionName: string, schema: Schema) {
14 | return new Model(collectionName, schema);
15 | }
16 |
17 | /**
18 | * Class definition of a Model.
19 | * Extends the Query class.
20 | * @param collectionName The collection name to apply the schema to
21 | * @param schema The created instance of the Schema class
22 | *
23 | * @returns An object of class SchemaOptions.
24 | */
25 | class Model extends Query {
26 | collectionName: string;
27 | schema: Schema;
28 |
29 | constructor(collectionName: string, schema: Schema) {
30 | super(collectionName, schema);
31 | //TODO: Can we delete these
32 | this.collectionName = collectionName;
33 | this.schema = schema;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/query.ts:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @description This file defines the query class and its methods.
4 | *
5 | */
6 | import { Connection } from './connections.ts';
7 | import { Bson } from '../deps.ts';
8 | import {
9 | CountOptions,
10 | InsertOptions,
11 | UpdateOptions,
12 | FindAndModifyOptions,
13 | DropOptions,
14 | AggregateOptions,
15 | FindOptions,
16 | DeleteOptions,
17 | } from '../deps.ts';
18 | import { dango } from './dango.ts';
19 | import { Schema, optionsObject } from './schema.ts';
20 |
21 | interface MatchInterface {
22 | $match: { [unknownKeyName: string]: string };
23 | }
24 |
25 | interface GroupInterface {
26 | $group: {
27 | [unknownKeyName: string]: string | { $sum: number };
28 | };
29 | }
30 |
31 | class Query {
32 | public collectionName: string;
33 | public connection: Connection | boolean;
34 | public schema: Schema;
35 | public updatedQueryObject: { [key: string]: unknown };
36 |
37 | constructor(collectionName: string, schema: Schema) {
38 | this.collectionName = collectionName;
39 | this.connection = dango.currentConnection;
40 | this.schema = schema;
41 | this.updatedQueryObject = {};
42 | }
43 | /**
44 | * Returns one document that satisfies the specified query criteria on the collection or view.
45 | *
46 | * @param query - Selects documents in a collection or view and returns a cursor to the selected documents.
47 | * @param options Additional options for the operation (e.g. lean, populate, projection)
48 | * @param callback
49 | * @returns A count of the documents in the database.
50 | * example: query.find();
51 | */
52 | public async find(
53 | allQueryObjects?: Record,
54 | options?: FindOptions,
55 | callback?: (input: unknown) => unknown
56 | ) {
57 | try {
58 | if (
59 | typeof this.connection === 'boolean' ||
60 | typeof this.connection.db === 'boolean'
61 | ) {
62 | if (this.connection === false) {
63 | throw new Error('No connection established before query.');
64 | }
65 | } else {
66 | const collection = this.connection.db.collection(this.collectionName);
67 | const data = await collection.find(allQueryObjects, options);
68 | const dataRes = await data.toArray();
69 |
70 | if (callback) return callback(data);
71 |
72 | return dataRes;
73 | }
74 | } catch (error) {
75 | throw new Error(`Error in find function. ${error}`);
76 | }
77 | }
78 | /**
79 | * Returns one document that satisfies the specified query criteria on the collection or view.
80 | *
81 | * @param query - The query used to match documents
82 | * @param options Additional options for the operation (e.g. lean, populate, projection)
83 | * @param callback
84 | * @returns The document matched and modified
85 | * example: query.findOne({ username: 'newtest2' });
86 | */
87 |
88 | public async findOne(
89 | queryObject: Record,
90 | options?: FindOptions,
91 | callback?: (input: unknown) => unknown
92 | ) {
93 | try {
94 | if (
95 | typeof this.connection === 'boolean' ||
96 | typeof this.connection.db === 'boolean'
97 | ) {
98 | if (this.connection === false) {
99 | throw new Error('No connection established before query.');
100 | }
101 | } else {
102 | const collection = this.connection.db.collection(this.collectionName);
103 | const data = await collection.findOne(queryObject, options);
104 |
105 | if (callback) return callback(data);
106 |
107 | return data;
108 | }
109 | } catch (error) {
110 | throw new Error(`Error in findOne function. ${error}`);
111 | }
112 | }
113 | /**
114 | Counts number of documents matching filter in a database collection.
115 | * @param Filter.
116 | * @param Additional options for the operation.
117 | * @param Optional callback such as (err, count);
118 | * @returns a count (number);
119 | * example: query.countDocuments({ username: 'test' });
120 | */
121 | public async countDocuments(
122 | queryObject: Record,
123 | callback?: (input: unknown) => unknown
124 | ) {
125 | try {
126 | if (
127 | typeof this.connection === 'boolean' ||
128 | typeof this.connection.db === 'boolean'
129 | ) {
130 | if (this.connection === false) {
131 | throw new Error('No connection established before query.');
132 | }
133 | } else {
134 | const collection = this.connection.db.collection(this.collectionName);
135 | const data = await collection.countDocuments(queryObject);
136 |
137 | if (callback) return callback(data);
138 | console.log(data);
139 |
140 | return data;
141 | }
142 | } catch (error) {
143 | throw new Error(`Error in countDocuments function. ${error}`);
144 | }
145 | }
146 | /**
147 | Returns the count of all documents in a collection or view. The method wraps the count command.
148 | * @param Additional options for the operation.
149 | * @param Optional callback such as (err, count);
150 | * @returns a count (number);
151 | * example: query.estimatedDocumentCount();
152 | */
153 | public async estimatedDocumentCount() {
154 | try {
155 | if (
156 | typeof this.connection === 'boolean' ||
157 | typeof this.connection.db === 'boolean'
158 | ) {
159 | if (this.connection === false) {
160 | throw new Error('No connection established before query.');
161 | }
162 | } else {
163 | const collection = this.connection.db.collection(this.collectionName);
164 | const data = await collection.estimatedDocumentCount();
165 |
166 | return data;
167 | }
168 | } catch (error) {
169 | throw new Error(`Error in estimatedDocumentCount function. ${error}`);
170 | }
171 | }
172 | /**
173 | Aggregation operations process multiple documents and return computed results. You can use aggregation operations to:
174 | Group values from multiple documents together.
175 | Perform operations on the grouped data to return a single result.
176 | Analyze data changes over time.
177 | * @param Aggregation pipeline as an array of objects.
178 | * @returns Documents returned are plain javascript documents;
179 | * example: query.aggregate([
180 | { $match: { username: 'test' } },
181 | { $group: { _id: '$username', total: { $sum: 1 } } },
182 | ]);
183 | */
184 | public async aggregate(arg1: [MatchInterface, GroupInterface]) {
185 | try {
186 | if (
187 | typeof this.connection === 'boolean' ||
188 | typeof this.connection.db === 'boolean'
189 | ) {
190 | if (this.connection === false) {
191 | throw new Error('No connection established before query.');
192 | }
193 | } else {
194 | const collection = this.connection.db.collection(this.collectionName);
195 | const data = await collection.aggregate(arg1);
196 | const dataRes = await data.toArray();
197 |
198 | return dataRes;
199 | }
200 | } catch (error) {
201 | throw new Error(`Error in aggregate function. ${error}`);
202 | }
203 | }
204 | /**
205 | * Find and modify a document in one, returning the matching document.
206 | *
207 | * @param The query used to match documents.
208 | * @param Additional options for the operation (e.g. sort, limit, skip)
209 | * @returns The document matched and modified
210 | * example:query.findAndModify({ username: 'emilia' },
211 | {
212 | sort: { _id: 1 },
213 | update: { $inc: { newField: +2 } },
214 | new: true,
215 | });
216 | */
217 | public async findAndModify(
218 | filter: Record,
219 | options?: FindAndModifyOptions
220 | ) {
221 | try {
222 | if (
223 | typeof this.connection === 'boolean' ||
224 | typeof this.connection.db === 'boolean'
225 | ) {
226 | if (this.connection === false) {
227 | throw new Error('No connection established before query.');
228 | }
229 | } else {
230 | const collection = this.connection.db.collection(this.collectionName);
231 | const data = await collection.findAndModify(filter, options);
232 |
233 | console.log('findByIdAndModify Successful', data);
234 | return data;
235 | }
236 | } catch (error) {
237 | throw new Error(`Error in findandModify function. ${error}`);
238 | }
239 | }
240 | /**
241 | * Issue a MongoDB findOneAndDelete() command by a document's _id field.
242 | *
243 | * @param The id used to match documents.
244 | * @param Callback function.
245 | * @returns The document matched and deleted.
246 | * example: query.findByIdAndDelete("62642ee21bcc7078ae1dba3d")
247 | */
248 | public async findByIdAndDelete(
249 | id: string,
250 | options?: FindOptions | DeleteOptions | ((input: unknown) => unknown),
251 | callback?: (input: unknown) => unknown
252 | ) {
253 | try {
254 | const stringId = new Bson.ObjectId(id);
255 | if (
256 | typeof this.connection === 'boolean' ||
257 | typeof this.connection.db === 'boolean'
258 | ) {
259 | if (this.connection === false) {
260 | throw new Error('No connection established before query.');
261 | }
262 | } else {
263 | const collection = this.connection.db.collection(this.collectionName);
264 |
265 | if (typeof options === 'function') {
266 | callback = options;
267 | options = {};
268 | }
269 |
270 | const data = await collection.deleteOne({ _id: stringId }, options);
271 | console.log('findByIdAndDelete Successful', data);
272 |
273 | if (callback) {
274 | return callback(data);
275 | } else {
276 | return data;
277 | }
278 | }
279 | } catch (error) {
280 | throw new Error(`Error in findByIdAndDelete function. ${error}`);
281 | }
282 | }
283 | /**
284 | * Issue a mongodb findAndModify remove command. Finds a matching document, removes it, passing the found document (if any) to the callback. Executes the query if callback is passed.
285 | *
286 | * @param Conditions.
287 | * @param Additional options for the operation.
288 | * @param Callback function.
289 | * @returns The document matched and removed.
290 | * example: query.findOneAndRemove({username: "Bob"}, (input) => {console.log('callback executed', input)});
291 | */
292 | public async findOneAndRemove(
293 | queryObject: Record,
294 | callback?: (input: unknown) => unknown
295 | ) {
296 | try {
297 | if (
298 | typeof this.connection === 'boolean' ||
299 | typeof this.connection.db === 'boolean'
300 | ) {
301 | if (this.connection === false) {
302 | throw new Error('No connection established before query.');
303 | }
304 | } else {
305 | const collection = this.connection.db.collection(this.collectionName);
306 | const data = await collection.findAndModify(queryObject, {
307 | remove: true,
308 | });
309 |
310 | console.log('findOneAndRemove Successful', data);
311 |
312 | if (callback) {
313 | return callback(data);
314 | } else {
315 | return data;
316 | }
317 | }
318 | } catch (error) {
319 | throw new Error(`Error in findOneAndRemove function. ${error}`);
320 | }
321 | }
322 | /**
323 | * Issue a mongodb findAndModify remove command by a document's _id field.
324 | *
325 | * @param The id used to match documents.
326 | * @param Additional options for the operation.
327 | * @param Callback function.
328 | * @returns The document matched and removed.
329 | * example: query.findByIdAndRemove("626d8508c522d90bacb1c843", (input) => {console.log('callback executed', input)});
330 | */
331 | public async findByIdAndRemove(
332 | id?: string,
333 | callback?: (input: unknown) => unknown
334 | ) {
335 | try {
336 | const stringId = new Bson.ObjectId(id);
337 |
338 | if (
339 | typeof this.connection === 'boolean' ||
340 | typeof this.connection.db === 'boolean'
341 | ) {
342 | if (this.connection === false) {
343 | throw new Error('No connection established before query.');
344 | }
345 | } else {
346 | const collection = this.connection.db.collection(this.collectionName);
347 | const data = await collection.findAndModify(
348 | { _id: stringId },
349 | { remove: true }
350 | );
351 |
352 | console.log('findByIdAndRemove Successful', data);
353 |
354 | if (callback) return callback(data);
355 |
356 | return data;
357 | }
358 | } catch (error) {
359 | throw new Error(`Error in findByIdAndRemove function. ${error}`);
360 | }
361 | }
362 | /**
363 | * ReplaceOne replaces the filters existing document with the input doc - same as update() in the mongoDB Driver. Returns a query.
364 | *
365 | * @param Filter.
366 | * @param Document which is the client input for what will replace the filter.
367 | * @param Additional options for the operation.
368 | * @param Callback function.
369 | * @returns The updated document.
370 | * example: await query.replaceOne("626d8508c522d90bacb1c843", (input) => {console.log('callback executed', input)});
371 | */
372 | public async replaceOne(
373 | filter: Record,
374 | document: Record,
375 | options?: UpdateOptions | ((input: unknown) => unknown),
376 | callback?: (input: unknown) => unknown
377 | ) {
378 | try {
379 | if (
380 | typeof this.connection === 'boolean' ||
381 | typeof this.connection.db === 'boolean'
382 | ) {
383 | if (this.connection === false) {
384 | throw new Error('No connection established before query.');
385 | }
386 | } else {
387 | const collection = this.connection.db.collection(this.collectionName);
388 | if (typeof options === 'function') callback = options;
389 | options = {};
390 |
391 | const data = await collection.replaceOne(filter, document, options);
392 |
393 | console.log('Successfully executed replaceOne', data);
394 |
395 | if (callback) return callback(data);
396 |
397 | return data;
398 | }
399 | } catch (error) {
400 | throw new Error(`Error in replaceOne function. ${error}`);
401 | }
402 | }
403 | /**
404 | * Insert One takes one parameter, a document, which inserts a document into the database
405 | *
406 | * @param Document which is the client input for what will replace the filter.
407 | * @param WriteConcern. Optional. A document that expresses the write concern of the insert command. Omit to use the default write concern.
408 | * Do not explicitly set the write concern for the operation if run in a transaction. To use write concern with transactions, see Transactions and Write Concern.
409 | * @returns The inserted document.
410 | * example: await query.insertOne({username: 'Celeste'});
411 | */
412 | public async insertOne(
413 | document: Record,
414 | writeConcern?: InsertOptions
415 | ) {
416 | try {
417 | if (
418 | typeof this.connection === 'boolean' ||
419 | typeof this.connection.db === 'boolean'
420 | ) {
421 | if (this.connection === false) {
422 | throw new Error('No connection established before query.');
423 | }
424 | } else {
425 | const collection = this.connection.db.collection(this.collectionName);
426 | await this.validateInsertAgainstSchema(
427 | document,
428 | this.schema,
429 | this.updatedQueryObject
430 | );
431 | const id = await collection.insertOne(
432 | this.updatedQueryObject,
433 | writeConcern
434 | );
435 | this.resetQueryObject();
436 | console.log('Successfully insertedOne');
437 | return id;
438 | }
439 | } catch (error) {
440 | throw new Error(`Error in insertOne function. ${error}`);
441 | }
442 | }
443 |
444 | /**
445 | * Insert Many inserts an array or object into the database.
446 | *
447 | * @param Document which is the client input for what will replace the filter.
448 | * @param options Optional for different inderting options
449 | * @param callback function
450 | * @returns a promise resolving to the raw result from the MongoDB driver if `options.rawResult` was `true`, or the documents that passed validation, otherwise
451 | * example: await query.insertMany([ { username: 'anotherOne'}, { username: 'Tulips' }], (input) => {console.log('callback executed', input)});
452 | */
453 | public async insertMany(
454 | document: Record[],
455 | options?: InsertOptions | ((input: unknown) => unknown),
456 | callback?: (input: unknown) => unknown
457 | ) {
458 | try {
459 | if (
460 | typeof this.connection === 'boolean' ||
461 | typeof this.connection.db === 'boolean'
462 | ) {
463 | if (this.connection === false) {
464 | throw new Error('No connection established before query.');
465 | }
466 | } else {
467 | const collection = this.connection.db.collection(this.collectionName);
468 |
469 | if (typeof options === 'function') callback = options;
470 | options = {};
471 |
472 | const validatedDocuments = [];
473 | for (const doc of document) {
474 | await this.validateInsertAgainstSchema(
475 | doc,
476 | this.schema,
477 | this.updatedQueryObject
478 | );
479 | validatedDocuments.push(this.updatedQueryObject);
480 | this.resetQueryObject();
481 | }
482 | const ids = await collection.insertMany(validatedDocuments, options);
483 | if (callback) return callback(ids);
484 |
485 | return ids;
486 | }
487 | } catch (error) {
488 | throw new Error(`Error in insertMany function. ${error}`);
489 | }
490 | }
491 |
492 | /**
493 | * Find One And Update finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callbacks.
494 | *
495 | * @param Filter which is the client input for the document to find
496 | * @param Update which is the clients input for what portion will be updated within the document
497 | * @param Additonal options UpdateOptions
498 | * @param callback function
499 | * @returns the found document
500 | * example: await query.findByIdAndUpdate('626aa9c8b1d75dd60462cf15', { username: "omgThisWorksAgain"}, (input) => {console.log('callback executed', input)})
501 | */
502 | public async findOneAndUpdate(
503 | filter: Record,
504 | update: Record,
505 | options?: UpdateOptions | ((input: unknown) => unknown),
506 | callback?: (input: unknown) => unknown
507 | ) {
508 | try {
509 | if (
510 | typeof this.connection === 'boolean' ||
511 | typeof this.connection.db === 'boolean'
512 | ) {
513 | if (this.connection === false) {
514 | throw new Error('No connection established before query.');
515 | }
516 | } else {
517 | const collection = this.connection.db.collection(this.collectionName);
518 | await this.validateUpdateAgainstSchema(
519 | update,
520 | this.schema,
521 | this.updatedQueryObject
522 | );
523 | const newUpdate = { $set: this.updatedQueryObject };
524 | if (typeof options === 'function') callback = options;
525 | options = {};
526 | const data = await collection.updateOne(filter, newUpdate, options);
527 | this.resetQueryObject();
528 | if (callback) return callback(data);
529 |
530 | return data;
531 | }
532 | } catch (error) {
533 | throw new Error(`Error in findOneAndUpdate function. ${error}`);
534 | }
535 | }
536 |
537 | /**
538 | * Find One And Replace finds a matching document, removes the contents of the document, and passes the input document (if any) into the document as a replacement.
539 | * @param Filter which is the client input for which document will be found
540 | * @param Replacement is the client input for which document will be replacing the filter
541 | * @param Additional options
542 | * @param Callback function
543 | * @returns
544 | * example: await query.findOneAndReplace({ username: "OneandUpdating" }, { username: "Iron_Man"}, (input) => {console.log('callback executed', input)})
545 | */
546 | public async findOneAndReplace(
547 | filter: Record,
548 | replacement: Record,
549 | options?: FindAndModifyOptions | ((input: unknown) => unknown),
550 | callback?: (input: unknown) => unknown
551 | ) {
552 | try {
553 | if (
554 | typeof this.connection === 'boolean' ||
555 | typeof this.connection.db === 'boolean'
556 | ) {
557 | if (this.connection === false) {
558 | throw new Error('No connection established before query.');
559 | }
560 | } else {
561 | const collection = this.connection.db.collection(this.collectionName);
562 | if (typeof options === 'function') callback = options;
563 | options = {};
564 | await this.validateReplaceAgainstSchema(
565 | filter,
566 | replacement,
567 | this.schema,
568 | this.updatedQueryObject
569 | );
570 | const data = await collection.replaceOne(
571 | filter,
572 | this.updatedQueryObject,
573 | options
574 | );
575 | this.resetQueryObject();
576 | if (callback) return callback(data);
577 |
578 | return data;
579 | }
580 | } catch (error) {
581 | throw new Error(`Error in findOneAndReplace function. ${error}`);
582 | }
583 | }
584 |
585 | /**
586 | * Find By Id finds a single document by its _id field
587 | * @param id which is the client input for which document will be found
588 | * @param Additional options such as projection, sort etc
589 | * @param callback function
590 | * @returns the document
591 | * example: await query.findById('626aaa96500d65b1228e6940');
592 | */
593 | public async findById(
594 | id: string,
595 | options?: FindOptions | ((input: unknown) => unknown),
596 | callback?: (input: unknown) => unknown
597 | ) {
598 | try {
599 | const stringId = new Bson.ObjectId(id);
600 |
601 | if (
602 | typeof this.connection === 'boolean' ||
603 | typeof this.connection.db === 'boolean'
604 | ) {
605 | if (this.connection === false) {
606 | throw new Error('No connection established before query.');
607 | }
608 | } else {
609 | const collection = this.connection.db.collection(this.collectionName);
610 |
611 | if (typeof options === 'function') callback = options;
612 | options = {};
613 |
614 | const data = await collection.findOne({ _id: stringId }, options);
615 | if (callback) {
616 | return callback(data);
617 | } else {
618 | return data;
619 | }
620 | }
621 | } catch (error) {
622 | throw new Error(`Error in findById function. ${error}`);
623 | }
624 | }
625 |
626 | /**
627 | * Find By Id and Update finds a matching document by _id, updates it according to the update arg, passing any options, and returns the found document (if any) to the callbacks.
628 | * @param id which is the client input for which document will be found
629 | * @param update which is the clients input for what portion will be updated within the document
630 | * @param additional options updateoptions
631 | * @param callback function
632 | * @returns the document
633 | * example: await query.findByIdAndUpdate(626aa9c8b1d75dd60462cf15', { username: "omgThisWorksAgain"}, (input) => {console.log('callback executed', input)});
634 | */
635 | public async findByIdAndUpdate(
636 | id: string,
637 | update: Record,
638 | options?: UpdateOptions | ((input: unknown) => unknown),
639 | callback?: (input: unknown) => unknown
640 | ) {
641 | try {
642 | const filter = { _id: new Bson.ObjectId(id) };
643 | console.log(filter);
644 |
645 | if (
646 | typeof this.connection === 'boolean' ||
647 | typeof this.connection.db === 'boolean'
648 | ) {
649 | if (this.connection === false) {
650 | throw new Error('No connection established before query.');
651 | }
652 | } else {
653 | const collection = this.connection.db.collection(this.collectionName);
654 |
655 | await this.validateUpdateAgainstSchema(
656 | update,
657 | this.schema,
658 | this.updatedQueryObject
659 | );
660 | const newUpdate = { $set: this.updatedQueryObject };
661 |
662 | if (typeof options === 'function') callback = options;
663 | options = {};
664 |
665 | const data = await collection.updateOne(filter, newUpdate, options);
666 | this.resetQueryObject();
667 | if (callback) {
668 | return callback(data);
669 | } else {
670 | return data;
671 | }
672 | }
673 | } catch (error) {
674 | throw new Error(`Error in findByIdAndUpdate function. ${error}`);
675 | }
676 | }
677 |
678 | /**
679 | * DropCollection drops current model/collection that user is connected to.
680 | * @returns undefined
681 | * example: Model.dropCollection()
682 | */
683 | public async dropCollection() {
684 | try {
685 | if (
686 | typeof this.connection === 'boolean' ||
687 | typeof this.connection.db === 'boolean'
688 | ) {
689 | if (this.connection === false) {
690 | throw new Error('No connection established before query.');
691 | }
692 | } else {
693 | const collection = this.connection.db.collection(this.collectionName);
694 | const data = await collection.drop();
695 |
696 | console.log('Collection successfully dropped.');
697 |
698 | return data;
699 | }
700 | } catch (error) {
701 | throw new Error(`Error in dropCollection function. ${error}`);
702 | }
703 | }
704 |
705 | /**
706 | * Delete One deletes the first document that matches conditions from the collection.
707 | * @param document which is the client input for which document will be found
708 | * @param additional options deleteOptions
709 | * @param callback function
710 | * @returns an object with the property deletedCount indicating how many documents were deleted.
711 | * example: await query.deleteOne({ username: "test"}, (input) => {console.log('callback executed', input)})
712 | */
713 | public async deleteOne(
714 | document: Record,
715 | options?: DeleteOptions | ((input: unknown) => unknown),
716 | callback?: (input: unknown) => unknown
717 | ) {
718 | try {
719 | if (typeof options === 'function') {
720 | callback = options;
721 | options = {};
722 | }
723 | if (
724 | typeof this.connection === 'boolean' ||
725 | typeof this.connection.db === 'boolean'
726 | ) {
727 | if (this.connection === false) {
728 | throw new Error('No connection established before query.');
729 | }
730 | } else {
731 | const collection = this.connection.db.collection(this.collectionName);
732 |
733 | const data = await collection.deleteOne(document, options);
734 | if (callback) {
735 | return callback(data);
736 | } else {
737 | const formattedReturnObj = { deletedCount: data };
738 |
739 | return formattedReturnObj;
740 | }
741 | }
742 | } catch (error) {
743 | throw new Error(`Error in deleteOne function. ${error}`);
744 | }
745 | }
746 |
747 | /**
748 | * Delete Many deletes all of the documents that match conditions from the collection
749 | * @param document which is the client input for which document will be found
750 | * @param additional options deleteOptions
751 | * @param callback function
752 | * @returns an object with the property deletedCount containing the number of documents deleted
753 | * example: await query.deleteMany({ username: 'newtest1' }, { limit: 1 } ,(data) => { console.log(data); });
754 | */
755 | public async deleteMany(
756 | document: Record,
757 | options?: DeleteOptions | ((input: unknown) => unknown),
758 | callback?: (input: unknown) => unknown
759 | ) {
760 | try {
761 | if (
762 | typeof this.connection === 'boolean' ||
763 | typeof this.connection.db === 'boolean'
764 | ) {
765 | if (this.connection === false) {
766 | throw new Error('No connection established before query.');
767 | }
768 | } else {
769 | const collection = this.connection.db.collection(this.collectionName);
770 | if (typeof options === 'function') {
771 | callback = options;
772 | options = {};
773 | }
774 |
775 | const data = await collection.deleteMany(document, options);
776 | const formattedReturnObj = { deletedCount: data };
777 | console.log(formattedReturnObj);
778 | if (callback) return callback(data);
779 |
780 | return formattedReturnObj;
781 |
782 | }
783 | } catch (error) {
784 | throw new Error(`Error in deleteMany function. ${error}`);
785 | }
786 | }
787 | /**
788 | * Update One finds a matching document, updates it according to the update arg, passing any options. Will update only the first document that matches filter regardless of the value of the multi option.
789 | *
790 | * @param document which is the client input for the document to find
791 | * @param Update which is the clients input for what portion will be updated within the document
792 | * @param Additonal options UpdateOptions
793 | * @param callback function params are (error, writeOpResult)
794 | * @returns the found document
795 | * example: await query.updateOne({ username: 'rob ott'}, { username: 'ROBO OTT' });
796 | */
797 | public async updateOne(
798 | document: Record,
799 | update: Record,
800 | options?: UpdateOptions | ((input: unknown) => unknown),
801 | callback?: (input: unknown) => unknown
802 | ) {
803 | try {
804 | if (
805 | typeof this.connection === 'boolean' ||
806 | typeof this.connection.db === 'boolean'
807 | ) {
808 | if (this.connection === false) {
809 | throw new Error('No connection established before query.');
810 | }
811 | } else {
812 | const collection = this.connection.db.collection(this.collectionName);
813 | if (typeof options === 'function') {
814 | callback = options;
815 | options = {};
816 | }
817 |
818 | await this.validateUpdateAgainstSchema(
819 | update,
820 | this.schema,
821 | this.updatedQueryObject
822 | );
823 | const setUpdateObject = { $set: this.updatedQueryObject };
824 |
825 | const data = await collection.updateOne(
826 | document,
827 | setUpdateObject,
828 | options
829 | );
830 | this.resetQueryObject();
831 |
832 | if (callback) return callback(data);
833 |
834 | return data;
835 | }
836 | } catch (error) {
837 | throw new Error(`Error in updateOne function. ${error}`);
838 | }
839 | }
840 |
841 | /**
842 | * Update Many updates many documents matching search criteria in the database.
843 | *
844 | * @param document which is the client input for the document to find
845 | * @param update which is the clients input for what portion will be updated within the document
846 | * @param options options UpdateOptions
847 | * @param callback function
848 | * @returns the found document
849 | * example: await query.updateMany({ name: 'Mireille' }, { favoriteFood: 'pizza' }, (input) => {console.log('callback executed', input)})
850 | */
851 | public async updateMany(
852 | document: Record,
853 | update: Record,
854 | options?: UpdateOptions | ((input: unknown) => unknown),
855 | callback?: (input: unknown) => unknown
856 | ) {
857 | // if upsert is true, and no matching documents are found, updateObject( regardless of how complete it is) will be inserted.
858 | try {
859 | if (
860 | typeof this.connection === 'boolean' ||
861 | typeof this.connection.db === 'boolean'
862 | ) {
863 | if (this.connection === false) {
864 | throw new Error('No connection established before query.');
865 | }
866 | } else {
867 | const collection = this.connection.db.collection(this.collectionName);
868 | if (typeof options === 'function') {
869 | callback = options;
870 | options = {};
871 | }
872 |
873 | await this.validateUpdateAgainstSchema(
874 | update,
875 | this.schema,
876 | this.updatedQueryObject
877 | );
878 | const setUpdateObject = { $set: this.updatedQueryObject };
879 | const data = await collection.updateMany(
880 | document,
881 | setUpdateObject,
882 | options
883 | );
884 | this.resetQueryObject();
885 |
886 | if (callback) return callback(data);
887 |
888 | return data;
889 | }
890 | } catch (error) {
891 | throw new Error(`Error in updateMany function. ${error}`);
892 | }
893 | }
894 |
895 | /**
896 | * Method validates schema for insert queries by calling each schema option. Validation steps will throw an error if validation fails.
897 | *
898 | * @param queryObject which is the client document to insert into the database
899 | * @param schema which is the current schema, either at the outer level of a document or is an embedded schema
900 | * @param updatedQueryObject which is the converted version of the user's queryObject with properly coverted types for each value
901 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option 'unique', set to true, this array will
902 | * contain property keys from outer levels
903 | * @returns true or undefined.
904 | */
905 | async validateInsertAgainstSchema(
906 | queryObject: Record,
907 | schema: Schema,
908 | updatedQueryObject: Record,
909 | embeddedUniqueProperty: string[] = []
910 | ) {
911 | const currentSchemaMap = schema.schemaMap;
912 |
913 | this.checkDataFields(queryObject, currentSchemaMap);
914 | for (const property in currentSchemaMap) {
915 | // current SchemaMap's current property value is either an instance of a Schema or a SchemaOption
916 | // If Schema is stored, validate embedded object.
917 | if (currentSchemaMap[property] instanceof Schema) {
918 | updatedQueryObject[property] = {};
919 | embeddedUniqueProperty.push(property);
920 | await this.validateInsertAgainstSchema(
921 | queryObject[property] as Record,
922 | currentSchemaMap[property] as Schema,
923 | updatedQueryObject[property] as Record,
924 | embeddedUniqueProperty
925 | );
926 | embeddedUniqueProperty.pop();
927 | } else {
928 | this.checkRequired(
929 | queryObject,
930 | property,
931 | currentSchemaMap[property] as optionsObject
932 | );
933 | this.setDefault(
934 | queryObject,
935 | property,
936 | currentSchemaMap[property] as optionsObject
937 | );
938 | this.populateQuery(
939 | queryObject,
940 | property,
941 | currentSchemaMap[property] as optionsObject,
942 | updatedQueryObject
943 | );
944 | await this.checkUnique(
945 | property,
946 | currentSchemaMap[property] as optionsObject,
947 | updatedQueryObject,
948 | embeddedUniqueProperty
949 | );
950 | this.checkConstraints(
951 | property,
952 | currentSchemaMap[property] as optionsObject
953 | );
954 | }
955 | }
956 | return true;
957 | }
958 |
959 | /**
960 | * Method validates schema for replace queries by calling each schema option. Validation steps will throw an error if validation fails.
961 | * checkUnique step is different from validateInsertAgainstSchema for edge case where document property flagged as unique replaces itself.
962 | *
963 | * @param findObject The query used to match documents
964 | * @param queryObject which is the client document to insert into the database
965 | * @param schema which is the current schema, either at the outer level of a document or is an embedded schema
966 | * @param updatedQueryObject which is the converted version of the user's queryObject with properly coverted types for each value
967 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option 'unique', set to true, this array will
968 | * contain property keys from outer levels
969 | * @returns true or undefined.
970 | */
971 | async validateReplaceAgainstSchema(
972 | findObject: Record,
973 | queryObject: Record,
974 | schema: Schema,
975 | updatedQueryObject: Record,
976 | embeddedUniqueProperty: string[] = []
977 | ) {
978 | const currentSchemaMap = schema.schemaMap;
979 | this.checkDataFields(queryObject, currentSchemaMap);
980 | for (const property in currentSchemaMap) {
981 | // current SchemaMap's current property value is either an instance of a Schema or a SchemaOption
982 | // If Schema is stored, validate embedded object.
983 | if (currentSchemaMap[property] instanceof Schema) {
984 | updatedQueryObject[property] = {};
985 | embeddedUniqueProperty.push(property);
986 | await this.validateReplaceAgainstSchema(
987 | findObject,
988 | queryObject[property] as Record,
989 | currentSchemaMap[property] as Schema,
990 | updatedQueryObject[property] as Record,
991 | embeddedUniqueProperty
992 | );
993 | embeddedUniqueProperty.pop();
994 | } else {
995 | this.checkRequired(queryObject, property, currentSchemaMap[property]);
996 | this.setDefault(queryObject, property, currentSchemaMap[property]);
997 | this.populateQuery(
998 | queryObject,
999 | property,
1000 | currentSchemaMap[property],
1001 | updatedQueryObject
1002 | );
1003 | await this.checkUniqueForReplace(
1004 | property,
1005 | currentSchemaMap[property],
1006 | findObject,
1007 | updatedQueryObject,
1008 | embeddedUniqueProperty
1009 | );
1010 | this.checkConstraints(property, currentSchemaMap[property]);
1011 | }
1012 | }
1013 | return true;
1014 | }
1015 |
1016 | /**
1017 | * Method validates schema for update queries by calling each schema option. Validation steps will throw an error if validation fails.
1018 | * checkRequired and setDefault steps are not needed in validateInsertAgainstSchema method.
1019 | *
1020 | * @param queryObject which is the client document field to update in the database
1021 | * @param schema which is the current schema, either at the outer level of a document or is an embedded schema
1022 | * @param updatedQueryObject which is the converted version of the user's queryObject with properly coverted types for each value
1023 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option 'unique', set to true, this array will
1024 | * contain property keys from outer levels
1025 | * @returns true or undefined.
1026 | */
1027 | async validateUpdateAgainstSchema(
1028 | queryObject: Record,
1029 | schema: Schema,
1030 | updatedQueryObject: Record,
1031 | embeddedUniqueProperty: string[] = []
1032 | ) {
1033 | const currentSchemaMap = schema.schemaMap;
1034 | this.checkDataFields(queryObject, currentSchemaMap);
1035 | for (const property in queryObject) {
1036 | // current SchemaMap's current property value is either an instance of a Schema or a SchemaOption
1037 | // If Schema is stored, validate embedded object.
1038 | if (currentSchemaMap[property] instanceof Schema) {
1039 | updatedQueryObject[property] = {};
1040 | embeddedUniqueProperty.push(property);
1041 | await this.validateUpdateAgainstSchema(
1042 | queryObject[property] as Record,
1043 | currentSchemaMap[property],
1044 | updatedQueryObject[property] as Record,
1045 | embeddedUniqueProperty
1046 | );
1047 | embeddedUniqueProperty.pop();
1048 | } else {
1049 | this.populateQuery(
1050 | queryObject,
1051 | property,
1052 | currentSchemaMap[property],
1053 | updatedQueryObject
1054 | );
1055 | await this.checkUnique(
1056 | property,
1057 | currentSchemaMap[property],
1058 | updatedQueryObject,
1059 | embeddedUniqueProperty
1060 | );
1061 | this.checkConstraints(property, currentSchemaMap[property]);
1062 | }
1063 | }
1064 | return true;
1065 | }
1066 |
1067 | /**
1068 | * Method loops through all fields in query objects and throws an error if any properties exist which are not present in the schema.
1069 | *
1070 | * @param queryObject which is the client document field to update or insert into the database
1071 | * @param schemaMap which contains the schemaMap for the current level in the user's document
1072 | * @returns true or undefined.
1073 | */
1074 | checkDataFields(
1075 | queryObject: Record,
1076 | schemaMap: Record
1077 | ) {
1078 | for (const property in queryObject) {
1079 | if (!Object.prototype.hasOwnProperty.call(schemaMap, property)) {
1080 | throw new Error(
1081 | 'Requested query object contains properties not present in the Schema.'
1082 | );
1083 | }
1084 | }
1085 | return true;
1086 | }
1087 |
1088 | /**
1089 | * Method validates a property with the option of 'required' has a given value and throws an error if any property does not meet schema criteria.
1090 | *
1091 | * @param queryObject which is the client document field to update or insert into the database
1092 | * @param propertyName which is the property key to check
1093 | * @param propertyOptions which is the propertyOptions object for the given property from the schema
1094 | * @returns true or undefined.
1095 | */
1096 | checkRequired(
1097 | queryObject: Record,
1098 | propertyName: string,
1099 | propertyOptions: optionsObject
1100 | ) {
1101 | if (propertyOptions.required === true) {
1102 | if (!Object.prototype.hasOwnProperty.call(queryObject, propertyName)) {
1103 | throw new Error(`${propertyName} is Required by the Schema.`);
1104 | }
1105 | }
1106 | return true;
1107 | }
1108 |
1109 | /**
1110 | * Method populates original queryObject with properties not present in the query but present in the schema with their specified default values.
1111 | *
1112 | * @param queryObject which is the client document field to update or insert into the database
1113 | * @param propertyName which is the property key to check
1114 | * @param propertyOptions which is the propertyOptions object for the given property from the schema
1115 | * @returns true or undefined.
1116 | */
1117 | setDefault(
1118 | queryObject: Record,
1119 | propertyName: string,
1120 | propertyOptions: optionsObject
1121 | ) {
1122 | if (!Object.prototype.hasOwnProperty.call(queryObject, propertyName)) {
1123 | queryObject[propertyName] = propertyOptions.default;
1124 | }
1125 | return true;
1126 | }
1127 |
1128 | /**
1129 | * Method populates the property updatedQueryObject object to be used in the actual query.
1130 | * Creates an instance of the given schema datatype with the user value from their query.
1131 | * Casts the value to the datatype and returns an error if impossible.
1132 | *
1133 | * @param queryObject which is the client document field to update or insert into the database
1134 | * @param propertyName which is the property key to check
1135 | * @param propertyOptions which is the propertyOptions object for the given property from the schema
1136 | * @param updatedQueryObject which is the formatted version of the user's queryObject with properly coverted types for each value.
1137 | * This may refer to the outer object or embedded objects within.
1138 | * @returns true or undefined.
1139 | */
1140 | populateQuery(
1141 | queryObject: Record,
1142 | propertyName: string,
1143 | propertyOptions: optionsObject,
1144 | updatedQueryObject: Record
1145 | ) {
1146 | const valueAsDatatype = new propertyOptions.type(queryObject[propertyName]);
1147 | valueAsDatatype.convertType();
1148 | valueAsDatatype.validateType();
1149 | if (valueAsDatatype.valid === false) {
1150 | throw new Error(
1151 | 'Data was not able to be translated to given specified schema data type.'
1152 | );
1153 | }
1154 | updatedQueryObject[propertyName] = valueAsDatatype.convertedValue;
1155 | }
1156 |
1157 | /**
1158 | * Method queries the database to see if a document already exists with a duplicate value for the given property.
1159 | *
1160 | * @param propertyName which is the property key to check
1161 | * @param propertyOptions which is the propertyOptions object for the given property from the schema
1162 | * @param updatedQueryObject which is the formatted version of the user's queryObject with properly coverted types for each value
1163 | * This may refer to the outer object or embedded objects within.
1164 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option unique, set to true, this array will
1165 | * contain property keys from outer levels
1166 | * @returns true or undefined.
1167 | */
1168 | async checkUnique(
1169 | propertyName: string,
1170 | propertyOptions: optionsObject,
1171 | updatedQueryObject: Record,
1172 | embeddedUniqueProperty: string[]
1173 | ) {
1174 | if (propertyOptions.unique === true) {
1175 | // query object to check if unique value already exists in database collection
1176 | const queryObjectForUnique: Record = {};
1177 | // constructs correctly formatted string for property key of queryObjectForUnique
1178 | const formattedPropertyName = this.formatQueryField(
1179 | propertyName,
1180 | embeddedUniqueProperty
1181 | );
1182 | queryObjectForUnique[formattedPropertyName] =
1183 | updatedQueryObject[propertyName];
1184 | const propertyExists = await this.findOne(queryObjectForUnique);
1185 | if (propertyExists !== undefined) {
1186 | throw new Error(
1187 | 'Property designated as unique in Schema already exists.'
1188 | );
1189 | }
1190 | }
1191 | return true;
1192 | }
1193 |
1194 | /**
1195 | * Method queries the database to see if a document already exists with a duplicate value for the given property.
1196 | * This method is different from checkUnique due to edge case where document property flagged as unique replaces itself.
1197 | *
1198 | * @param propertyName which is the property key to check
1199 | * @param findObject The query used to match documents
1200 | * @param updatedQueryObject which is the formatted version of the user's queryObject with properly coverted types for each value
1201 | * This may refer to the outer object or embedded objects within.
1202 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option unique, set to true, this array will
1203 | * contain property keys from outer levels
1204 | * @returns true or undefined.
1205 | */
1206 | async checkUniqueForReplace(
1207 | propertyName: string,
1208 | propertyOptions: optionsObject,
1209 | findObject: Record,
1210 | updatedQueryObject: Record,
1211 | embeddedUniqueProperty: string[]
1212 | ) {
1213 | if (propertyOptions.unique === true) {
1214 | let originalPropertyValue = await this.findOne(findObject);
1215 | if (originalPropertyValue === undefined) {
1216 | throw new Error('No database entry found on query.');
1217 | } else if (originalPropertyValue === null)
1218 | throw new Error('No database entry found on query.');
1219 | else if (typeof originalPropertyValue === 'object') {
1220 | // iterate through embeddedUniqueProperty array to access embedded objects that directly contains current propertyName
1221 | if (embeddedUniqueProperty.length) {
1222 | embeddedUniqueProperty.forEach((prop) => {
1223 | //@ts-ignore Fix later.
1224 | originalPropertyValue = originalPropertyValue[prop];
1225 | });
1226 | }
1227 | // convert originalPropertyValue's type to match matching property's type in the updatedQueryObject
1228 | const valueAsDatatype = new propertyOptions.type(
1229 | //@ts-ignore Fix later.
1230 | originalPropertyValue[propertyName]
1231 | );
1232 | valueAsDatatype.convertType();
1233 | valueAsDatatype.validateType();
1234 | if (valueAsDatatype.valid === false) {
1235 | throw new Error(
1236 | 'Data was not able to be translated to given specified schema data type.'
1237 | );
1238 | }
1239 | const formattedOriginalPropertyValue = JSON.stringify(
1240 | valueAsDatatype.convertedValue
1241 | );
1242 | const formattedUpdatedQueryObjectValue = JSON.stringify(
1243 | updatedQueryObject[propertyName]
1244 | );
1245 |
1246 | // query object to check if unique value already exists in database collection
1247 | const queryObjectForUnique: Record = {};
1248 | // constructs correctly formatted string for property key of queryObjectForUnique
1249 | const formattedPropertyName = this.formatQueryField(
1250 | propertyName,
1251 | embeddedUniqueProperty
1252 | );
1253 | queryObjectForUnique[formattedPropertyName] =
1254 | updatedQueryObject[propertyName];
1255 | const propertyExists = await this.findOne(queryObjectForUnique);
1256 |
1257 | // If property value exists in database collection, AND the property value from the found document (ln 1096) doesn't match
1258 | // the property value in the updatedQueryObject, throw an error.
1259 | if (
1260 | propertyExists !== undefined &&
1261 | formattedOriginalPropertyValue !== formattedUpdatedQueryObjectValue
1262 | ) {
1263 | throw new Error(
1264 | `Property designated as unique in Schema already exists. ${formattedPropertyName}: ${formattedUpdatedQueryObjectValue}`
1265 | );
1266 | }
1267 | }
1268 | }
1269 | return true;
1270 | }
1271 |
1272 | /**
1273 | * Method validates that the casted datatype satisfies the user defined callback function.
1274 | *
1275 | * @param propertyName which is the property key to check
1276 | * @param propertyOptions which is the propertyOptions object for the given property from the schema
1277 | * @returns true or undefined.
1278 | */
1279 | checkConstraints(propertyName: string, propertyOptions: optionsObject) {
1280 | if (propertyOptions.validator === null) return true;
1281 | if (typeof propertyOptions.validator !== 'function') {
1282 | throw new Error(
1283 | 'Callback given as validator in Schema is not a function.'
1284 | );
1285 | }
1286 | const isConstraintMet = propertyOptions.validator(
1287 | this.updatedQueryObject[propertyName]
1288 | );
1289 | if (isConstraintMet !== true) {
1290 | throw new Error('Callback given as validator in Schema is violated.');
1291 | }
1292 | return true;
1293 | }
1294 |
1295 | /**
1296 | * Method resets the updatedQueryObject to an empty object after an insert, replace, or update query.
1297 | *
1298 | * @returns undefined.
1299 | */
1300 | resetQueryObject() {
1301 | this.updatedQueryObject = {};
1302 | return;
1303 | }
1304 |
1305 | /**
1306 | * Method
1307 | *
1308 | * @param propertyName which is the property key to check, will be the last property in the returned string
1309 | * @param embeddedUniqueProperty When checking a property in an embedded document with the schema option 'unique', set to true, this array will
1310 | * contain property keys from outer levels.
1311 | * @returns a string that is either equal to propertyName or a concatenated string containing properties from parent levels with dot notation
1312 | * in order to format a proper string key to execute a database query for a value inside an embedded document.
1313 | */
1314 | formatQueryField(propertyName: string, embeddedUniqueProperty: string[]) {
1315 | let string = '';
1316 | if (embeddedUniqueProperty.length === 0) {
1317 | return propertyName;
1318 | } else {
1319 | while (embeddedUniqueProperty.length > 0) {
1320 | if (string === '') string += embeddedUniqueProperty.shift();
1321 | else string += `.${embeddedUniqueProperty.shift()}`;
1322 | }
1323 | string += `.${propertyName}`;
1324 | }
1325 | return string;
1326 | }
1327 | }
1328 |
1329 | export { Query };
1330 |
--------------------------------------------------------------------------------
/lib/schema.ts:
--------------------------------------------------------------------------------
1 | // deno-lint-ignore-file no-explicit-any
2 |
3 | /**
4 | *
5 | * @description This file defines the schema class.
6 | *
7 | */
8 |
9 | import { dango } from './dango.ts';
10 |
11 | /**
12 | * Class definition of a Schema.
13 | * @param schemaObj Object passed in by users containing key value pairs of properties allowed in the document with a value of an object containing property options including:
14 | * type - Required. A lowercase string of expected datatype
15 | * required - If set to true, the property must include a value in insertion/replace queries
16 | * unique - If set to true, the database can only have one instance with the specified insertion/update value. Ignores null.
17 | * default - The default value of the property if not specified.
18 | * validator - A user-defined function that will take the converted value and return a boolean for validation
19 | * Sets the schemaMap property to objects populated with all options.
20 | *
21 | * @returns An object of class Schema.
22 | */
23 | export class Schema {
24 | schemaMap: Record;
25 |
26 | constructor(schemaObj: Record) {
27 | if (schemaObj === undefined) {
28 | throw new Error('Schema requires a valid argument.');
29 | }
30 | this.schemaMap = {};
31 | for (const property in schemaObj) {
32 | // SJ: check if Schema assigned as value
33 | if (schemaObj[property] instanceof Schema) {
34 | // SJ: assign Schema
35 | this.schemaMap[property] = schemaObj[property];
36 | } else if (typeof schemaObj[property] === 'object') {
37 | this.schemaMap[property] = new SchemaOptions(schemaObj[property]);
38 | } else if (
39 | typeof schemaObj[property] !== 'object' &&
40 | Object.prototype.hasOwnProperty.call(dango.types, schemaObj[property])
41 | ) {
42 | this.schemaMap[property] = new SchemaOptions({
43 | type: schemaObj[property],
44 | });
45 | } else {
46 | throw new Error(
47 | 'Argument for schema definition incorrectly formatted.'
48 | );
49 | }
50 | }
51 | }
52 | }
53 |
54 | export interface optionsObject {
55 | type: any;
56 | required?: boolean;
57 | unique?: boolean;
58 | default?: any;
59 | validator?: Function | null;
60 | }
61 |
62 | /**
63 | * Class definition of a SchemaOptions.
64 | * @param options Options object passed in by the user. Default values are set for each property and overriden by user input.
65 | *
66 | * @returns An object of class SchemaOptions.
67 | */
68 |
69 | export class SchemaOptions {
70 | type: any;
71 | required?: boolean;
72 | unique?: boolean;
73 | default?: any;
74 | validator?: Function | null;
75 |
76 | constructor(options: optionsObject) {
77 | if (!Object.prototype.hasOwnProperty.call(options, 'type')) {
78 | throw new Error('Type must be specified');
79 | }
80 | this.type = undefined;
81 | this.required = false;
82 | this.unique = false;
83 | this.default = null;
84 | this.validator = null;
85 | for (const key in options) {
86 | if (key === 'type') {
87 | if (Object.prototype.hasOwnProperty.call(dango.types, options[key])) {
88 | this.type = dango.types[options[key]];
89 | } else {
90 | throw new Error('Specified type is invalid');
91 | }
92 | } else if (Object.prototype.hasOwnProperty.call(this, key)) {
93 | this[key as keyof optionsObject] = options[key as keyof optionsObject];
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/mod.ts:
--------------------------------------------------------------------------------
1 | export { dango } from './lib/dango.ts';
--------------------------------------------------------------------------------
/tests/test_connections.ts:
--------------------------------------------------------------------------------
1 | // Required Flags for Test:
2 | // - --allow-read
3 | // - --allow-net
4 |
5 | import {
6 | assertInstanceOf,
7 | assertRejects,
8 | assertStrictEquals,
9 | assertThrows,
10 | afterEach,
11 | beforeEach,
12 | describe,
13 | it,
14 | } from '../deps.ts';
15 |
16 | import { dotenv } from '../deps.ts';
17 |
18 | import { MongoClient, Database } from '../deps.ts';
19 |
20 | import { Connection } from '../lib/connections.ts';
21 |
22 | const ENV = dotenv.config({ path: '../.env' });
23 |
24 | describe('Connection constructor', () => {
25 | let newObject: unknown;
26 | describe('creating an instance of the class with no input', () => {
27 | it('will throw an error', () => {
28 | assertThrows(() => {
29 | //@ts-ignore Ignore TS warning to run test
30 | newObject = new Connection();
31 | });
32 | });
33 | });
34 | describe('creating an instance of the class with a valid URI string', () => {
35 | beforeEach(() => {
36 | newObject = new Connection(ENV.URI_STRING);
37 | });
38 | it('will create an instance of the class', () => {
39 | assertInstanceOf(newObject, Connection);
40 | });
41 | it('will have a connected property assigned the value of false', () => {
42 | if (newObject instanceof Connection) {
43 | assertStrictEquals(newObject.connected, false);
44 | }
45 | });
46 | it('will have a connectionString property assigned the value of the URI connection string', () => {
47 | if (newObject instanceof Connection) {
48 | //@ts-ignore Ignore TS warning to run test
49 | assertStrictEquals(newObject.connectionString, ENV.URI_STRING);
50 | }
51 | });
52 | it('will have a db property assigned the value of false', () => {
53 | if (newObject instanceof Connection) {
54 | assertStrictEquals(newObject.db, false);
55 | }
56 | });
57 | it('will have a client property assigned the value of undefined', () => {
58 | if (newObject instanceof Connection) {
59 | //@ts-ignore Ignore TS warning to run test
60 | assertStrictEquals(newObject.client, undefined);
61 | }
62 | });
63 | it('will have a connect method', () => {
64 | if (newObject instanceof Connection) {
65 | assertInstanceOf(newObject.connect, Function);
66 | }
67 | });
68 | it('will have a disconnect method', () => {
69 | if (newObject instanceof Connection) {
70 | assertInstanceOf(newObject.disconnect, Function);
71 | }
72 | });
73 | });
74 | });
75 | describe('Connection methods', () => {
76 | let newObject: unknown;
77 | describe('using the connect method', () => {
78 | describe('creating a Connection object with a valid URI', () => {
79 | beforeEach(() => {
80 | newObject = new Connection(ENV.URI_STRING);
81 | });
82 | afterEach(async () => {
83 | if (newObject instanceof Connection) {
84 | await newObject.disconnect();
85 | }
86 | });
87 | it('will set connected property to true', async () => {
88 | if (newObject instanceof Connection) {
89 | await newObject.connect();
90 | assertStrictEquals(newObject.connected, true);
91 | }
92 | });
93 | it('will connect to the MongoClient and set client property to the connection', async () => {
94 | if (newObject instanceof Connection) {
95 | await newObject.connect();
96 | //@ts-ignore Ignore TS warning to run test
97 | assertInstanceOf(newObject.client, MongoClient);
98 | }
99 | });
100 | it('will connect to the database and set db property to the connection', async () => {
101 | if (newObject instanceof Connection) {
102 | await newObject.connect();
103 | assertInstanceOf(newObject.db, Database);
104 | }
105 | });
106 | });
107 | describe('creating a Connection object with an invalid URI', () => {
108 | beforeEach(() => {
109 | newObject = new Connection('BAD_URI_STRING');
110 | });
111 | it('will throw an error', async () => {
112 | await assertRejects(async () => {
113 | if (newObject instanceof Connection) {
114 | await newObject.connect();
115 | }
116 | });
117 | });
118 | it('will not change the value of the connected property', async () => {
119 | if (newObject instanceof Connection) {
120 | try {
121 | await newObject.connect();
122 | } catch (err) {
123 | assertStrictEquals(newObject.connected, false);
124 | }
125 | }
126 | });
127 | it('will not change the value of the db property', async () => {
128 | if (newObject instanceof Connection) {
129 | try {
130 | await newObject.connect();
131 | } catch (err) {
132 | assertStrictEquals(newObject.db, false);
133 | }
134 | }
135 | });
136 | });
137 | });
138 | describe('using the disconnect method', () => {
139 | describe('invoking disconnect after a connection is established', () => {
140 | beforeEach(() => {
141 | newObject = new Connection(ENV.URI_STRING);
142 | });
143 | it('should reset the value of the connected property', async () => {
144 | if (newObject instanceof Connection) {
145 | await newObject.connect();
146 | await newObject.disconnect();
147 | assertStrictEquals(newObject.connected, false);
148 | }
149 | });
150 | it('should reset the value of the db property', async () => {
151 | if (newObject instanceof Connection) {
152 | await newObject.connect();
153 | await newObject.disconnect();
154 | assertStrictEquals(newObject.db, false);
155 | }
156 | });
157 | });
158 | describe('invoking disconnect before a connection is established', () => {
159 | it('will throw an error', () => {
160 | if (newObject instanceof Connection) {
161 | assertRejects(async () => {
162 | if (newObject instanceof Connection) {
163 | await newObject.disconnect();
164 | }
165 | });
166 | }
167 | });
168 | it('will not change the value of the connected property', async () => {
169 | if (newObject instanceof Connection) {
170 | try {
171 | await newObject.disconnect();
172 | } catch (err) {
173 | assertStrictEquals(newObject.connected, false);
174 | }
175 | }
176 | });
177 | it('will not change the value of the db property', async () => {
178 | if (newObject instanceof Connection) {
179 | try {
180 | await newObject.disconnect();
181 | } catch (err) {
182 | assertStrictEquals(newObject.db, false);
183 | }
184 | }
185 | });
186 | });
187 | });
188 | });
189 |
--------------------------------------------------------------------------------
/tests/test_datatypes.ts:
--------------------------------------------------------------------------------
1 | import {
2 | assertInstanceOf,
3 | assertStrictEquals,
4 | assertThrows,
5 | beforeEach,
6 | describe,
7 | it,
8 | } from '../deps.ts';
9 |
10 | import {
11 | SchemaNumber,
12 | SchemaDecimal128,
13 | SchemaString,
14 | SchemaBoolean,
15 | SchemaObjectId,
16 | SchemaUUID,
17 | SchemaDate
18 | } from '../lib/datatypes.ts'
19 |
20 | import {
21 | Bson
22 | } from '../deps.ts'
23 |
24 | describe('Schema Number', () => {
25 | let newObject: unknown
26 | describe('creating an instance of the class with no input', () => {
27 | it('will throw an error', () => {
28 | assertThrows(() => {
29 | //@ts-ignore Ignore TS warning to run test
30 | newObject = new SchemaNumber();
31 | });
32 | });
33 | });
34 | describe('creating an instance of the class with a null input', () => {
35 | beforeEach(() => {
36 | newObject = new SchemaNumber(null)
37 | });
38 | it('will create an instance of the class', () => {
39 | assertInstanceOf(newObject, SchemaNumber)
40 | });
41 | it('will have a convertType method', () => {
42 | if (newObject instanceof SchemaNumber) {
43 | assertInstanceOf(newObject.convertType, Function);
44 | }
45 | });
46 | it('will cast the input and set property convertedValue to null', () => {
47 | if (newObject instanceof SchemaNumber) {
48 | newObject.convertType();
49 | assertStrictEquals(newObject.convertedValue, null);
50 | }
51 | });
52 | it('will set property valid to true after invoking validateType', () => {
53 | if (newObject instanceof SchemaNumber) {
54 | newObject.validateType();
55 | assertStrictEquals(newObject.valid, false);
56 | newObject.convertType()
57 | newObject.validateType();
58 | assertStrictEquals(newObject.valid, true);
59 | }
60 | });
61 | });
62 | describe('creating an instance of the class with a Bson Double input', () => {
63 | beforeEach(() => {
64 | newObject = new SchemaNumber(new Bson.Double(3.1415))
65 | });
66 | it('will create an instance of the class', () => {
67 | assertInstanceOf(newObject, SchemaNumber)
68 | });
69 | it('will have a convertType method', () => {
70 | if (newObject instanceof SchemaNumber) {
71 | assertInstanceOf(newObject.convertType, Function);
72 | }
73 | });
74 | it('will cast the input and set property convertedValue to a Double', () => {
75 | if (newObject instanceof SchemaNumber) {
76 | newObject.convertType();
77 | assertInstanceOf(newObject.convertedValue, Bson.Double);
78 | }
79 | });
80 | it('will set property valid to true after invoking validateType', () => {
81 | if (newObject instanceof SchemaNumber) {
82 | newObject.validateType();
83 | assertStrictEquals(newObject.valid, false);
84 | newObject.convertType()
85 | newObject.validateType();
86 | assertStrictEquals(newObject.valid, true);
87 | }
88 | });
89 | });
90 | describe('creating an instance of the class with a non-string or number input', () => {
91 | beforeEach(() => {
92 | newObject = new SchemaNumber([5])
93 | });
94 | it('will create an instance of the class', () => {
95 | assertInstanceOf(newObject, SchemaNumber)
96 | });
97 | it('will have a convertType method', () => {
98 | if (newObject instanceof SchemaNumber) {
99 | assertInstanceOf(newObject.convertType, Function);
100 | }
101 | });
102 | it('will be unable cast the input and set property convertedValue to a Double. Value will be undefined', () => {
103 | if (newObject instanceof SchemaNumber) {
104 | newObject.convertType();
105 | assertStrictEquals(newObject.convertedValue, undefined);
106 | }
107 | });
108 | it('will set property valid to false after invoking validateType', () => {
109 | if (newObject instanceof SchemaNumber) {
110 | newObject.validateType();
111 | assertStrictEquals(newObject.valid, false);
112 | newObject.convertType()
113 | newObject.validateType();
114 | assertStrictEquals(newObject.valid, false);
115 | }
116 | });
117 | });
118 | describe('creating an instance of the class with string input that cannot be cast to a number', () => {
119 | beforeEach(() => {
120 | newObject = new SchemaNumber('dangoDB')
121 | });
122 | it('will create an instance of the class', () => {
123 | assertInstanceOf(newObject, SchemaNumber)
124 | });
125 | it('will have a convertType method', () => {
126 | if (newObject instanceof SchemaNumber) {
127 | assertInstanceOf(newObject.convertType, Function);
128 | }
129 | });
130 | it('will be unable cast the input and set property convertedValue to a Double. Value will be undefined', () => {
131 | if (newObject instanceof SchemaNumber) {
132 | newObject.convertType();
133 | assertStrictEquals(newObject.convertedValue, undefined);
134 | }
135 | });
136 | it('will set property valid to false after invoking validateType', () => {
137 | if (newObject instanceof SchemaNumber) {
138 | newObject.validateType();
139 | assertStrictEquals(newObject.valid, false);
140 | newObject.convertType()
141 | newObject.validateType();
142 | assertStrictEquals(newObject.valid, false);
143 | }
144 | });
145 | });
146 | describe('creating an instance of the class with string input that can be cast to a number', () => {
147 | beforeEach(() => {
148 | newObject = new SchemaNumber('5')
149 | });
150 | it('will create an instance of the class', () => {
151 | assertInstanceOf(newObject, SchemaNumber)
152 | });
153 | it('will have a convertType method', () => {
154 | if (newObject instanceof SchemaNumber) {
155 | assertInstanceOf(newObject.convertType, Function);
156 | }
157 | });
158 | it('will cast the input and set property convertedValue to a Double', () => {
159 | if (newObject instanceof SchemaNumber) {
160 | newObject.convertType();
161 | assertInstanceOf(newObject.convertedValue, Bson.Double);
162 | }
163 | });
164 | it('will set property valid to true after invoking validateType', () => {
165 | if (newObject instanceof SchemaNumber) {
166 | newObject.validateType();
167 | assertStrictEquals(newObject.valid, false);
168 | newObject.convertType()
169 | newObject.validateType();
170 | assertStrictEquals(newObject.valid, true);
171 | }
172 | });
173 | });
174 | describe('creating an instance of the class with a number input', () => {
175 | beforeEach(() => {
176 | newObject = new SchemaNumber(5)
177 | });
178 | it('will create an instance of the class', () => {
179 | assertInstanceOf(newObject, SchemaNumber)
180 | });
181 | it('will have a convertType method', () => {
182 | if (newObject instanceof SchemaNumber) {
183 | assertInstanceOf(newObject.convertType, Function);
184 | }
185 | });
186 | it('will cast the input and set property convertedValue to a Double', () => {
187 | if (newObject instanceof SchemaNumber) {
188 | newObject.convertType();
189 | assertInstanceOf(newObject.convertedValue, Bson.Double);
190 | }
191 | });
192 | it('will set property valid to true after invoking validateType', () => {
193 | if (newObject instanceof SchemaNumber) {
194 | newObject.validateType();
195 | assertStrictEquals(newObject.valid, false);
196 | newObject.convertType()
197 | newObject.validateType();
198 | assertStrictEquals(newObject.valid, true);
199 | }
200 | });
201 | });
202 | });
203 |
204 | describe('Schema Decimal128', () => {
205 | let newObject: unknown
206 | describe('creating an instance of the class with no input', () => {
207 | it('will throw an error', () => {
208 | assertThrows(() => {
209 | //@ts-ignore Ignore TS warning to run test
210 | newObject = new SchemaDecimal128();
211 | });
212 | });
213 | });
214 | describe('creating an instance of the class with a null input', () => {
215 | beforeEach(() => {
216 | newObject = new SchemaDecimal128(null)
217 | });
218 | it('will create an instance of the class', () => {
219 | assertInstanceOf(newObject, SchemaDecimal128)
220 | });
221 | it('will have a convertType method', () => {
222 | if (newObject instanceof SchemaDecimal128) {
223 | assertInstanceOf(newObject.convertType, Function);
224 | }
225 | });
226 | it('will cast the input and set property convertedValue to null', () => {
227 | if (newObject instanceof SchemaDecimal128) {
228 | newObject.convertType();
229 | assertStrictEquals(newObject.convertedValue, null);
230 | }
231 | });
232 | it('will set property valid to true after invoking validateType', () => {
233 | if (newObject instanceof SchemaDecimal128) {
234 | newObject.validateType();
235 | assertStrictEquals(newObject.valid, false);
236 | newObject.convertType()
237 | newObject.validateType();
238 | assertStrictEquals(newObject.valid, true);
239 | }
240 | });
241 | });
242 | describe('creating an instance of the class with a Decimal128 input', () => {
243 | beforeEach(() => {
244 | newObject = new SchemaDecimal128(new Bson.Decimal128('3.1415'))
245 | });
246 | it('will create an instance of the class', () => {
247 | assertInstanceOf(newObject, SchemaDecimal128)
248 | });
249 | it('will have a convertType method', () => {
250 | if (newObject instanceof SchemaDecimal128) {
251 | assertInstanceOf(newObject.convertType, Function);
252 | }
253 | });
254 | it('will cast the input and set property convertedValue to a Double', () => {
255 | if (newObject instanceof SchemaDecimal128) {
256 | newObject.convertType();
257 | assertInstanceOf(newObject.convertedValue, Bson.Decimal128);
258 | }
259 | });
260 | it('will set property valid to true after invoking validateType', () => {
261 | if (newObject instanceof SchemaDecimal128) {
262 | newObject.validateType();
263 | assertStrictEquals(newObject.valid, false);
264 | newObject.convertType()
265 | newObject.validateType();
266 | assertStrictEquals(newObject.valid, true);
267 | }
268 | });
269 | });
270 | describe('creating an instance of the class with a non-string or number input', () => {
271 | beforeEach(() => {
272 | newObject = new SchemaDecimal128([5])
273 | });
274 | it('will create an instance of the class', () => {
275 | assertInstanceOf(newObject, SchemaDecimal128)
276 | });
277 | it('will have a convertType method', () => {
278 | if (newObject instanceof SchemaDecimal128) {
279 | assertInstanceOf(newObject.convertType, Function);
280 | }
281 | });
282 | it('will be unable cast the input and set property convertedValue to a Decimal128. Value will be undefined', () => {
283 | if (newObject instanceof SchemaDecimal128) {
284 | newObject.convertType();
285 | assertStrictEquals(newObject.convertedValue, undefined);
286 | }
287 | });
288 | it('will set property valid to false after invoking validateType', () => {
289 | if (newObject instanceof SchemaDecimal128) {
290 | newObject.validateType();
291 | assertStrictEquals(newObject.valid, false);
292 | newObject.convertType()
293 | newObject.validateType();
294 | assertStrictEquals(newObject.valid, false);
295 | }
296 | });
297 | });
298 | describe('creating an instance of the class with string input that cannot be cast to a Decimal128', () => {
299 | beforeEach(() => {
300 | newObject = new SchemaDecimal128('dangoDB')
301 | });
302 | it('will create an instance of the class', () => {
303 | assertInstanceOf(newObject, SchemaDecimal128)
304 | });
305 | it('will have a convertType method', () => {
306 | if (newObject instanceof SchemaDecimal128) {
307 | assertInstanceOf(newObject.convertType, Function);
308 | }
309 | });
310 | it('will be unable cast the input and set property convertedValue to a Decimal128. Value will be undefined', () => {
311 | if (newObject instanceof SchemaDecimal128) {
312 | newObject.convertType();
313 | assertStrictEquals(newObject.convertedValue, undefined);
314 | }
315 | });
316 | it('will set property valid to false after invoking validateType', () => {
317 | if (newObject instanceof SchemaDecimal128) {
318 | newObject.validateType();
319 | assertStrictEquals(newObject.valid, false);
320 | newObject.convertType()
321 | newObject.validateType();
322 | assertStrictEquals(newObject.valid, false);
323 | }
324 | });
325 | });
326 | describe('creating an instance of the class with string input that can be cast to a Decimal128', () => {
327 | beforeEach(() => {
328 | newObject = new SchemaDecimal128('3.1415')
329 | });
330 | it('will create an instance of the class', () => {
331 | assertInstanceOf(newObject, SchemaDecimal128)
332 | });
333 | it('will have a convertType method', () => {
334 | if (newObject instanceof SchemaDecimal128) {
335 | assertInstanceOf(newObject.convertType, Function);
336 | }
337 | });
338 | it('will cast the input and set property convertedValue to a Decimal128', () => {
339 | if (newObject instanceof SchemaDecimal128) {
340 | newObject.convertType();
341 | assertInstanceOf(newObject.convertedValue, Bson.Decimal128);
342 | }
343 | });
344 | it('will set property valid to true after invoking validateType', () => {
345 | if (newObject instanceof SchemaDecimal128) {
346 | newObject.validateType();
347 | assertStrictEquals(newObject.valid, false);
348 | newObject.convertType()
349 | newObject.validateType();
350 | assertStrictEquals(newObject.valid, true);
351 | }
352 | });
353 | });
354 | describe('creating an instance of the class with a number input', () => {
355 | beforeEach(() => {
356 | newObject = new SchemaDecimal128(3.1415)
357 | });
358 | it('will create an instance of the class', () => {
359 | assertInstanceOf(newObject, SchemaDecimal128)
360 | });
361 | it('will have a convertType method', () => {
362 | if (newObject instanceof SchemaDecimal128) {
363 | assertInstanceOf(newObject.convertType, Function);
364 | }
365 | });
366 | it('will cast the input and set property convertedValue to a Decimal128', () => {
367 | if (newObject instanceof SchemaDecimal128) {
368 | newObject.convertType();
369 | assertInstanceOf(newObject.convertedValue, Bson.Decimal128);
370 | }
371 | });
372 | it('will set property valid to true after invoking validateType', () => {
373 | if (newObject instanceof SchemaDecimal128) {
374 | newObject.validateType();
375 | assertStrictEquals(newObject.valid, false);
376 | newObject.convertType()
377 | newObject.validateType();
378 | assertStrictEquals(newObject.valid, true);
379 | }
380 | });
381 | });
382 | });
383 |
384 | describe('Schema String', () => {
385 | let newObject: unknown
386 | describe('creating an instance of the class with no input', () => {
387 | it('will throw an error', () => {
388 | assertThrows(() => {
389 | //@ts-ignore Ignore TS warning to run test
390 | newObject = new SchemaString();
391 | });
392 | });
393 | });
394 | describe('creating an instance of the class with a null input', () => {
395 | beforeEach(() => {
396 | newObject = new SchemaString(null)
397 | });
398 | it('will create an instance of the class', () => {
399 | assertInstanceOf(newObject, SchemaString)
400 | });
401 | it('will have a convertType method', () => {
402 | if (newObject instanceof SchemaString) {
403 | assertInstanceOf(newObject.convertType, Function);
404 | }
405 | });
406 | it('will cast the input and set property convertedValue to null', () => {
407 | if (newObject instanceof SchemaString) {
408 | newObject.convertType();
409 | assertStrictEquals(newObject.convertedValue, null);
410 | }
411 | });
412 | it('will set property valid to true after invoking validateType', () => {
413 | if (newObject instanceof SchemaString) {
414 | newObject.validateType();
415 | assertStrictEquals(newObject.valid, false);
416 | newObject.convertType()
417 | newObject.validateType();
418 | assertStrictEquals(newObject.valid, true);
419 | }
420 | });
421 | });
422 | describe('creating an instance of the class with a String object input', () => {
423 | beforeEach(() => {
424 | newObject = new SchemaString(new String('dangoDB'));
425 | });
426 | it('will create an instance of the class', () => {
427 | assertInstanceOf(newObject, SchemaString)
428 | });
429 | it('will have a convertType method', () => {
430 | if (newObject instanceof SchemaString) {
431 | assertInstanceOf(newObject.convertType, Function);
432 | }
433 | });
434 | it('will cast the input and set property convertedValue to a string', () => {
435 | if (newObject instanceof SchemaString) {
436 | newObject.convertType();
437 | assertInstanceOf(newObject.convertedValue, String);
438 | }
439 | });
440 | it('will set property valid to true after invoking validateType', () => {
441 | if (newObject instanceof SchemaString) {
442 | newObject.validateType();
443 | assertStrictEquals(newObject.valid, false);
444 | newObject.convertType()
445 | newObject.validateType();
446 | assertStrictEquals(newObject.valid, true);
447 | }
448 | });
449 | });
450 | describe('creating an instance of the class with a non-string or number or boolean input', () => {
451 | beforeEach(() => {
452 | newObject = new SchemaString(['dangoDB'])
453 | });
454 | it('will create an instance of the class', () => {
455 | assertInstanceOf(newObject, SchemaString)
456 | });
457 | it('will have a convertType method', () => {
458 | if (newObject instanceof SchemaString) {
459 | assertInstanceOf(newObject.convertType, Function);
460 | }
461 | });
462 | it('will be unable cast the input and set property convertedValue to a string. Value will be undefined', () => {
463 | if (newObject instanceof SchemaString) {
464 | newObject.convertType();
465 | assertStrictEquals(newObject.convertedValue, undefined);
466 | }
467 | });
468 | it('will set property valid to false after invoking validateType', () => {
469 | if (newObject instanceof SchemaString) {
470 | newObject.validateType();
471 | assertStrictEquals(newObject.valid, false);
472 | newObject.convertType()
473 | newObject.validateType();
474 | assertStrictEquals(newObject.valid, false);
475 | }
476 | });
477 | });
478 | describe('creating an instance of the class with string input', () => {
479 | beforeEach(() => {
480 | newObject = new SchemaString('dangoDB')
481 | });
482 | it('will create an instance of the class', () => {
483 | assertInstanceOf(newObject, SchemaString)
484 | });
485 | it('will have a convertType method', () => {
486 | if (newObject instanceof SchemaString) {
487 | assertInstanceOf(newObject.convertType, Function);
488 | }
489 | });
490 | it('will cast the input and set property convertedValue to a string', () => {
491 | if (newObject instanceof SchemaString) {
492 | newObject.convertType();
493 | assertStrictEquals(typeof newObject.convertedValue, 'string');
494 | }
495 | });
496 | it('will set property valid to true after invoking validateType', () => {
497 | if (newObject instanceof SchemaString) {
498 | newObject.validateType();
499 | assertStrictEquals(newObject.valid, false);
500 | newObject.convertType()
501 | newObject.validateType();
502 | assertStrictEquals(newObject.valid, true);
503 | }
504 | });
505 | });
506 | describe('creating an instance of the class with a number input', () => {
507 | beforeEach(() => {
508 | newObject = new SchemaString(3.1415)
509 | });
510 | it('will create an instance of the class', () => {
511 | assertInstanceOf(newObject, SchemaString)
512 | });
513 | it('will have a convertType method', () => {
514 | if (newObject instanceof SchemaString) {
515 | assertInstanceOf(newObject.convertType, Function);
516 | }
517 | });
518 | it('will cast the input and set property convertedValue to a string', () => {
519 | if (newObject instanceof SchemaString) {
520 | newObject.convertType();
521 | assertStrictEquals(typeof newObject.convertedValue, 'string');
522 | }
523 | });
524 | it('will set property valid to true after invoking validateType', () => {
525 | if (newObject instanceof SchemaString) {
526 | newObject.validateType();
527 | assertStrictEquals(newObject.valid, false);
528 | newObject.convertType()
529 | newObject.validateType();
530 | assertStrictEquals(newObject.valid, true);
531 | }
532 | });
533 | });
534 | describe('creating an instance of the class with a boolean input', () => {
535 | beforeEach(() => {
536 | newObject = new SchemaString(true)
537 | });
538 | it('will create an instance of the class', () => {
539 | assertInstanceOf(newObject, SchemaString)
540 | });
541 | it('will have a convertType method', () => {
542 | if (newObject instanceof SchemaString) {
543 | assertInstanceOf(newObject.convertType, Function);
544 | }
545 | });
546 | it('will cast the input and set property convertedValue to a string', () => {
547 | if (newObject instanceof SchemaString) {
548 | newObject.convertType();
549 | assertStrictEquals(typeof newObject.convertedValue, 'string');
550 | }
551 | });
552 | it('will cast the input and set property convertedValue to a value of "true"', () => {
553 | if (newObject instanceof SchemaString) {
554 | newObject.convertType();
555 | assertStrictEquals(newObject.convertedValue, 'true');
556 | }
557 | });
558 | it('will set property valid to true after invoking validateType', () => {
559 | if (newObject instanceof SchemaString) {
560 | newObject.validateType();
561 | assertStrictEquals(newObject.valid, false);
562 | newObject.convertType()
563 | newObject.validateType();
564 | assertStrictEquals(newObject.valid, true);
565 | }
566 | });
567 | });
568 | });
569 |
570 | describe('Schema Boolean', () => {
571 | let newObject: unknown
572 | describe('creating an instance of the class with no input', () => {
573 | it('will throw an error', () => {
574 | assertThrows(() => {
575 | //@ts-ignore Ignore TS warning to run test
576 | newObject = new SchemaBoolean();
577 | });
578 | });
579 | });
580 | describe('creating an instance of the class with a null input', () => {
581 | beforeEach(() => {
582 | newObject = new SchemaBoolean(null)
583 | });
584 | it('will create an instance of the class', () => {
585 | assertInstanceOf(newObject, SchemaBoolean)
586 | });
587 | it('will have a convertType method', () => {
588 | if (newObject instanceof SchemaBoolean) {
589 | assertInstanceOf(newObject.convertType, Function);
590 | }
591 | });
592 | it('will cast the input and set property convertedValue to null', () => {
593 | if (newObject instanceof SchemaBoolean) {
594 | newObject.convertType();
595 | assertStrictEquals(newObject.convertedValue, null);
596 | }
597 | });
598 | it('will set property valid to true after invoking validateType', () => {
599 | if (newObject instanceof SchemaBoolean) {
600 | newObject.validateType();
601 | assertStrictEquals(newObject.valid, false);
602 | newObject.convertType()
603 | newObject.validateType();
604 | assertStrictEquals(newObject.valid, true);
605 | }
606 | });
607 | });
608 | describe('creating an instance of the class with a Boolean object input', () => {
609 | beforeEach(() => {
610 | newObject = new SchemaBoolean(new Boolean(false));
611 | });
612 | it('will create an instance of the class', () => {
613 | assertInstanceOf(newObject, SchemaBoolean)
614 | });
615 | it('will have a convertType method', () => {
616 | if (newObject instanceof SchemaBoolean) {
617 | assertInstanceOf(newObject.convertType, Function);
618 | }
619 | });
620 | it('will cast the input and set property convertedValue to a boolean', () => {
621 | if (newObject instanceof SchemaBoolean) {
622 | newObject.convertType();
623 | assertInstanceOf(newObject.convertedValue, Boolean);
624 | }
625 | });
626 | it('will set property valid to true after invoking validateType', () => {
627 | if (newObject instanceof SchemaBoolean) {
628 | newObject.validateType();
629 | assertStrictEquals(newObject.valid, false);
630 | newObject.convertType()
631 | newObject.validateType();
632 | assertStrictEquals(newObject.valid, true);
633 | }
634 | });
635 | });
636 | describe('creating an instance of the class with a non-string or number or boolean input', () => {
637 | beforeEach(() => {
638 | newObject = new SchemaBoolean([true])
639 | });
640 | it('will create an instance of the class', () => {
641 | assertInstanceOf(newObject, SchemaBoolean)
642 | });
643 | it('will have a convertType method', () => {
644 | if (newObject instanceof SchemaBoolean) {
645 | assertInstanceOf(newObject.convertType, Function);
646 | }
647 | });
648 | it('will be unable cast the input and set property convertedValue to a boolean. Value will be undefined', () => {
649 | if (newObject instanceof SchemaBoolean) {
650 | newObject.convertType();
651 | assertStrictEquals(newObject.convertedValue, undefined);
652 | }
653 | });
654 | it('will set property valid to false after invoking validateType', () => {
655 | if (newObject instanceof SchemaBoolean) {
656 | newObject.validateType();
657 | assertStrictEquals(newObject.valid, false);
658 | newObject.convertType()
659 | newObject.validateType();
660 | assertStrictEquals(newObject.valid, false);
661 | }
662 | });
663 | });
664 | describe('creating an instance of the class with string input', () => {
665 | let newObject1: unknown;
666 | let newObject2: unknown;
667 | beforeEach(() => {
668 | newObject1 = new SchemaBoolean('true');
669 | newObject2 = new SchemaBoolean('false');
670 | });
671 | it('will create an instance of the class', () => {
672 | assertInstanceOf(newObject1, SchemaBoolean)
673 | });
674 | it('will have a convertType method', () => {
675 | if (newObject1 instanceof SchemaBoolean) {
676 | assertInstanceOf(newObject1.convertType, Function);
677 | }
678 | });
679 | it('will cast the input and set property convertedValue to a boolean of string value', () => {
680 | if (newObject1 instanceof SchemaBoolean) {
681 | newObject1.convertType();
682 | assertStrictEquals(typeof newObject1.convertedValue, 'boolean');
683 | assertStrictEquals(newObject1.convertedValue, true);
684 | }
685 | if (newObject2 instanceof SchemaBoolean) {
686 | newObject2.convertType();
687 | assertStrictEquals(typeof newObject2.convertedValue, 'boolean');
688 | assertStrictEquals(newObject2.convertedValue, false);
689 | }
690 | });
691 | it('will set property valid to true after invoking validateType', () => {
692 | if (newObject1 instanceof SchemaBoolean) {
693 | newObject1.validateType();
694 | assertStrictEquals(newObject1.valid, false);
695 | newObject1.convertType()
696 | newObject1.validateType();
697 | assertStrictEquals(newObject1.valid, true);
698 | }
699 | });
700 | });
701 | describe('creating an instance of the class with number input', () => {
702 | let newObject1: unknown;
703 | let newObject2: unknown;
704 | beforeEach(() => {
705 | newObject1 = new SchemaBoolean(1);
706 | newObject2 = new SchemaBoolean(0);
707 | });
708 | it('will create an instance of the class', () => {
709 | assertInstanceOf(newObject1, SchemaBoolean)
710 | });
711 | it('will have a convertType method', () => {
712 | if (newObject1 instanceof SchemaBoolean) {
713 | assertInstanceOf(newObject1.convertType, Function);
714 | }
715 | });
716 | it('will cast the input and set property convertedValue to a boolean of number value (1 = true, 0 = false)', () => {
717 | if (newObject1 instanceof SchemaBoolean) {
718 | newObject1.convertType();
719 | assertStrictEquals(typeof newObject1.convertedValue, 'boolean');
720 | assertStrictEquals(newObject1.convertedValue, true);
721 | }
722 | if (newObject2 instanceof SchemaBoolean) {
723 | newObject2.convertType();
724 | assertStrictEquals(typeof newObject2.convertedValue, 'boolean');
725 | assertStrictEquals(newObject2.convertedValue, false);
726 | }
727 | });
728 | it('will set property valid to true after invoking validateType', () => {
729 | if (newObject1 instanceof SchemaBoolean) {
730 | newObject1.validateType();
731 | assertStrictEquals(newObject1.valid, false);
732 | newObject1.convertType()
733 | newObject1.validateType();
734 | assertStrictEquals(newObject1.valid, true);
735 | }
736 | });
737 | });
738 | describe('creating an instance of the class with a boolean input', () => {
739 | beforeEach(() => {
740 | newObject = new SchemaBoolean(true)
741 | });
742 | it('will create an instance of the class', () => {
743 | assertInstanceOf(newObject, SchemaBoolean)
744 | });
745 | it('will have a convertType method', () => {
746 | if (newObject instanceof SchemaBoolean) {
747 | assertInstanceOf(newObject.convertType, Function);
748 | }
749 | });
750 | it('will cast the input and set property convertedValue to a boolean', () => {
751 | if (newObject instanceof SchemaBoolean) {
752 | newObject.convertType();
753 | assertStrictEquals(typeof newObject.convertedValue, 'boolean');
754 | assertStrictEquals(newObject.convertedValue, true);
755 | }
756 | });
757 | it('will set property valid to true after invoking validateType', () => {
758 | if (newObject instanceof SchemaBoolean) {
759 | newObject.validateType();
760 | assertStrictEquals(newObject.valid, false);
761 | newObject.convertType()
762 | newObject.validateType();
763 | assertStrictEquals(newObject.valid, true);
764 | }
765 | });
766 | });
767 | });
768 |
769 | describe('Schema ObjectId', () => {
770 | let newObject: unknown
771 | describe('creating an instance of the class with no input', () => {
772 | it('will throw an error', () => {
773 | assertThrows(() => {
774 | //@ts-ignore Ignore TS warning to run test
775 | newObject = new SchemaObjectId();
776 | });
777 | });
778 | });
779 | describe('creating an instance of the class with a null input', () => {
780 | beforeEach(() => {
781 | newObject = new SchemaObjectId(null)
782 | });
783 | it('will create an instance of the class', () => {
784 | assertInstanceOf(newObject, SchemaObjectId)
785 | });
786 | it('will have a convertType method', () => {
787 | if (newObject instanceof SchemaObjectId) {
788 | assertInstanceOf(newObject.convertType, Function);
789 | }
790 | });
791 | it('will cast the input and set property convertedValue to null', () => {
792 | if (newObject instanceof SchemaObjectId) {
793 | newObject.convertType();
794 | assertStrictEquals(newObject.convertedValue, null);
795 | }
796 | });
797 | it('will set property valid to true after invoking validateType', () => {
798 | if (newObject instanceof SchemaObjectId) {
799 | newObject.validateType();
800 | assertStrictEquals(newObject.valid, false);
801 | newObject.convertType()
802 | newObject.validateType();
803 | assertStrictEquals(newObject.valid, true);
804 | }
805 | });
806 | });
807 | describe('creating an instance of the class with a ObjectId input', () => {
808 | beforeEach(() => {
809 | newObject = new SchemaObjectId(new Bson.ObjectId('62743a2d6e85141671a45ea7'));
810 | });
811 | it('will create an instance of the class', () => {
812 | assertInstanceOf(newObject, SchemaObjectId)
813 | });
814 | it('will have a convertType method', () => {
815 | if (newObject instanceof SchemaObjectId) {
816 | assertInstanceOf(newObject.convertType, Function);
817 | }
818 | });
819 | it('will cast the input and set property convertedValue to an ObjectId', () => {
820 | if (newObject instanceof SchemaObjectId) {
821 | newObject.convertType();
822 | assertInstanceOf(newObject.convertedValue, Bson.ObjectId);
823 | }
824 | });
825 | it('will set property valid to true after invoking validateType', () => {
826 | if (newObject instanceof SchemaObjectId) {
827 | newObject.validateType();
828 | assertStrictEquals(newObject.valid, false);
829 | newObject.convertType()
830 | newObject.validateType();
831 | assertStrictEquals(newObject.valid, true);
832 | }
833 | });
834 | });
835 | describe('creating an invalid ObjectId', () => {
836 | beforeEach(() => {
837 | newObject = new SchemaObjectId('dangoDB')
838 | });
839 | it('will create an instance of the class', () => {
840 | assertInstanceOf(newObject, SchemaObjectId)
841 | });
842 | it('will have a convertType method', () => {
843 | if (newObject instanceof SchemaObjectId) {
844 | assertInstanceOf(newObject.convertType, Function);
845 | }
846 | });
847 | it('will be unable cast the input and set property convertedValue to an ObjectId. Value will be undefined', () => {
848 | if (newObject instanceof SchemaObjectId) {
849 | newObject.convertType();
850 | assertStrictEquals(newObject.convertedValue, undefined);
851 | }
852 | });
853 | it('will set property valid to false after invoking validateType', () => {
854 | if (newObject instanceof SchemaObjectId) {
855 | newObject.validateType();
856 | assertStrictEquals(newObject.valid, false);
857 | newObject.convertType()
858 | newObject.validateType();
859 | assertStrictEquals(newObject.valid, false);
860 | }
861 | });
862 | });
863 | describe('creating an instance of the class with string input that can be cast to an ObjectId', () => {
864 | beforeEach(() => {
865 | newObject = new SchemaObjectId('62743a2d6e85141671a45ea7')
866 | });
867 | it('will create an instance of the class', () => {
868 | assertInstanceOf(newObject, SchemaObjectId)
869 | });
870 | it('will have a convertType method', () => {
871 | if (newObject instanceof SchemaObjectId) {
872 | assertInstanceOf(newObject.convertType, Function);
873 | }
874 | });
875 | it('will be able to cast the input and set property convertedValue to an ObjectId', () => {
876 | if (newObject instanceof SchemaObjectId) {
877 | newObject.convertType();
878 | assertInstanceOf(newObject.convertedValue, Bson.ObjectId);
879 | }
880 | });
881 | it('will set property valid to true after invoking validateType', () => {
882 | if (newObject instanceof SchemaObjectId) {
883 | newObject.validateType();
884 | assertStrictEquals(newObject.valid, false);
885 | newObject.convertType()
886 | newObject.validateType();
887 | assertStrictEquals(newObject.valid, true);
888 | }
889 | });
890 | });
891 | });
892 |
893 | describe('Schema UUID', () => {
894 | let newObject: unknown
895 | describe('creating an instance of the class with no input', () => {
896 | it('will throw an error', () => {
897 | assertThrows(() => {
898 | //@ts-ignore Ignore TS warning to run test
899 | newObject = new SchemaUUID();
900 | });
901 | });
902 | });
903 | describe('creating an instance of the class with a null input', () => {
904 | beforeEach(() => {
905 | newObject = new SchemaUUID(null)
906 | });
907 | it('will create an instance of the class', () => {
908 | assertInstanceOf(newObject, SchemaUUID)
909 | });
910 | it('will have a convertType method', () => {
911 | if (newObject instanceof SchemaUUID) {
912 | assertInstanceOf(newObject.convertType, Function);
913 | }
914 | });
915 | it('will cast the input and set property convertedValue to null', () => {
916 | if (newObject instanceof SchemaUUID) {
917 | newObject.convertType();
918 | assertStrictEquals(newObject.convertedValue, null);
919 | }
920 | });
921 | it('will set property valid to true after invoking validateType', () => {
922 | if (newObject instanceof SchemaUUID) {
923 | newObject.validateType();
924 | assertStrictEquals(newObject.valid, false);
925 | newObject.convertType()
926 | newObject.validateType();
927 | assertStrictEquals(newObject.valid, true);
928 | }
929 | });
930 | });
931 | describe('creating an instance of the class with a UUID input', () => {
932 | beforeEach(() => {
933 | newObject = new SchemaUUID(new Bson.UUID());
934 | });
935 | it('will create an instance of the class', () => {
936 | assertInstanceOf(newObject, SchemaUUID)
937 | });
938 | it('will have a convertType method', () => {
939 | if (newObject instanceof SchemaUUID) {
940 | assertInstanceOf(newObject.convertType, Function);
941 | }
942 | });
943 | it('will cast the input and set property convertedValue to an UUID', () => {
944 | if (newObject instanceof SchemaUUID) {
945 | newObject.convertType();
946 | assertInstanceOf(newObject.convertedValue, Bson.UUID);
947 | }
948 | });
949 | it('will set property valid to true after invoking validateType', () => {
950 | if (newObject instanceof SchemaUUID) {
951 | newObject.validateType();
952 | assertStrictEquals(newObject.valid, false);
953 | newObject.convertType()
954 | newObject.validateType();
955 | assertStrictEquals(newObject.valid, true);
956 | }
957 | });
958 | });
959 | describe('creating an invalid UUID', () => {
960 | beforeEach(() => {
961 | newObject = new SchemaUUID('dangoDB')
962 | });
963 | it('will create an instance of the class', () => {
964 | assertInstanceOf(newObject, SchemaUUID)
965 | });
966 | it('will have a convertType method', () => {
967 | if (newObject instanceof SchemaUUID) {
968 | assertInstanceOf(newObject.convertType, Function);
969 | }
970 | });
971 | it('will be unable cast the input and set property convertedValue to an UUID. Value will be undefined', () => {
972 | if (newObject instanceof SchemaUUID) {
973 | newObject.convertType();
974 | assertStrictEquals(newObject.convertedValue, undefined);
975 | }
976 | });
977 | it('will set property valid to false after invoking validateType', () => {
978 | if (newObject instanceof SchemaUUID) {
979 | newObject.validateType();
980 | assertStrictEquals(newObject.valid, false);
981 | newObject.convertType()
982 | newObject.validateType();
983 | assertStrictEquals(newObject.valid, false);
984 | }
985 | });
986 | });
987 | describe('creating an instance of the class with string input that can be cast to a UUID', () => {
988 | let validUUID!: string
989 | beforeEach(() => {
990 | validUUID = new Bson.UUID().toString();
991 | newObject = new SchemaUUID(validUUID)
992 | });
993 | it('will create an instance of the class', () => {
994 | assertInstanceOf(newObject, SchemaUUID)
995 | });
996 | it('will have a convertType method', () => {
997 | if (newObject instanceof SchemaUUID) {
998 | assertInstanceOf(newObject.convertType, Function);
999 | }
1000 | });
1001 | it('will be able to cast the input and set property convertedValue to an UUID', () => {
1002 | if (newObject instanceof SchemaUUID) {
1003 | newObject.convertType();
1004 | assertInstanceOf(newObject.convertedValue, Bson.UUID);
1005 | }
1006 | });
1007 | it('will set property valid to true after invoking validateType', () => {
1008 | if (newObject instanceof SchemaUUID) {
1009 | newObject.validateType();
1010 | assertStrictEquals(newObject.valid, false);
1011 | newObject.convertType()
1012 | newObject.validateType();
1013 | assertStrictEquals(newObject.valid, true);
1014 | }
1015 | });
1016 | });
1017 | });
1018 |
1019 | describe('Schema Date', () => {
1020 | let newObject: unknown
1021 | describe('creating an instance of the class with no input', () => {
1022 | it('will throw an error', () => {
1023 | assertThrows(() => {
1024 | //@ts-ignore Ignore TS warning to run test
1025 | newObject = new SchemaDate();
1026 | });
1027 | });
1028 | });
1029 | describe('creating an instance of the class with a null input', () => {
1030 | beforeEach(() => {
1031 | newObject = new SchemaDate(null)
1032 | });
1033 | it('will create an instance of the class', () => {
1034 | assertInstanceOf(newObject, SchemaDate)
1035 | });
1036 | it('will have a convertType method', () => {
1037 | if (newObject instanceof SchemaDate) {
1038 | assertInstanceOf(newObject.convertType, Function);
1039 | }
1040 | });
1041 | it('will cast the input and set property convertedValue to null', () => {
1042 | if (newObject instanceof SchemaDate) {
1043 | newObject.convertType();
1044 | assertStrictEquals(newObject.convertedValue, null);
1045 | }
1046 | });
1047 | it('will set property valid to true after invoking validateType', () => {
1048 | if (newObject instanceof SchemaNumber) {
1049 | newObject.validateType();
1050 | assertStrictEquals(newObject.valid, false);
1051 | newObject.convertType()
1052 | newObject.validateType();
1053 | assertStrictEquals(newObject.valid, true);
1054 | }
1055 | });
1056 | });
1057 | describe('creating an instance of the class with a Date object input', () => {
1058 | beforeEach(() => {
1059 | newObject = new SchemaDate(new Date())
1060 | });
1061 | it('will create an instance of the class', () => {
1062 | assertInstanceOf(newObject, SchemaDate)
1063 | });
1064 | it('will have a convertType method', () => {
1065 | if (newObject instanceof SchemaDate) {
1066 | assertInstanceOf(newObject.convertType, Function);
1067 | }
1068 | });
1069 | it('will cast the input and set property convertedValue to a Date', () => {
1070 | if (newObject instanceof SchemaDate) {
1071 | newObject.convertType();
1072 | assertInstanceOf(newObject.convertedValue, Date);
1073 | }
1074 | });
1075 | it('will set property valid to true after invoking validateType', () => {
1076 | if (newObject instanceof SchemaDate) {
1077 | newObject.validateType();
1078 | assertStrictEquals(newObject.valid, false);
1079 | newObject.convertType()
1080 | newObject.validateType();
1081 | assertStrictEquals(newObject.valid, true);
1082 | }
1083 | });
1084 | });
1085 | describe('creating an instance of the class with a non-string or number input', () => {
1086 | beforeEach(() => {
1087 | newObject = new SchemaDate([5])
1088 | });
1089 | it('will create an instance of the class', () => {
1090 | assertInstanceOf(newObject, SchemaDate)
1091 | });
1092 | it('will have a convertType method', () => {
1093 | if (newObject instanceof SchemaDate) {
1094 | assertInstanceOf(newObject.convertType, Function);
1095 | }
1096 | });
1097 | it('will be unable cast the input and set property convertedValue to a Date. Value will be undefined', () => {
1098 | if (newObject instanceof SchemaDate) {
1099 | newObject.convertType();
1100 | assertStrictEquals(newObject.convertedValue, undefined);
1101 | }
1102 | });
1103 | it('will set property valid to false after invoking validateType', () => {
1104 | if (newObject instanceof SchemaDate) {
1105 | newObject.validateType();
1106 | assertStrictEquals(newObject.valid, false);
1107 | newObject.convertType()
1108 | newObject.validateType();
1109 | assertStrictEquals(newObject.valid, false);
1110 | }
1111 | });
1112 | });
1113 | describe('creating an instance of the class with string input that cannot be cast to a date', () => {
1114 | beforeEach(() => {
1115 | newObject = new SchemaDate('dangoDB')
1116 | });
1117 | it('will create an instance of the class', () => {
1118 | assertInstanceOf(newObject, SchemaDate)
1119 | });
1120 | it('will have a convertType method', () => {
1121 | if (newObject instanceof SchemaDate) {
1122 | assertInstanceOf(newObject.convertType, Function);
1123 | }
1124 | });
1125 | it('will be unable cast the input and set property convertedValue to a Date. Value will be undefined', () => {
1126 | if (newObject instanceof SchemaDate) {
1127 | newObject.convertType();
1128 | assertStrictEquals(newObject.convertedValue, undefined);
1129 | }
1130 | });
1131 | it('will set property valid to false after invoking validateType', () => {
1132 | if (newObject instanceof SchemaDate) {
1133 | newObject.validateType();
1134 | assertStrictEquals(newObject.valid, false);
1135 | newObject.convertType()
1136 | newObject.validateType();
1137 | assertStrictEquals(newObject.valid, false);
1138 | }
1139 | });
1140 | });
1141 | describe('creating an instance of the class with string input that can be cast to a date', () => {
1142 | beforeEach(() => {
1143 | newObject = new SchemaDate('1991-03-15')
1144 | });
1145 | it('will create an instance of the class', () => {
1146 | assertInstanceOf(newObject, SchemaDate)
1147 | });
1148 | it('will have a convertType method', () => {
1149 | if (newObject instanceof SchemaDate) {
1150 | assertInstanceOf(newObject.convertType, Function);
1151 | }
1152 | });
1153 | it('will cast the input and set property convertedValue to a Dated', () => {
1154 | if (newObject instanceof SchemaDate) {
1155 | newObject.convertType();
1156 | assertInstanceOf(newObject.convertedValue, Date);
1157 | }
1158 | });
1159 | it('will set property valid to true after invoking validateType', () => {
1160 | if (newObject instanceof SchemaDate) {
1161 | newObject.validateType();
1162 | assertStrictEquals(newObject.valid, false);
1163 | newObject.convertType()
1164 | newObject.validateType();
1165 | assertStrictEquals(newObject.valid, true);
1166 | }
1167 | });
1168 | });
1169 | describe('creating an instance of the class with a number input', () => {
1170 | beforeEach(() => {
1171 | newObject = new SchemaDate(668995200000)
1172 | });
1173 | it('will create an instance of the class', () => {
1174 | assertInstanceOf(newObject, SchemaDate)
1175 | });
1176 | it('will have a convertType method', () => {
1177 | if (newObject instanceof SchemaDate) {
1178 | assertInstanceOf(newObject.convertType, Function);
1179 | }
1180 | });
1181 | it('will cast the input and set property convertedValue to a Date', () => {
1182 | if (newObject instanceof SchemaDate) {
1183 | newObject.convertType();
1184 | assertInstanceOf(newObject.convertedValue, Date);
1185 | }
1186 | });
1187 | it('will set property valid to true after invoking validateType', () => {
1188 | if (newObject instanceof SchemaDate) {
1189 | newObject.validateType();
1190 | assertStrictEquals(newObject.valid, false);
1191 | newObject.convertType()
1192 | newObject.validateType();
1193 | assertStrictEquals(newObject.valid, true);
1194 | }
1195 | });
1196 | });
1197 | });
--------------------------------------------------------------------------------
/tests/test_query.ts:
--------------------------------------------------------------------------------
1 | import {
2 | assertInstanceOf,
3 | assertStrictEquals,
4 | assertThrows,
5 | beforeEach,
6 | describe,
7 | it,
8 | } from '../deps.ts';
9 |
10 | // import {
11 | // Query
12 | // } from '../lib/query.ts';
13 |
14 | import { model } from '../lib/model.ts';
15 |
16 | import { Bson } from '../deps.ts';
17 |
18 | import { Schema, SchemaOptions } from '../lib/schema.ts';
19 |
20 | import { dango } from '../lib/dango.ts';
21 |
22 | describe('test Query methods', () => {
23 | const UserSchema = {
24 | name: {
25 | type: 'string',
26 | required: true,
27 | },
28 | occupation: {
29 | type: 'string',
30 | required: false,
31 | default: null,
32 | },
33 | age: {
34 | type: 'number',
35 | required: false,
36 | default: null,
37 | },
38 | };
39 |
40 | const collectionName = 'testCollection';
41 | const testSchema = dango.schema(UserSchema);
42 | // console.log('testSchema: ', testSchema);
43 | let queryObject: Record;
44 |
45 | const query = dango.model(collectionName, testSchema);
46 |
47 | describe('checkDataFields', () => {
48 | // let queryObject: Record;
49 | let schemaMap: Record;
50 |
51 | schemaMap = testSchema.schemaMap;
52 | queryObject = { name: 'Mr. A', age: 25 };
53 | // console.log('queryObject: ', queryObject);
54 | it('it will throw an error if extra properties are present in the queryObject', () => {
55 | queryObject = { name: 'Mr. A', school: 'Furinkan HS' };
56 | assertThrows(() => query.checkDataFields(queryObject, schemaMap));
57 | });
58 |
59 | it('it will return true if only fields specified in the schema are present in the queryObject', () => {
60 | queryObject = { name: 'Mr. A' };
61 | assertStrictEquals(query.checkDataFields(queryObject, schemaMap), true);
62 | });
63 | });
64 |
65 | describe('checkRequired', () => {
66 | let propertyName: string;
67 | let propertyOptions: SchemaOptions;
68 |
69 | beforeEach(() => {
70 | propertyName = 'name';
71 | propertyOptions = testSchema.schemaMap[propertyName];
72 | });
73 |
74 | it('will throw an error when a required property is missing from queryObject', () => {
75 | queryObject = { age: 25 };
76 | assertThrows(() => {
77 | query.checkRequired(queryObject, propertyName, propertyOptions);
78 | });
79 | });
80 | it('will return true when required properties are in queryObject', () => {
81 | queryObject = { name: 'Mr. C' };
82 | assertStrictEquals(
83 | query.checkRequired(queryObject, propertyName, propertyOptions),
84 | true
85 | );
86 | });
87 | });
88 |
89 | describe('setDefault', () => {
90 | let propertyName: string;
91 | let propertyOptions: SchemaOptions;
92 | beforeEach(() => {
93 | propertyName = 'occupation';
94 | propertyOptions = testSchema.schemaMap[propertyName];
95 | // queryObject = { name: 'Mr. D' };
96 | });
97 |
98 | it('will set property value in queryObject to default if value not assigned', () => {
99 | queryObject = { name: 'Mr. D' };
100 | query.setDefault(queryObject, propertyName, propertyOptions);
101 | assertStrictEquals(queryObject[propertyName], null);
102 | });
103 |
104 | it('will not assign a default value to a property in queryObject if it already has an assigned value', () => {
105 | queryObject = { name: 'Mr. D', occupation: 'baker' };
106 | query.setDefault(queryObject, propertyName, propertyOptions);
107 | assertStrictEquals(queryObject[propertyName], 'baker');
108 | });
109 | });
110 |
111 | describe('populateQuery', () => {
112 | let propertyName: string;
113 | let propertyOptions: SchemaOptions;
114 | let updatedQueryObject: Record = {};
115 |
116 | it('will assign converted value (STRING) to updatedQueryObject', () => {
117 | queryObject = { name: 'Mick Jagger' };
118 | propertyName = 'name';
119 | propertyOptions = testSchema.schemaMap[propertyName];
120 | query.populateQuery(
121 | queryObject,
122 | propertyName,
123 | propertyOptions,
124 | updatedQueryObject
125 | );
126 | assertStrictEquals(
127 | updatedQueryObject[propertyName],
128 | queryObject[propertyName]
129 | );
130 | });
131 |
132 | it('will convert a number to a Bson.Double and assign it to updatedQueryObject', () => {
133 | queryObject = { age: 25 };
134 | propertyName = 'age';
135 | propertyOptions = testSchema.schemaMap[propertyName];
136 | query.populateQuery(
137 | queryObject,
138 | propertyName,
139 | propertyOptions,
140 | updatedQueryObject
141 | );
142 | assertInstanceOf(updatedQueryObject[propertyName], Bson.Double);
143 | });
144 |
145 | it('will throw an error if assigned value in queryObject cannot be converted to specified type in schema', () => {
146 | queryObject = { age: true };
147 | propertyName = 'age';
148 | propertyOptions = testSchema.schemaMap[propertyName];
149 | assertThrows(() => {
150 | query.populateQuery(
151 | queryObject,
152 | propertyName,
153 | propertyOptions,
154 | updatedQueryObject
155 | );
156 | });
157 | });
158 | });
159 |
160 | // need to figure out how to mock or craete a fake evalauted result from invoking findOne query
161 | // describe('checkUnique', () => {
162 | // let propertyName: string;
163 | // let propertyOptions: SchemaOptions;
164 | // let updatedQueryObject: Record = {};
165 | // let embeddedUniqueProperty: string[];
166 | // })
167 |
168 | // checkUniqueForReplace
169 |
170 | describe('checkConstraints', () => {
171 | let propertyName: string;
172 | let propertyOptions: SchemaOptions;
173 |
174 | it('will return true if validator property is set to null', () => {
175 | propertyName = 'name';
176 | propertyOptions = testSchema.schemaMap[propertyName];
177 | assertStrictEquals(
178 | query.checkConstraints(propertyName, propertyOptions),
179 | true
180 | );
181 | });
182 | });
183 |
184 | describe('resetQueryObject', () => {
185 | query.updatedQueryObject = { test: 'a test' };
186 | query.resetQueryObject();
187 | const updatedQueryObjectKeys = Object.keys(query.updatedQueryObject);
188 | it('will reset updatedQuery object', () => {
189 | assertStrictEquals(updatedQueryObjectKeys.length, 0);
190 | });
191 | });
192 |
193 | describe('formatQueryString result', () => {
194 | let propertyName: string;
195 | let propArray: string[];
196 |
197 | beforeEach(() => {
198 | propertyName = 'test';
199 | propArray = ['address', 'property_id'];
200 | });
201 |
202 | it('will return propertyName when embeddedUnique is empty', () => {
203 | assertStrictEquals(
204 | query.formatQueryField(propertyName, []),
205 | propertyName
206 | );
207 | });
208 | it('will return concatenated string using elements from embeddedUniqeProperty and propertyName', () => {
209 | assertStrictEquals(
210 | query.formatQueryField(propertyName, propArray),
211 | `address.property_id.${propertyName}`
212 | );
213 | });
214 | });
215 | });
216 |
--------------------------------------------------------------------------------
/tests/test_schema.ts:
--------------------------------------------------------------------------------
1 | import {
2 | assertInstanceOf,
3 | assertStrictEquals,
4 | assertThrows,
5 | beforeEach,
6 | describe,
7 | it,
8 | } from '../deps.ts';
9 |
10 | import { Schema, SchemaOptions } from '../lib/schema.ts';
11 |
12 | import {
13 | SchemaNumber,
14 | SchemaDecimal128,
15 | SchemaString,
16 | SchemaBoolean,
17 | SchemaObjectId,
18 | SchemaUUID,
19 | SchemaDate,
20 | } from '../lib/datatypes.ts';
21 |
22 | import {
23 | spy,
24 | assertSpyCall,
25 | assertSpyCalls,
26 | } from 'https://deno.land/std@0.139.0/testing/mock.ts';
27 |
28 | describe('SchemaOptions Class', () => {
29 | let newObject: unknown;
30 | describe('creating an instance of the class with no input', () => {
31 | it('will throw an error', () => {
32 | assertThrows(() => {
33 | //@ts-ignore Ignore TS warning to run test
34 | newObject = new SchemaOptions();
35 | });
36 | });
37 | });
38 | describe('creating an instance of the class with no type property', () => {
39 | it('will throw an error', () => {
40 | assertThrows(() => {
41 | //@ts-ignore Ignore TS warning to run test
42 | newObject = new SchemaOptions({
43 | required: true,
44 | unique: true,
45 | });
46 | });
47 | });
48 | });
49 | describe('creating an instance of the class with an invalid type property', () => {
50 | it('will throw an error', () => {
51 | assertThrows(() => {
52 | //@ts-ignore Ignore TS warning to run test
53 | newObject = new SchemaOptions({
54 | type: 'dangoDB',
55 | });
56 | });
57 | });
58 | });
59 | describe('creating an instance of the class with a valid options object', () => {
60 | describe('the only property is type', () => {
61 | describe('the type is number', () => {
62 | beforeEach(() => {
63 | newObject = new SchemaOptions({
64 | type: 'number',
65 | });
66 | });
67 | it('will create an instance of the class', () => {
68 | assertInstanceOf(newObject, SchemaOptions);
69 | });
70 | it('will have assign the type property the class definition of the data type', () => {
71 | if (newObject instanceof SchemaOptions) {
72 | assertStrictEquals(newObject.type, SchemaNumber);
73 | }
74 | });
75 | it('will have a default required property with the value of false', () => {
76 | if (newObject instanceof SchemaOptions) {
77 | assertStrictEquals(newObject.required, false);
78 | }
79 | });
80 | it('will have a default unique property with the value of false', () => {
81 | if (newObject instanceof SchemaOptions) {
82 | assertStrictEquals(newObject.unique, false);
83 | }
84 | });
85 | it('will have a default default property with the value of null', () => {
86 | if (newObject instanceof SchemaOptions) {
87 | assertStrictEquals(newObject.default, null);
88 | }
89 | });
90 | it('will have a default validator property with the value of null', () => {
91 | if (newObject instanceof SchemaOptions) {
92 | assertStrictEquals(newObject.validator, null);
93 | }
94 | });
95 | });
96 | describe('the type is decimal128', () => {
97 | beforeEach(() => {
98 | newObject = new SchemaOptions({
99 | type: 'decimal128',
100 | });
101 | });
102 | it('will create an instance of the class', () => {
103 | assertInstanceOf(newObject, SchemaOptions);
104 | });
105 | it('will have assign the type property the class definition of the data type', () => {
106 | if (newObject instanceof SchemaOptions) {
107 | assertStrictEquals(newObject.type, SchemaDecimal128);
108 | }
109 | });
110 | it('will have a default required property with the value of false', () => {
111 | if (newObject instanceof SchemaOptions) {
112 | assertStrictEquals(newObject.required, false);
113 | }
114 | });
115 | it('will have a default unique property with the value of false', () => {
116 | if (newObject instanceof SchemaOptions) {
117 | assertStrictEquals(newObject.unique, false);
118 | }
119 | });
120 | it('will have a default default property with the value of null', () => {
121 | if (newObject instanceof SchemaOptions) {
122 | assertStrictEquals(newObject.default, null);
123 | }
124 | });
125 | it('will have a default validator property with the value of null', () => {
126 | if (newObject instanceof SchemaOptions) {
127 | assertStrictEquals(newObject.validator, null);
128 | }
129 | });
130 | });
131 | describe('the type is string', () => {
132 | beforeEach(() => {
133 | newObject = new SchemaOptions({
134 | type: 'string',
135 | });
136 | });
137 | it('will create an instance of the class', () => {
138 | assertInstanceOf(newObject, SchemaOptions);
139 | });
140 | it('will have assign the type property the class definition of the data type', () => {
141 | if (newObject instanceof SchemaOptions) {
142 | assertStrictEquals(newObject.type, SchemaString);
143 | }
144 | });
145 | it('will have a default required property with the value of false', () => {
146 | if (newObject instanceof SchemaOptions) {
147 | assertStrictEquals(newObject.required, false);
148 | }
149 | });
150 | it('will have a default unique property with the value of false', () => {
151 | if (newObject instanceof SchemaOptions) {
152 | assertStrictEquals(newObject.unique, false);
153 | }
154 | });
155 | it('will have a default default property with the value of null', () => {
156 | if (newObject instanceof SchemaOptions) {
157 | assertStrictEquals(newObject.default, null);
158 | }
159 | });
160 | it('will have a default validator property with the value of null', () => {
161 | if (newObject instanceof SchemaOptions) {
162 | assertStrictEquals(newObject.validator, null);
163 | }
164 | });
165 | });
166 | describe('the type is boolean', () => {
167 | beforeEach(() => {
168 | newObject = new SchemaOptions({
169 | type: 'boolean',
170 | });
171 | });
172 | it('will create an instance of the class', () => {
173 | assertInstanceOf(newObject, SchemaOptions);
174 | });
175 | it('will have assign the type property the class definition of the data type', () => {
176 | if (newObject instanceof SchemaOptions) {
177 | assertStrictEquals(newObject.type, SchemaBoolean);
178 | }
179 | });
180 | it('will have a default required property with the value of false', () => {
181 | if (newObject instanceof SchemaOptions) {
182 | assertStrictEquals(newObject.required, false);
183 | }
184 | });
185 | it('will have a default unique property with the value of false', () => {
186 | if (newObject instanceof SchemaOptions) {
187 | assertStrictEquals(newObject.unique, false);
188 | }
189 | });
190 | it('will have a default default property with the value of null', () => {
191 | if (newObject instanceof SchemaOptions) {
192 | assertStrictEquals(newObject.default, null);
193 | }
194 | });
195 | it('will have a default validator property with the value of null', () => {
196 | if (newObject instanceof SchemaOptions) {
197 | assertStrictEquals(newObject.validator, null);
198 | }
199 | });
200 | });
201 | describe('the type is objectid', () => {
202 | beforeEach(() => {
203 | newObject = new SchemaOptions({
204 | type: 'objectid',
205 | });
206 | });
207 | it('will create an instance of the class', () => {
208 | assertInstanceOf(newObject, SchemaOptions);
209 | });
210 | it('will have assign the type property the class definition of the data type', () => {
211 | if (newObject instanceof SchemaOptions) {
212 | assertStrictEquals(newObject.type, SchemaObjectId);
213 | }
214 | });
215 | it('will have a default required property with the value of false', () => {
216 | if (newObject instanceof SchemaOptions) {
217 | assertStrictEquals(newObject.required, false);
218 | }
219 | });
220 | it('will have a default unique property with the value of false', () => {
221 | if (newObject instanceof SchemaOptions) {
222 | assertStrictEquals(newObject.unique, false);
223 | }
224 | });
225 | it('will have a default default property with the value of null', () => {
226 | if (newObject instanceof SchemaOptions) {
227 | assertStrictEquals(newObject.default, null);
228 | }
229 | });
230 | it('will have a default validator property with the value of null', () => {
231 | if (newObject instanceof SchemaOptions) {
232 | assertStrictEquals(newObject.validator, null);
233 | }
234 | });
235 | });
236 | describe('the type is objectid', () => {
237 | beforeEach(() => {
238 | newObject = new SchemaOptions({
239 | type: 'UUID',
240 | });
241 | });
242 | it('will create an instance of the class', () => {
243 | assertInstanceOf(newObject, SchemaOptions);
244 | });
245 | it('will have assign the type property the class definition of the data type', () => {
246 | if (newObject instanceof SchemaOptions) {
247 | assertStrictEquals(newObject.type, SchemaUUID);
248 | }
249 | });
250 | it('will have a default required property with the value of false', () => {
251 | if (newObject instanceof SchemaOptions) {
252 | assertStrictEquals(newObject.required, false);
253 | }
254 | });
255 | it('will have a default unique property with the value of false', () => {
256 | if (newObject instanceof SchemaOptions) {
257 | assertStrictEquals(newObject.unique, false);
258 | }
259 | });
260 | it('will have a default default property with the value of null', () => {
261 | if (newObject instanceof SchemaOptions) {
262 | assertStrictEquals(newObject.default, null);
263 | }
264 | });
265 | it('will have a default validator property with the value of null', () => {
266 | if (newObject instanceof SchemaOptions) {
267 | assertStrictEquals(newObject.validator, null);
268 | }
269 | });
270 | });
271 | describe('the type is date', () => {
272 | beforeEach(() => {
273 | newObject = new SchemaOptions({
274 | type: 'date',
275 | });
276 | });
277 | it('will create an instance of the class', () => {
278 | assertInstanceOf(newObject, SchemaOptions);
279 | });
280 | it('will have assign the type property the class definition of the data type', () => {
281 | if (newObject instanceof SchemaOptions) {
282 | assertStrictEquals(newObject.type, SchemaDate);
283 | }
284 | });
285 | it('will have a default required property with the value of false', () => {
286 | if (newObject instanceof SchemaOptions) {
287 | assertStrictEquals(newObject.required, false);
288 | }
289 | });
290 | it('will have a default unique property with the value of false', () => {
291 | if (newObject instanceof SchemaOptions) {
292 | assertStrictEquals(newObject.unique, false);
293 | }
294 | });
295 | it('will have a default default property with the value of null', () => {
296 | if (newObject instanceof SchemaOptions) {
297 | assertStrictEquals(newObject.default, null);
298 | }
299 | });
300 | it('will have a default validator property with the value of null', () => {
301 | if (newObject instanceof SchemaOptions) {
302 | assertStrictEquals(newObject.validator, null);
303 | }
304 | });
305 | });
306 | });
307 | describe('the only properties are type and required', () => {
308 | beforeEach(() => {
309 | newObject = new SchemaOptions({
310 | type: 'number',
311 | required: true,
312 | });
313 | });
314 | it('will create an instance of the class', () => {
315 | assertInstanceOf(newObject, SchemaOptions);
316 | });
317 | it('will have assign the type property the class definition of the data type', () => {
318 | if (newObject instanceof SchemaOptions) {
319 | assertStrictEquals(newObject.type, SchemaNumber);
320 | }
321 | });
322 | it('will have a required property with the value of true', () => {
323 | if (newObject instanceof SchemaOptions) {
324 | assertStrictEquals(newObject.required, true);
325 | }
326 | });
327 | it('will have a default unique property with the value of false', () => {
328 | if (newObject instanceof SchemaOptions) {
329 | assertStrictEquals(newObject.unique, false);
330 | }
331 | });
332 | it('will have a default default property with the value of null', () => {
333 | if (newObject instanceof SchemaOptions) {
334 | assertStrictEquals(newObject.default, null);
335 | }
336 | });
337 | it('will have a default validator property with the value of null', () => {
338 | if (newObject instanceof SchemaOptions) {
339 | assertStrictEquals(newObject.validator, null);
340 | }
341 | });
342 | });
343 | describe('the only properties are type and unique', () => {
344 | beforeEach(() => {
345 | newObject = new SchemaOptions({
346 | type: 'number',
347 | unique: true,
348 | });
349 | });
350 | it('will create an instance of the class', () => {
351 | assertInstanceOf(newObject, SchemaOptions);
352 | });
353 | it('will have assign the type property the class definition of the data type', () => {
354 | if (newObject instanceof SchemaOptions) {
355 | assertStrictEquals(newObject.type, SchemaNumber);
356 | }
357 | });
358 | it('will have a default required property with the value of false', () => {
359 | if (newObject instanceof SchemaOptions) {
360 | assertStrictEquals(newObject.required, false);
361 | }
362 | });
363 | it('will have a unique property with the value of true', () => {
364 | if (newObject instanceof SchemaOptions) {
365 | assertStrictEquals(newObject.unique, true);
366 | }
367 | });
368 | it('will have a default default property with the value of null', () => {
369 | if (newObject instanceof SchemaOptions) {
370 | assertStrictEquals(newObject.default, null);
371 | }
372 | });
373 | it('will have a default validator property with the value of null', () => {
374 | if (newObject instanceof SchemaOptions) {
375 | assertStrictEquals(newObject.validator, null);
376 | }
377 | });
378 | });
379 | describe('the only properties are type and default', () => {
380 | beforeEach(() => {
381 | newObject = new SchemaOptions({
382 | type: 'number',
383 | default: 54,
384 | });
385 | });
386 | it('will create an instance of the class', () => {
387 | assertInstanceOf(newObject, SchemaOptions);
388 | });
389 | it('will have assign the type property the class definition of the data type', () => {
390 | if (newObject instanceof SchemaOptions) {
391 | assertStrictEquals(newObject.type, SchemaNumber);
392 | }
393 | });
394 | it('will have a default required property with the value of false', () => {
395 | if (newObject instanceof SchemaOptions) {
396 | assertStrictEquals(newObject.required, false);
397 | }
398 | });
399 | it('will have a default unique property with the value of false', () => {
400 | if (newObject instanceof SchemaOptions) {
401 | assertStrictEquals(newObject.unique, false);
402 | }
403 | });
404 | it('will have a default property with the value of 54', () => {
405 | if (newObject instanceof SchemaOptions) {
406 | assertStrictEquals(newObject.default, 54);
407 | }
408 | });
409 | it('will have a default validator property with the value of null', () => {
410 | if (newObject instanceof SchemaOptions) {
411 | assertStrictEquals(newObject.validator, null);
412 | }
413 | });
414 | });
415 | describe('the only properties are type and validator', () => {
416 | let testFunc!: Function;
417 | beforeEach(() => {
418 | testFunc = (num: number): boolean => num > 5;
419 | newObject = new SchemaOptions({
420 | type: 'number',
421 | validator: testFunc,
422 | });
423 | });
424 | it('will create an instance of the class', () => {
425 | assertInstanceOf(newObject, SchemaOptions);
426 | });
427 | it('will have assign the type property the class definition of the data type', () => {
428 | if (newObject instanceof SchemaOptions) {
429 | assertStrictEquals(newObject.type, SchemaNumber);
430 | }
431 | });
432 | it('will have a default required property with the value of false', () => {
433 | if (newObject instanceof SchemaOptions) {
434 | assertStrictEquals(newObject.required, false);
435 | }
436 | });
437 | it('will have a default unique property with the value of false', () => {
438 | if (newObject instanceof SchemaOptions) {
439 | assertStrictEquals(newObject.unique, false);
440 | }
441 | });
442 | it('will have a default default property with the value of null', () => {
443 | if (newObject instanceof SchemaOptions) {
444 | assertStrictEquals(newObject.default, null);
445 | }
446 | });
447 | it('will have a validator property with the value of testFunc', () => {
448 | if (newObject instanceof SchemaOptions) {
449 | assertStrictEquals(newObject.validator, testFunc);
450 | }
451 | });
452 | });
453 | describe('all options properties are provided to SchemaOptions', () => {
454 | let testFunc!: Function;
455 | beforeEach(() => {
456 | testFunc = (num: number): boolean => num > 5;
457 | newObject = new SchemaOptions({
458 | type: 'number',
459 | required: true,
460 | unique: true,
461 | default: 315,
462 | validator: testFunc,
463 | });
464 | });
465 | it('will create an instance of the class', () => {
466 | assertInstanceOf(newObject, SchemaOptions);
467 | });
468 | it('will have assign the type property the class definition of the data type', () => {
469 | if (newObject instanceof SchemaOptions) {
470 | assertStrictEquals(newObject.type, SchemaNumber);
471 | }
472 | });
473 | it('will have a required property with the value of true', () => {
474 | if (newObject instanceof SchemaOptions) {
475 | assertStrictEquals(newObject.required, true);
476 | }
477 | });
478 | it('will have a unique property with the value of true', () => {
479 | if (newObject instanceof SchemaOptions) {
480 | assertStrictEquals(newObject.unique, true);
481 | }
482 | });
483 | it('will have a default property with the value of 315', () => {
484 | if (newObject instanceof SchemaOptions) {
485 | assertStrictEquals(newObject.default, 315);
486 | }
487 | });
488 | it('will have a validator property with the value of testFunc', () => {
489 | if (newObject instanceof SchemaOptions) {
490 | assertStrictEquals(newObject.validator, testFunc);
491 | }
492 | });
493 | });
494 | describe('an unknown options property was provided to SchemaOptions', () => {
495 | let testFunc!: Function;
496 | beforeEach(() => {
497 | testFunc = (num: number): boolean => num > 5;
498 | newObject = new SchemaOptions({
499 | type: 'number',
500 | required: true,
501 | unique: true,
502 | default: 315,
503 | validator: testFunc,
504 | //@ts-ignore Ignore TS warning to run test
505 | unknownProp: false,
506 | });
507 | });
508 | it('will create an instance of the class', () => {
509 | assertInstanceOf(newObject, SchemaOptions);
510 | });
511 | it('will have assign the type property the class definition of the data type', () => {
512 | if (newObject instanceof SchemaOptions) {
513 | assertStrictEquals(newObject.type, SchemaNumber);
514 | }
515 | });
516 | it('will have a required property with the value of true', () => {
517 | if (newObject instanceof SchemaOptions) {
518 | assertStrictEquals(newObject.required, true);
519 | }
520 | });
521 | it('will have a unique property with the value of true', () => {
522 | if (newObject instanceof SchemaOptions) {
523 | assertStrictEquals(newObject.unique, true);
524 | }
525 | });
526 | it('will have a default property with the value of 315', () => {
527 | if (newObject instanceof SchemaOptions) {
528 | assertStrictEquals(newObject.default, 315);
529 | }
530 | });
531 | it('will have a validator property with the value of testFunc', () => {
532 | if (newObject instanceof SchemaOptions) {
533 | assertStrictEquals(newObject.validator, testFunc);
534 | }
535 | });
536 | it('will not have unknownProp as a property', () => {
537 | if (newObject instanceof SchemaOptions) {
538 | assertStrictEquals(
539 | Object.prototype.hasOwnProperty.call(newObject, 'unknownProp'),
540 | false
541 | );
542 | }
543 | });
544 | });
545 | });
546 | });
547 |
548 | describe('Schema Class', () => {
549 | let newObject: unknown;
550 | describe('creating an instance of the class with no input', () => {
551 | it('will throw an error', () => {
552 | assertThrows(() => {
553 | //@ts-ignore Ignore TS warning to run test
554 | newObject = new Schema();
555 | });
556 | });
557 | });
558 | describe('creating an instance of the class with a schema object', () => {
559 | describe('and the schema object only has one property with only type defined with an invalid type', () => {
560 | it('will throw an error', () => {
561 | assertThrows(() => {
562 | newObject = new Schema({
563 | name: 'dangoDB',
564 | });
565 | });
566 | });
567 | });
568 | describe('and the schema object only has one property with only type defined with a valid type', () => {
569 | beforeEach(() => {
570 | newObject = new Schema({
571 | name: 'string',
572 | });
573 | });
574 | it('will create an instance of the class', () => {
575 | assertInstanceOf(newObject, Schema);
576 | });
577 | it('will have a property schemaMap', () => {
578 | if (newObject instanceof Schema) {
579 | assertStrictEquals(
580 | Object.prototype.hasOwnProperty.call(newObject, 'schemaMap'),
581 | true
582 | );
583 | }
584 | });
585 | it('will have a property schemaMap with the property name', () => {
586 | if (newObject instanceof Schema) {
587 | assertStrictEquals(
588 | Object.prototype.hasOwnProperty.call(newObject.schemaMap, 'name'),
589 | true
590 | );
591 | }
592 | });
593 | it('will have a property schemaMap with the property name which is an instance of SchemaOptions', () => {
594 | if (newObject instanceof Schema) {
595 | assertInstanceOf(newObject.schemaMap.name, SchemaOptions);
596 | }
597 | });
598 | });
599 | describe('and the schema object only has one property with a value of an options object', () => {
600 | let testFunc!: (param: string) => boolean;
601 | beforeEach(() => {
602 | newObject = new Schema({
603 | name: {
604 | type: 'string',
605 | required: true,
606 | unique: true,
607 | default: 'dangoDB',
608 | validator: (testFunc = (str: string): boolean => str[0] === 'b'),
609 | },
610 | });
611 | });
612 | it('will create an instance of the class', () => {
613 | assertInstanceOf(newObject, Schema);
614 | });
615 | it('will have a property schemaMap', () => {
616 | if (newObject instanceof Schema) {
617 | assertStrictEquals(
618 | Object.prototype.hasOwnProperty.call(newObject, 'schemaMap'),
619 | true
620 | );
621 | }
622 | });
623 | it('will have a property schemaMap with the property name', () => {
624 | if (newObject instanceof Schema) {
625 | assertStrictEquals(
626 | Object.prototype.hasOwnProperty.call(newObject.schemaMap, 'name'),
627 | true
628 | );
629 | }
630 | });
631 | it('will have a property schemaMap with the property name which is an instance of SchemaOptions', () => {
632 | if (newObject instanceof Schema) {
633 | assertInstanceOf(newObject.schemaMap.name, SchemaOptions);
634 | }
635 | });
636 | });
637 | });
638 | });
639 |
--------------------------------------------------------------------------------