adapt-authoring-mongodb 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,15 @@
1
- name: Add to main project
1
+ # Calls the org-level reusable workflow to add PRs to the TODO Board
2
+
3
+ name: Add PR to Project
2
4
 
3
5
  on:
4
- issues:
5
- types:
6
- - opened
7
6
  pull_request:
8
7
  types:
9
8
  - opened
9
+ - reopened
10
10
 
11
11
  jobs:
12
12
  add-to-project:
13
- name: Add to main project
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/add-to-project@v0.1.0
17
- with:
18
- project-url: https://github.com/orgs/adapt-security/projects/5
19
- github-token: ${{ secrets.PROJECTS_SECRET }}
13
+ uses: adapt-security/.github/.github/workflows/new.yml@main
14
+ secrets:
15
+ PROJECTS_SECRET: ${{ secrets.PROJECTS_SECRET }}
@@ -3,10 +3,16 @@ on:
3
3
  push:
4
4
  branches:
5
5
  - master
6
+
6
7
  jobs:
7
8
  release:
8
9
  name: Release
9
10
  runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write # to be able to publish a GitHub release
13
+ issues: write # to be able to comment on released issues
14
+ pull-requests: write # to be able to comment on released pull requests
15
+ id-token: write # to enable use of OIDC for trusted publishing and npm provenance
10
16
  steps:
11
17
  - name: Checkout
12
18
  uses: actions/checkout@v3
@@ -16,10 +22,11 @@ jobs:
16
22
  uses: actions/setup-node@v3
17
23
  with:
18
24
  node-version: 'lts/*'
25
+ - name: Update npm
26
+ run: npm install -g npm@latest
19
27
  - name: Install dependencies
20
28
  run: npm ci
21
29
  - name: Release
22
30
  env:
23
31
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24
- NPM_TOKEN: ${{ secrets.AAT_NPM_TOKEN }}
25
- run: npx semantic-release
32
+ run: npx semantic-release
@@ -3,7 +3,7 @@
3
3
  "documentation": {
4
4
  "enable": true,
5
5
  "manualPages": {
6
- "using-mongodb.md": "basics"
6
+ "using-mongodb.md": "development"
7
7
  }
8
8
  }
9
9
  }
@@ -1,66 +1,254 @@
1
1
  # Using MongoDB
2
- The `adapt-authoring-mongodb` module adds the ability to work with MongoDB databases.
2
+ > This guide assumes you have a working MongoDB instance. See the [installation guide](install) for more information.
3
+ ---
3
4
 
4
- ## Installing MongoDB
5
- To save data with the authoring tool, you'll need either a local MongoDB install, or a hosted solution.
5
+ The Adapt authoring tool provides two ways to interact with a MongoDB database:
6
6
 
7
- If you're new to hosting web applications/MongoDB and are intending to make your install accessible via the internet, we'd suggest going with a hosted solution.
7
+ 1. Via functions inherited from **AbstractApiModule** - _recommended for most use cases_
8
+ 2. Directly via **MongoDBModule**
8
9
 
9
- For instructions on installing MongoDB, see the [MongoDB docs](https://docs.mongodb.com/manual/installation/#mongodb-community-edition-installation-tutorials).
10
+ ### Which approach should I use?
10
11
 
11
- ## Using the module
12
- The `adapt-authoring-mongodb` module uses MongoDB's [Node.js driver](https://mongodb.github.io/node-mongodb-native/4.2) behind-the-scenes for communicating with MongoDB.
12
+ Use `AbstractApiModule` when you're building a full-featured API module that manages a collection of documents. It provides various functionality by default, such as schema validation, lifecycle hooks, caching, and automatic REST endpoint generation.
13
13
 
14
- Where possible, the MongoDBModule API has been designed to mirror the MongoDB Node.js driver API in both naming convensions and parameter naming/order. In some cases this has been changed for ease-of-use (e.g. `insertOne` has been renamed to `insert`). Please see the [Adapt authoring reference for the MongoDBModule](/class/node_modules/adapt-authoring-mongodb/lib/MongoDBModule.js~MongoDBModule.html) for more information (details on which of the MongoDB Node.js driver functions are used is specified there).
14
+ Use `MongoDBModule` directly when you don't need the extra functionality provided by `AbstractApiModule`, need low-level database access, such as aggregation pipelines, bulk operations, or working with collections outside your module's scope.
15
15
 
16
- > If you're new to working with MongoDB, check out this [Quick Start](https://mongodb.github.io/node-mongodb-native/4.2/#quick-start) guide in the official docs for a good overview.
16
+ ---
17
17
 
18
- ### Basic use
19
- The following functions provide the most common functionality, and will likely be the functions you use most often. Please see the [API reference]([/class/node_modules/adapt-authoring-mongodb/lib/MongoDBModule.js~MongoDBModule.html](https://tomtaylor.codes/ls/jsdoc3/MongoDBModule.html)) for full details.
18
+ ## Using AbstractApiModule
20
19
 
21
- #### `find(collectionName, query, options)`
22
- Retrieves a document.
20
+ If your module extends `AbstractApiModule`, you get a complete set of database operations with built-in validation and hooks.
23
21
 
24
- #### `insert(collectionName, data, options)`
25
- Inserts a new document.
22
+ ### Inserting documents
26
23
 
27
- #### `replace(collectionName, query, data, options)`
28
- Completely replaces an existing document.
24
+ ```javascript
25
+ // Validates and inserts a new document. Returns the inserted document.
26
+ const doc = await this.insert({ title: 'My Document', status: 'draft' }, { schemaName: 'myresource' })
27
+ ```
28
+
29
+ ### Querying documents
30
+
31
+ ```javascript
32
+ /**
33
+ * find()
34
+ * Returns an array of documents matching the query (empty if no matches are found)
35
+ */
29
36
 
30
- #### `update(collectionName, query, data, options)`
31
- Updates only specific fields of an existing document.
37
+ // Find all
38
+ const docs = await this.find()
32
39
 
33
- #### `delete(collectionName, query, options)`
34
- Removes an existing document.
40
+ // Find with query
41
+ const drafts = await this.find({ status: 'draft' })
35
42
 
36
- ### Querying the database
37
- The `find` function is used to retrieve documents from the database.
43
+ // Find with pagination
44
+ const page = await this.find({}, {}, { limit: 10, skip: 20 })
38
45
 
39
46
 
40
- #### Examples
41
- Note that the MongoDB module, as with the rest of the application, makes heavy use of promises, and will return a pending Promise for all relevant functions.
47
+ /**
48
+ * findOne()
49
+ * Returns single document matching the query
50
+ * if the strict option is NOT set to false, an error will be thrown if no results are found
51
+ */
42
52
 
43
- Inserting a document into the 'test' collection:
53
+ const doc = await this.findOne({ _id: '507f1f77bcf86cd799439011' })
54
+
55
+ // Allow no results (returns undefined instead of throwing)
56
+ const doc = await this.findOne({ _id: id }, { strict: false })
44
57
  ```
45
- try {
46
- const data = await mongodb.insert('test', { hello: 'world' });
47
- } catch(e) {
48
- // handle error
49
- }
58
+
59
+ ### Updating documents
60
+
61
+ ```javascript
62
+ // Update a single document
63
+ const updated = await this.update({ _id: '507f1f77bcf86cd799439011' }, { status: 'published' })
64
+
65
+ // Perform a raw update (gives direct access to extra MongoDB update functionality)
66
+ const updated = await this.update({ _id: id }, { $inc: { viewCount: 1 } }, { rawUpdate: true })
67
+
68
+ // Update all documents which match a query
69
+ const updated = await this.updateMany({ status: 'draft' }, { status: 'archived' })
70
+ ```
71
+
72
+ ### Deleting documents
73
+
74
+ ```javascript
75
+ // Remove a single document. Returns the deleted document
76
+ const deleted = await this.delete({ _id: '507f1f77bcf86cd799439011' })
77
+
78
+ // Remove multiple documents. Returns an array of deleted documents
79
+ const deleted = await this.deleteMany({ status: 'archived' })
80
+ ```
81
+
82
+
83
+ ### Options reference
84
+
85
+ The following options can be specified when using the above functions. If the option relates to specific functions, this has been noted.
86
+
87
+ | Option | Type | Description |
88
+ | ------ | ---- | ----------- |
89
+ | `schemaName` | String | Schema to validate against (defaults to module's schema) |
90
+ | `collectionName` | String | Collection to operate on (defaults to module's collection) |
91
+ | `validate` | Boolean | Whether to validate data (default: `true`) |
92
+ | `invokePreHook` | Boolean | Whether to invoke pre-operation hooks (default: `true`) |
93
+ | `invokePostHook` | Boolean | Whether to invoke post-operation hooks (default: `true`) |
94
+ | `rawUpdate` | Boolean | **For `update`/`updateMany`**: pass data directly to MongoDB without wrapping in `$set` |
95
+ | `strict` | Boolean | **For `findOne`**: throw error if no document found (default: `true`) |
96
+
97
+ ### Lifecycle hooks
98
+
99
+ In addition to the CRUD functions, `AbstractApiModule` provides hooks that allow you to intercept and modify data at various points. These hooks are accessible both internally and externally (i.e. from other modules).
100
+
101
+ For more detail on the hook system, see this [page](hooks).
102
+
103
+ | Hook | Parameters | Mutable | Description |
104
+ | ---- | ---------- | :-----: | ----------- |
105
+ | `preInsertHook` | `(data, options, mongoOptions)` | Yes | Before insert |
106
+ | `postInsertHook` | `(doc)` | No | After insert |
107
+ | `preUpdateHook` | `(originalDoc, newData, options, mongoOptions)` | Yes | Before update |
108
+ | `postUpdateHook` | `(originalDoc, updatedDoc)` | No | After update |
109
+ | `preDeleteHook` | `(doc, options, mongoOptions)` | No | Before delete |
110
+ | `postDeleteHook` | `(doc)` | No | After delete |
111
+
112
+ ### Examples
113
+
114
+ ```javascript
115
+ // INTERNAL: Modify data before insert
116
+ this.preInsertHook.tap(data => {
117
+ data.createdAt = new Date()
118
+ })
119
+
120
+ // EXTERNAL: React after insert
121
+ mymodule.postInsertHook.tap(doc => {
122
+ this.log('info', `mymodule created document ${doc._id}`)
123
+ })
50
124
  ```
51
125
 
52
- ### Advanced use
53
- It is possible to access the Node.js driver API directly from the MongoDBModule instance to allow for extra functionality not covered by the MongoDBModule itself. An example of this being creating an aggregation pipeline.
126
+ ---
127
+
128
+ ## Using MongoDBModule directly
54
129
 
55
- There are two methods of accessing the driver API. Which one you use is entirely up to you, and mostly comes down to code brevity:
56
- - Using the MongoDB client instance [[Adapt docs](https://tomtaylor.codes/ls/jsdoc3/MongoDBModule.html#client), [MongoDB Node.js driver docs](https://mongodb.github.io/node-mongodb-native/3.6/api/MongoClient.html)]
57
- - Using the MongoDB collection [[Adapt docs](), [MongoDB Node.js driver docs](https://tomtaylor.codes/ls/jsdoc3/MongoDBModule.html#getCollection)]
130
+ For operations not covered by `AbstractApiModule`, use the MongoDB module directly.
58
131
 
132
+ Note that this module follows the MongoDB Node.js driver API fairly closely. As such, there are some (less user-friendly) differences between the functions found in `AbstractApiModule`.
133
+
134
+ **Warning:** the `mongodb` module provides low-level access to the database, and does not perform any tasks such as data validation. Proceed with caution.
135
+
136
+ ### Accessing the mongodb module
137
+
138
+ ```javascript
139
+ const mongodb = await this.app.waitForModule('mongodb')
59
140
  ```
60
- // using the client instance
61
- mongodb.client.db.collection('mycollection').aggregate(/* args */)
62
- // using MongoDBModule#getCollection
63
- mongodb.getCollection('mycollection').aggregate(/* args */)
141
+
142
+ ### Inserting documents
143
+
144
+ ```javascript
145
+ const doc = await mongodb.insert('users', { email: 'user@example.com', name: 'Test User' })
64
146
  ```
65
147
 
66
- See the [MongoDB Node.js driver docs](https://mongodb.github.io/node-mongodb-native/4.2/classes/Collection.html) for the full API.
148
+ ### Querying documents
149
+
150
+ ```javascript
151
+ // Find all
152
+ const allUsers = await mongodb.find('users')
153
+
154
+ // Find with query
155
+ const user = await mongodb.find('users', { email: 'user@example.com' })
156
+
157
+ // Find with options
158
+ const page = await mongodb.find('users', {}, { limit: 10, skip: 20, sort: { createdAt: -1 }})
159
+ ```
160
+
161
+ ### Updating documents
162
+
163
+ ```javascript
164
+ // Update a single document
165
+ const updated = await mongodb.update('users', { _id: '507f1f77bcf86cd799439011' }, { $set: { name: 'Updated Name' } })
166
+
167
+ // Update multiple documents
168
+ const updated = await mongodb.updateMany('users', { role: 'guest' }, { $set: { active: false } })
169
+
170
+ // Replace an entire document
171
+ const replaced = await mongodb.replace('users', { _id: '507f1f77bcf86cd799439011' }, { ...data })
172
+ ```
173
+
174
+ ### Deleting documents
175
+
176
+ ```javascript
177
+ // Remove a single document
178
+ await mongodb.delete('users', { _id: '507f1f77bcf86cd799439011' })
179
+
180
+ // Removing multiple documents
181
+ await mongodb.deleteMany('users', { active: false })
182
+ ```
183
+
184
+ ### Advanced operations
185
+
186
+ For aggregation pipelines and other advanced features, access the underlying driver:
187
+
188
+ ```javascript
189
+ // Get a collection reference
190
+ const collection = mongodb.getCollection('users')
191
+
192
+ // Aggregation pipeline
193
+ const results = await collection.aggregate([
194
+ { $match: { active: true } },
195
+ { $group: { _id: '$role', count: { $sum: 1 } } }
196
+ ]).toArray()
197
+
198
+ // Database statistics
199
+ const stats = await mongodb.client.db().stats()
200
+ ```
201
+
202
+ ### Working with ObjectIds
203
+
204
+ The module automatically converts string IDs to ObjectIds. You can also work with them directly:
205
+
206
+ ```javascript
207
+ // Create a new ObjectId
208
+ const id = mongodb.ObjectId.create()
209
+
210
+ // Check if a string is valid
211
+ const isValid = mongodb.ObjectId.isValid('507f1f77bcf86cd799439011')
212
+
213
+ // Parse a string to ObjectId
214
+ const objectId = mongodb.ObjectId.parse('507f1f77bcf86cd799439011')
215
+ ```
216
+
217
+ ### Indexes
218
+
219
+ Set indexes for better query performance:
220
+
221
+ ```javascript
222
+ // Simple unique index
223
+ await mongodb.setIndex('users', 'email', { unique: true })
224
+
225
+ // Compound index
226
+ await mongodb.setIndex('content', { courseId: 1, type: 1 })
227
+ ```
228
+
229
+ See the [MongoDB Node.js driver documentation](https://mongodb.github.io/node-mongodb-native/) for the full driver API.
230
+
231
+ ---
232
+
233
+ ## Error handling
234
+
235
+ Common database errors:
236
+
237
+ | Error | Description |
238
+ | ----- | ----------- |
239
+ | `MONGO_CONN_FAILED` | Failed to connect to the database |
240
+ | `MONGO_DUPL_INDEX` | Duplicate key violation (unique index) |
241
+ | `MONGO_IMMUTABLE_FIELD` | Attempted to modify an immutable field |
242
+ | `INVALID_OBJECTID` | Invalid ObjectId string format |
243
+ | `NOT_FOUND` | Document not found (AbstractApiModule only) |
244
+ | `TOO_MANY_RESULTS` | Multiple documents found when one expected |
245
+
246
+ ```javascript
247
+ try {
248
+ await this.insert({ email: 'existing@example.com' })
249
+ } catch (e) {
250
+ if (e.code === 'MONGO_DUPL_INDEX') {
251
+ // Handle duplicate
252
+ }
253
+ }
254
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-mongodb",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Module for saving to a MongoDB instance",
5
5
  "homepage": "https://github.com/adapt-security/adapt-authoring-mongodb",
6
6
  "license": "GPL-3.0",
@@ -8,21 +8,21 @@
8
8
  "main": "index.js",
9
9
  "repository": "github:adapt-security/adapt-authoring-mongodb",
10
10
  "dependencies": {
11
- "mongodb": "^6.7.0"
11
+ "mongodb": "^7.0.0"
12
12
  },
13
13
  "peerDependencies": {
14
14
  "adapt-authoring-core": "github:adapt-security/adapt-authoring-core"
15
15
  },
16
16
  "devDependencies": {
17
- "standard": "^17.1.0",
18
- "@semantic-release/commit-analyzer": "^9.0.2",
17
+ "@semantic-release/commit-analyzer": "^13.0.1",
19
18
  "@semantic-release/git": "^10.0.1",
20
- "@semantic-release/github": "^8.0.5",
21
- "@semantic-release/npm": "^9.0.1",
22
- "@semantic-release/release-notes-generator": "^10.0.3",
23
- "conventional-changelog-eslint": "^3.0.9",
24
- "semantic-release": "^21.0.1",
25
- "semantic-release-replace-plugin": "^1.2.7"
19
+ "@semantic-release/github": "^12.0.2",
20
+ "@semantic-release/npm": "^13.1.2",
21
+ "@semantic-release/release-notes-generator": "^14.1.0",
22
+ "conventional-changelog-eslint": "^6.0.0",
23
+ "semantic-release": "^25.0.2",
24
+ "semantic-release-replace-plugin": "^1.2.7",
25
+ "standard": "^17.1.0"
26
26
  },
27
27
  "release": {
28
28
  "plugins": [
@@ -1,16 +0,0 @@
1
- name: Add labelled PRs to project
2
-
3
- on:
4
- pull_request:
5
- types: [ labeled ]
6
-
7
- jobs:
8
- add-to-project:
9
- if: ${{ github.event.label.name == 'dependencies' }}
10
- name: Add to main project
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/add-to-project@v0.1.0
14
- with:
15
- project-url: https://github.com/orgs/adapt-security/projects/5
16
- github-token: ${{ secrets.PROJECTS_SECRET }}