@simtlix/simfinity-js 1.1.0 → 1.2.0

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.
package/.eslintrc.json CHANGED
@@ -2,7 +2,8 @@
2
2
  "env": {
3
3
  "commonjs": true,
4
4
  "es2020": true,
5
- "node": true
5
+ "node": true,
6
+ "jest": true
6
7
  },
7
8
  "extends": [
8
9
  "airbnb-base"
@@ -6,7 +6,7 @@ on:
6
6
 
7
7
  jobs:
8
8
  build:
9
- runs-on: ubuntu-20.04
9
+ runs-on: ubuntu-24.04
10
10
  steps:
11
11
  - uses: actions/checkout@v2.3.4
12
12
  - uses: actions/setup-node@v1.4.4
@@ -7,7 +7,7 @@ on:
7
7
 
8
8
  jobs:
9
9
  build:
10
- runs-on: ubuntu-20.04
10
+ runs-on: ubuntu-24.04
11
11
  steps:
12
12
  - uses: actions/checkout@v2.3.4
13
13
  - uses: actions/setup-node@v1.4.4
@@ -19,7 +19,7 @@ jobs:
19
19
 
20
20
  publish-npm:
21
21
  needs: build
22
- runs-on: ubuntu-20.04
22
+ runs-on: ubuntu-24.04
23
23
  steps:
24
24
  - uses: actions/checkout@v2.3.4
25
25
  - uses: actions/setup-node@v1.4.4
@@ -32,7 +32,7 @@ jobs:
32
32
  NODE_AUTH_TOKEN: ${{secrets.NPMJS_TOKEN}}
33
33
  publish-gpr:
34
34
  needs: build
35
- runs-on: ubuntu-20.04
35
+ runs-on: ubuntu-24.04
36
36
  steps:
37
37
  - uses: actions/checkout@v2.3.4
38
38
  - uses: actions/setup-node@v1.4.4
@@ -7,7 +7,7 @@ on:
7
7
 
8
8
  jobs:
9
9
  generate_release:
10
- runs-on: ubuntu-20.04
10
+ runs-on: ubuntu-24.04
11
11
  steps:
12
12
  - name: Inject slug/short variables
13
13
  uses: rlespinasse/github-slug-action@3.1.0
package/README.md CHANGED
@@ -1,6 +1,50 @@
1
+ # Simfinity.js
1
2
 
3
+ Simfinity.js is a powerful library that automatically generates a GraphQL schema from your Mongoose models. It simplifies the process of creating a GraphQL API for your Node.js application by providing a set of conventions and helpers to handle common CRUD operations, filtering, pagination, and sorting.
2
4
 
3
- [![npm version](https://badge.fury.io/js/%40simtlix%2Fsimfinity-js.svg)](https://badge.fury.io/js/%40simtlix%2Fsimfinity-js)
5
+ ## Installation
6
+
7
+ To use Simfinity.js in your project, you'll need to have `mongoose` and `graphql` installed as peer dependencies.
8
+
9
+ ```bash
10
+ npm install mongoose graphql @simtlix/simfinity-js
11
+ ```
12
+
13
+ ## Core Concepts
14
+
15
+ The core of Simfinity.js revolves around two main concepts: connecting your Mongoose models to GraphQL types and creating a schema.
16
+
17
+ ### Connecting Models
18
+
19
+ The `simfinity.connect()` method is used to link a Mongoose model to a GraphQLObjectType. This tells Simfinity how to handle the data for that type.
20
+
21
+ ```javascript
22
+ const mongoose = require('mongoose');
23
+ const { GraphQLObjectType, GraphQLString, GraphQLNonNull } = require('graphql');
24
+ const simfinity = require('@simtlix/simfinity-js');
25
+
26
+ // 1. Define your GraphQL Type
27
+ const BookType = new GraphQLObjectType({
28
+ name: 'Book',
29
+ fields: () => ({
30
+ id: { type: new GraphQLNonNull(GraphQLID) },
31
+ title: { type: new GraphQLNonNull(GraphQLString) },
32
+ }),
33
+ });
34
+
35
+ // 2. Connect the type to Simfinity
36
+ simfinity.connect(null, BookType, 'book', 'books');
37
+ ```
38
+
39
+ ### Creating a Schema
40
+
41
+ Once you've connected your types, you can generate a GraphQL schema using `simfinity.createSchema()`. This will automatically create the queries and mutations for your connected types.
42
+
43
+ ```javascript
44
+ const schema = simfinity.createSchema();
45
+ ```
46
+
47
+ ---
4
48
 
5
49
  # About SimfinityJS
6
50
  SimfinityJS is a Node.js framework that allows bringing all the power and flexibility of MongoDB query language to GraphQL interfaces.
@@ -137,3 +181,530 @@ query {
137
181
  Visit the [samples site](https://github.com/simtlix/simfinity.js-samples) and learn about SimfinityJS through different use cases
138
182
 
139
183
 
184
+
185
+ ## Bookstore Example
186
+
187
+ Let's explore how to use Simfinity.js with a simple bookstore example. We'll have two main types: `Author` and `Book`.
188
+
189
+ ### 1. Define Your GraphQL Types
190
+
191
+ First, we'll define the `GraphQLObjectType` for our `Author` and `Book` models. Notice the `extensions` field on the `author` field of the `BookType`. This is how you define relationships in Simfinity.
192
+
193
+ ```javascript
194
+ const {
195
+ GraphQLObjectType,
196
+ GraphQLString,
197
+ GraphQLNonNull,
198
+ GraphQLID,
199
+ GraphQLList,
200
+ } = require('graphql');
201
+
202
+ const AuthorType = new GraphQLObjectType({
203
+ name: 'Author',
204
+ fields: () => ({
205
+ id: { type: new GraphQLNonNull(GraphQLID) },
206
+ name: { type: new GraphQLNonNull(GraphQLString) },
207
+ books: {
208
+ type: new GraphQLList(BookType),
209
+ extensions: {
210
+ relation: {
211
+ connectionField: 'authorId',
212
+ },
213
+ },
214
+ },
215
+ }),
216
+ });
217
+
218
+ const BookType = new GraphQLObjectType({
219
+ name: 'Book',
220
+ fields: () => ({
221
+ id: { type: new GraphQLNonNull(GraphQLID) },
222
+ title: { type: new GraphQLNonNull(GraphQLString) },
223
+ author: {
224
+ type: AuthorType,
225
+ extensions: {
226
+ relation: {
227
+ connectionField: 'authorId',
228
+ },
229
+ },
230
+ },
231
+ }),
232
+ });
233
+ ```
234
+
235
+ ### Advanced Relationship Definition
236
+
237
+ For more control over your relationships, you can provide additional options in the `extensions.relation` object and add a custom `resolve` function.
238
+
239
+ * `connectionField`: (Required) The name of the field on the current model that stores the ID of the related object (e.g., `authorId` on the `Book` model).
240
+ * `displayField`: (Optional) The name of the field on the related object that should be used as its display value. This can be useful for auto-generated UI components.
241
+ * `resolve`: (Optional) A custom resolver function to fetch the related data. This gives you full control over how the relationship is resolved. If not provided, Simfinity.js will handle it automatically based on the `connectionField`.
242
+
243
+ Here's an example of an `Episode` type with a relationship to a `Season` type, using these advanced options. This demonstrates how to define which field to display from the related object and how to write a custom resolver.
244
+
245
+ ```javascript
246
+ const { GraphQLID, GraphQLObjectType, GraphQLString, GraphQLInt } = require('graphql');
247
+ const GraphQLDateTime = require('graphql-iso-date').GraphQLDateTime;
248
+ const simfinity = require('@simtlix/simfinity-js');
249
+ const seasonType = require('./season'); // Assuming seasonType is defined elsewhere
250
+
251
+ const episodeType = new GraphQLObjectType({
252
+ name: 'episode',
253
+ fields: () => ({
254
+ id: { type: GraphQLID },
255
+ number: { type: GraphQLInt },
256
+ name: { type: GraphQLString },
257
+ date: { type: GraphQLDateTime },
258
+ season: {
259
+ type: seasonType,
260
+ extensions: {
261
+ relation: {
262
+ connectionField: 'seasonID',
263
+ displayField: 'number'
264
+ }
265
+ },
266
+ resolve(parent) {
267
+ // Use simfinity.getModel() to get the Mongoose model for a GraphQL type
268
+ return simfinity.getModel(seasonType).findById(parent.seasonID);
269
+ }
270
+ },
271
+ })
272
+ });
273
+ ```
274
+
275
+ In this example:
276
+ - The `season` field on `episodeType` is linked to `seasonType`.
277
+ - `connectionField: 'seasonID'` tells Simfinity that the `seasonID` field in the episode document holds the ID of the related season.
278
+ - `displayField: 'number'` suggests that the `number` field of a season (e.g., season 1, season 2) should be used to represent it.
279
+ - The `resolve` function manually fetches the season document using its ID from the parent episode. This is useful for custom logic, but often not necessary, as Simfinity can resolve it automatically.
280
+
281
+ ### 2. Connect Your Types
282
+
283
+ Next, we'll connect these types to Simfinity. This will automatically generate the Mongoose models and the necessary queries and mutations.
284
+
285
+ ```javascript
286
+ const simfinity = require('@simtlix/simfinity-js');
287
+
288
+ simfinity.connect(null, AuthorType, 'author', 'authors');
289
+ simfinity.connect(null, BookType, 'book', 'books');
290
+ ```
291
+
292
+ ### 3. Create the Server
293
+
294
+ Finally, we'll create a simple Express server with `express-graphql` to serve our schema.
295
+
296
+ ```javascript
297
+ const express = require('express');
298
+ const { graphqlHTTP } = require('express-graphql');
299
+ const mongoose = require('mongoose');
300
+ const simfinity = require('@simtlix/simfinity-js');
301
+
302
+ // ... (AuthorType and BookType definitions)
303
+
304
+ // Connect to MongoDB
305
+ mongoose.connect('mongodb://localhost/bookstore', {
306
+ useNewUrlParser: true,
307
+ useUnifiedTopology: true,
308
+ });
309
+
310
+ const app = express();
311
+
312
+ app.use('/graphql', graphqlHTTP({
313
+ schema: simfinity.createSchema(),
314
+ graphiql: true,
315
+ }));
316
+
317
+ app.listen(4000, () => {
318
+ console.log('Server is running on port 4000');
319
+ });
320
+ ```
321
+
322
+ ## Creating Complex Objects
323
+
324
+ Simfinity.js makes it easy to create and connect objects in a single mutation. When you define a relationship, the input type for the parent object will automatically include a field for the child's ID.
325
+
326
+ For our bookstore example, the `addBook` mutation will accept an `author` field, which is an object containing the `id` of the author.
327
+
328
+ ### Creating a Book for an Existing Author
329
+
330
+ To create a new book and link it to an author that already exists, you can use the `addBook` mutation and provide the author's ID.
331
+
332
+ ```graphql
333
+ mutation {
334
+ addBook(input: {
335
+ title: "The Hitchhiker's Guide to the Galaxy",
336
+ author: {
337
+ id: "author_id_here"
338
+ }
339
+ }) {
340
+ id
341
+ title
342
+ author {
343
+ id
344
+ name
345
+ }
346
+ }
347
+ }
348
+ ```
349
+
350
+ This will create a new book and set its `authorId` field to the provided author ID.
351
+
352
+ ---
353
+
354
+ ## Querying Data
355
+
356
+ Simfinity.js provides a rich set of querying capabilities that are automatically added to your schema. You can filter, paginate, and sort your data with ease.
357
+
358
+ ### Basic Queries
359
+
360
+ To get a list of all books, you can use the `books` query:
361
+
362
+ ```graphql
363
+ query {
364
+ books {
365
+ id
366
+ title
367
+ }
368
+ }
369
+ ```
370
+
371
+ To get a single book by its ID, you can use the `book` query:
372
+
373
+ ```graphql
374
+ query {
375
+ book(id: "book_id_here") {
376
+ id
377
+ title
378
+ author {
379
+ id
380
+ name
381
+ }
382
+ }
383
+ }
384
+ ```
385
+
386
+ ### Filtering
387
+
388
+ You can filter your queries using the `filter` argument. The filter object takes an `operator` and a `value`.
389
+
390
+ #### Simple Equality Filter
391
+
392
+ To find all books with a specific title:
393
+
394
+ ```graphql
395
+ query {
396
+ books(title: {
397
+ operator: EQ,
398
+ value: "The Hitchhiker's Guide to the Galaxy"
399
+ }) {
400
+ id
401
+ title
402
+ }
403
+ }
404
+ ```
405
+
406
+ #### Using Other Operators
407
+
408
+ Simfinity.js supports a variety of operators: `EQ`, `NE`, `GT`, `GTE`, `LT`, `LTE`, `LIKE`, `IN`, `NIN`, and `BTW`.
409
+
410
+ To find all books with "Guide" in the title:
411
+
412
+ ```graphql
413
+ query {
414
+ books(title: {
415
+ operator: LIKE,
416
+ value: "Guide"
417
+ }) {
418
+ id
419
+ title
420
+ }
421
+ }
422
+ ```
423
+
424
+ ### Filtering on Nested Objects
425
+
426
+ You can also filter based on the fields of a related object. To do this, you provide a `terms` array to the filter argument, where each term specifies a `path` to the nested field.
427
+
428
+ To find all books by a specific author:
429
+
430
+ ```graphql
431
+ query {
432
+ books(author: {
433
+ terms: [
434
+ {
435
+ path: "name",
436
+ operator: EQ,
437
+ value: "Douglas Adams"
438
+ }
439
+ ]
440
+ }) {
441
+ id
442
+ title
443
+ author {
444
+ name
445
+ }
446
+ }
447
+ }
448
+ ```
449
+
450
+ You can also use a deeper path to filter on nested relations. For example, if our `Author` type had a `country` relation, we could find all books by authors from a specific country:
451
+
452
+ ```graphql
453
+ query {
454
+ books(author: {
455
+ terms: [
456
+ {
457
+ path: "country.name",
458
+ operator: EQ,
459
+ value: "England"
460
+ }
461
+ ]
462
+ }) {
463
+ id
464
+ title
465
+ author {
466
+ name
467
+ country {
468
+ name
469
+ }
470
+ }
471
+ }
472
+ }
473
+ ```
474
+
475
+ ### Pagination
476
+
477
+ To paginate your results, you can use the `pagination` argument. You can also get a `count` of the total number of documents that match the query.
478
+
479
+ ```graphql
480
+ query {
481
+ books(pagination: {
482
+ page: 1,
483
+ size: 10,
484
+ count: true
485
+ }) {
486
+ id
487
+ title
488
+ }
489
+ }
490
+ ```
491
+
492
+ ### Sorting
493
+
494
+ To sort your results, you can use the `sort` argument.
495
+
496
+ ```graphql
497
+ query {
498
+ books(sort: {
499
+ terms: [
500
+ {
501
+ field: "title",
502
+ order: ASC
503
+ }
504
+ ]
505
+ }) {
506
+ id
507
+ title
508
+ }
509
+ }
510
+ ```
511
+
512
+ ## Mutations
513
+
514
+ Simfinity.js automatically generates `add`, `update`, and `delete` mutations for each type you connect.
515
+
516
+ ### Add Mutation
517
+
518
+ To create a new author:
519
+
520
+ ```graphql
521
+ mutation {
522
+ addAuthor(input: {
523
+ name: "J.R.R. Tolkien"
524
+ }) {
525
+ id
526
+ name
527
+ }
528
+ }
529
+ ```
530
+
531
+ ### Update Mutation
532
+
533
+ To update an existing author's name:
534
+
535
+ ```graphql
536
+ mutation {
537
+ updateAuthor(input: {
538
+ id: "author_id_here",
539
+ name: "John Ronald Reuel Tolkien"
540
+ }) {
541
+ id
542
+ name
543
+ }
544
+ }
545
+ ```
546
+
547
+ ### Delete Mutation
548
+
549
+ To delete an author:
550
+
551
+ ```graphql
552
+ mutation {
553
+ deleteAuthor(id: "author_id_here") {
554
+ id
555
+ }
556
+ }
557
+ ```
558
+
559
+ ## Lifecycle Hooks with Controllers
560
+
561
+ For more granular control over the automatically generated mutations (`add`, `update`, `delete`), you can provide a controller object to Simfinity.js. This controller can contain methods that are executed as lifecycle hooks during these operations, allowing you to run validation, perform modifications, or trigger side effects.
562
+
563
+ The controller is passed as the fifth argument to the `simfinity.connect()` method.
564
+
565
+ ### Controller Methods
566
+
567
+ * `onCreating({ doc })`: Executed just before a new document is created. You can modify the `doc` or throw an error to prevent creation.
568
+ * `onUpdating({ doc, originalDoc })`: Executed before a document is updated. It receives the new `doc` with the pending changes and the `originalDoc` as it exists in the database.
569
+ * `onDeleting({ doc })`: Executed before a document is deleted. It receives the document that is about to be removed.
570
+
571
+ ### Example
572
+
573
+ Here's how you can define a controller for our `Book` type to add custom validation and logging:
574
+
575
+ ```javascript
576
+ const bookController = {
577
+ onCreating: async ({ doc }) => {
578
+ // Validate that a book has a title before saving.
579
+ if (!doc.title || doc.title.trim().length === 0) {
580
+ throw new Error('Book title cannot be empty.');
581
+ }
582
+ console.log(`A new book titled "${doc.title}" is being created.`);
583
+ // You can also modify the document before it's saved, e.g., to add a timestamp.
584
+ },
585
+
586
+ onUpdating: async ({ doc, originalDoc }) => {
587
+ // Log the update operation.
588
+ console.log(`The book "${originalDoc.title}" is being updated.`);
589
+ // 'doc' contains the new values, while 'originalDoc' has the old ones.
590
+ },
591
+
592
+ onDeleting: async ({ doc }) => {
593
+ // Perform a final check or logging before deletion.
594
+ console.log(`The book "${doc.title}" is being deleted.`);
595
+ // This is a good place to perform related cleanup operations.
596
+ }
597
+ };
598
+
599
+ // Connect the BookType with its controller
600
+ simfinity.connect(
601
+ null, // mongooseModel
602
+ BookType, // graphQLType
603
+ 'book', // singularName
604
+ 'books', // pluralName
605
+ bookController // controller
606
+ );
607
+ ```
608
+
609
+ When you now use the `addBook`, `updateBook`, or `deleteBook` mutations, the corresponding controller methods will be executed. For example, trying to create a book with an empty title would now fail with the custom error message.
610
+
611
+ ## State Machines
612
+
613
+ Simfinity.js has built-in support for state machines, allowing you to manage the lifecycle of your objects in a declarative way. You can define states and actions that transition an object from one state to another. For each action, you can also specify business logic that gets executed during the transition.
614
+
615
+ ### Defining a State Machine
616
+
617
+ Let's look at an example of a `Season` type that has a lifecycle managed by a state machine. The process involves four main steps:
618
+
619
+ 1. **Define States**: Create a `GraphQLEnumType` to represent the possible states.
620
+ 2. **Define Type**: Create the `GraphQLObjectType` that will have a state field.
621
+ 3. **Configure State Machine**: Define an object with the `initialState` and the `actions` that govern transitions.
622
+ 4. **Connect**: Use `simfinity.connect()` to link the type with its state machine.
623
+
624
+ Here is the complete example:
625
+
626
+ ```javascript
627
+ const graphql = require('graphql');
628
+ const simfinity = require('@simtlix/simfinity-js');
629
+
630
+ const { GraphQLObjectType, GraphQLID, GraphQLInt, GraphQLEnumType } = graphql;
631
+
632
+ // 1. Define the states using a GraphQLEnumType
633
+ const seasonState = new GraphQLEnumType({
634
+ name: 'seasonState',
635
+ values: {
636
+ SCHEDULED: { value: 'SCHEDULED' },
637
+ ACTIVE: { value: 'ACTIVE' },
638
+ FINISHED: { value: 'FINISHED' }
639
+ }
640
+ });
641
+
642
+ // 2. Define the GraphQLObjectType
643
+ const seasonType = new GraphQLObjectType({
644
+ name: 'season',
645
+ fields: () => ({
646
+ id: { type: GraphQLID },
647
+ number: { type: GraphQLInt },
648
+ year: { type: GraphQLInt },
649
+ state: { type: seasonState }
650
+ })
651
+ });
652
+
653
+ // 3. Define the state machine configuration
654
+ const stateMachine = {
655
+ initialState: 'SCHEDULED', // The value of the initial state
656
+ actions: {
657
+ activate: {
658
+ from: 'SCHEDULED',
659
+ to: 'ACTIVE',
660
+ action: async ({ doc }) => {
661
+ // Business logic to run on activation
662
+ // The 'doc' parameter contains the document being transitioned
663
+ console.log(`Activating season ${doc._id} of year ${doc.year}`);
664
+ }
665
+ },
666
+ finalize: {
667
+ from: 'ACTIVE',
668
+ to: 'FINISHED',
669
+ action: async ({ doc }) => {
670
+ // Business logic to run on finalization
671
+ console.log(`Finalizing season ${doc._id} of year ${doc.year}`);
672
+ }
673
+ }
674
+ }
675
+ };
676
+
677
+ // 4. Connect the type and its state machine to Simfinity
678
+ simfinity.connect(
679
+ null,
680
+ seasonType,
681
+ 'season',
682
+ 'seasons',
683
+ null,
684
+ null,
685
+ stateMachine
686
+ );
687
+ ```
688
+
689
+ When a new `season` is created, its `state` field will automatically be set to `SCHEDULED`.
690
+
691
+ ### Triggering State Transitions
692
+
693
+ When you connect a type with a state machine, Simfinity.js automatically creates a GraphQL mutation for each action. The mutation name is a combination of the action name and the type name (e.g., `actionName` + `TypeName`).
694
+
695
+ For our `season` example, Simfinity.js will generate `activateSeason` and `finalizeSeason` mutations.
696
+
697
+ To activate a season, you would call the `activateSeason` mutation with the ID of the season:
698
+
699
+ ```graphql
700
+ mutation {
701
+ activateSeason(id: "season_id_here") {
702
+ id
703
+ state
704
+ }
705
+ }
706
+ ```
707
+
708
+ This will change the season's state from `SCHEDULED` to `ACTIVE` and execute the `action` function defined for the `activate` transition.
709
+
710
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "peerDependencies": {
18
18
  "graphql": "^14.7.0",
19
- "mongoose": "^5.12.1"
19
+ "mongoose": "^8.15.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "eslint": "^7.7.0",
@@ -29,5 +29,9 @@
29
29
  "pre-commit": "npm run lint || npm run lint-fix"
30
30
  },
31
31
  "owner": "simtlix"
32
+ },
33
+ "optionalDependencies": {
34
+ "graphql": "^14.7.0",
35
+ "mongoose": "^8.15.1"
32
36
  }
33
37
  }
package/src/index.js CHANGED
@@ -7,7 +7,7 @@ const QLOperator = require('./const/QLOperator');
7
7
  const QLValue = require('./const/QLValue');
8
8
  const QLSort = require('./const/QLSort');
9
9
 
10
- mongoose.set('useFindAndModify', false);
10
+ mongoose.set('strictQuery', false);
11
11
 
12
12
  const {
13
13
  GraphQLObjectType, GraphQLString, GraphQLID, GraphQLSchema, GraphQLList,
@@ -470,7 +470,17 @@ const onDeleteObject = async (Model, gqltype, controller, args, session, linkToP
470
470
  await controller.onDelete(deletedObject, session);
471
471
  }
472
472
 
473
- return Model.findByIdAndDelete(args, deletedObject.modelArgs).session(session);
473
+ return Model.findByIdAndDelete({ _id: args.id }).session(session);
474
+ };
475
+
476
+ const onDeleteSubject = async (Model, controller, id, session) => {
477
+ const currentObject = await Model.findById({ _id: id }).lean();
478
+
479
+ if (controller && controller.onDelete) {
480
+ await controller.onDelete(currentObject, session);
481
+ }
482
+
483
+ return Model.findByIdAndDelete(id, { session });
474
484
  };
475
485
 
476
486
  const onUpdateSubject = async (Model, gqltype, controller, args, session, linkToParent) => {
@@ -481,7 +491,6 @@ const onUpdateSubject = async (Model, gqltype, controller, args, session, linkTo
481
491
  await iterateonCollectionFields(materializedModel, gqltype, objectId, session);
482
492
  }
483
493
 
484
- let modifiedObject = materializedModel.modelArgs;
485
494
  const currentObject = await Model.findById({ _id: objectId }).lean();
486
495
 
487
496
  const argTypes = gqltype.getFields();
@@ -490,32 +499,32 @@ const onUpdateSubject = async (Model, gqltype, controller, args, session, linkTo
490
499
  if (fieldEntry.extensions && fieldEntry.extensions.relation
491
500
  && fieldEntry.extensions.relation.embedded) {
492
501
  const oldObjectData = currentObject[fieldEntryName];
493
- const newObjectData = modifiedObject[fieldEntryName];
502
+ const newObjectData = materializedModel.modelArgs[fieldEntryName];
494
503
  if (newObjectData) {
495
504
  if (Array.isArray(oldObjectData) && Array.isArray(newObjectData)) {
496
- modifiedObject[fieldEntryName] = newObjectData;
505
+ materializedModel.modelArgs[fieldEntryName] = newObjectData;
497
506
  } else {
498
- modifiedObject[fieldEntryName] = { ...oldObjectData, ...newObjectData };
507
+ materializedModel.modelArgs[fieldEntryName] = { ...oldObjectData, ...newObjectData };
499
508
  }
500
509
  }
501
510
  }
502
511
 
503
512
  if (args[fieldEntryName] === null
504
513
  && !(fieldEntry.type instanceof GraphQLNonNull)) {
505
- modifiedObject = { ...modifiedObject, $unset: { [fieldEntryName]: '' } };
514
+ materializedModel.modelArgs = { ...materializedModel.modelArgs, $unset: { [fieldEntryName]: '' } };
506
515
  }
507
516
  });
508
517
 
509
518
  if (controller && controller.onUpdating) {
510
- await controller.onUpdating(objectId, modifiedObject, args, session);
519
+ await controller.onUpdating(objectId, materializedModel.modelArgs, session);
511
520
  }
512
521
 
513
522
  const result = Model.findByIdAndUpdate(
514
- objectId, modifiedObject, { new: true },
515
- );
523
+ objectId, materializedModel.modelArgs, { new: true },
524
+ ).session(session);
516
525
 
517
526
  if (controller && controller.onUpdated) {
518
- await controller.onUpdated(result, args, session);
527
+ await controller.onUpdated(result, session);
519
528
  }
520
529
 
521
530
  return result;
@@ -633,7 +642,10 @@ const executeItemFunction = async (gqltype, collectionField, objectId, session,
633
642
  };
634
643
  break;
635
644
  case operations.DELETE:
636
- // TODO: implement
645
+ operationFunction = async (collectionItem) => {
646
+ await onDeleteSubject(typesDict.types[collectionGQLType.name].model,
647
+ typesDict.types[collectionGQLType.name].controller, collectionItem, session);
648
+ };
637
649
  }
638
650
 
639
651
  for (const element of collectionFieldsList) {