0.5.0 • Published 2 years ago

@chcaa/strapi-utils v0.5.0

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

@chcaa/strapi-utils

A set of utility modules to make it easier to work with strapi.

Installation

Run npm install @chcaa/strapi-utils

Modules

@chcaa/strapi-utils contains the following modules:

lifecycle/CollectionChangedHook

Strapi's native collection lifecycle hooks are fine when you only care about changes to the collection itself, but if you also need to know when relations have changed, e.g. to update a slug field, it can become a complex task to handle when only using the native hooks.

The @chcaa/strapi-utils/lifecycle.CollectionChangedHook makes it easy to be notified whenever a collection or it's relations has changed simply by registering the paths of interest.

Create an instance using let cch = new CollectionChangedHook(). This would typically be done in strapi's bootstrap() startup hook, but could be done anywhere.

The constructor can take an optional options object to configure the registered listeners (can be overwritten pr. listener). The following options can be set:

options:object

  • onSelf:object
    • emitBeforeCreate:boolean default=false should listeners observing their own collection receive beforeCreate events?
    • emitAfterCreate:boolean default=true should listeners observing their own collection receive afterCreate events?
    • emitBeforeUpdate:boolean default=false should listeners observing their own collection receive beforeUpdate events?
    • emitAfterUpdate:boolean default=true should listeners observing their own collection receive afterUpdate events?
    • emitBeforeDelete:boolean default=false should listeners observing their own collection receive beforeDelete events?
    • emitAfterDelete:boolean default=false should listeners observing their own collection receive afterDelete events?
  • causedBy:object
    • includeBeforeState:boolean default=false include before state of changed entries on afterUpdate events. To e.g. create diffs
    • includeAfterState:boolean default=false include after state of changed entries on afterUpdate events. To e.g. create diffs in combination with includeBeforeState
    • calculateAttributesChanged:boolean default=false calculate and include which attributes did change on afterUpdate events. For each entity changed an object with properties matching the entities will be included with true|false if the attribute value was changed or not. If not set explicitly to false includeBeforeState and includeAfterState will be included as well
    • includeInferredAfterState:boolean default=false include inferred after state of changed entries on beforeUpdate events on listeners observing the collection itself. The state is inferred using the current state and then strapiEvent.params.data object.
    • calculateInferredAttributesChanged:boolean, default=false calculate and include in beforeUpdate events on listeners observing the collection itself which inferred attributes would change. The changes are based on comparing the current state with the inferred after state.

      NOTE includeInferredAfterState and calculateInferredAttributesChanged cannot infer changes to component data due to insufficient info in the strapiEvent, for components use the afterState and calculateAttributesChanges in afterXXX events. HINT ids, beforeState, afterState and changedAttributes arrays are all ordered in the same way so to find the matching object of beforeState, afterState and changedAttributes can be done as:

      for (let i = 0; i < ids.length; i ++) {
          let beforeEntity = causedBy.beforeState[i];
          let afterEntity = causedBy.afterState[i];
          let attributesChanged = causedBy.attributesChanged[i];
      }

Register listeners for each collection to be notified about changes to using:

register(collectionUid, relationsPaths, listener, options)

  • collectionUid:string the uid of the collection to be notified about changes to
  • relationPaths:string[] one or more relation paths to track for changes.
  • listener:function(event:object) the function to call when changes occur

    • event.idsAffected:number[] the ids of the collection objects affected by the change
    • event.causedBy:object an object with information about the changes
      • relationPath:string one of the registered paths which resulted in the change event
      • collectionUid:string the uid of the collection which changed
      • ids:number[]: the ids of the collection which changed
      • eventType:string describes what happened to the affected path in form of one of "afterCreate"|"afterUpdate"|"afterDelete" and equivalent "beforeXXX" if enabled in options
      • strapiEvent:object the strapi-event that triggered this event
    • options:object [optional] a set of options to configure the listener (overwrites the default options set in the constructor). See the constructor above for details

All relational paths must point to a collection but are allowed to have intermediate "components" in their path.

To get notifications about the collection itself register an empty string ("") along with the other paths of interest.

In the example below we want to be notified about changes to movies when the movie itself ("") changes, directors of the movie ("directors") changes or an actor in the cast ("cast.actor") changes.

const { CollectionChangedHook } = require('@chcaa/strapi-utils/lifecycle');

let cch = new CollectionChangedHook({ onSelf: { emitBeforeEvents: true }});

cch.register("api::movie.movie", ["", "directors", "cast.actor"], ({ idsAffected, causedBy }) => {
    if (causedBy.eventType.startsWith('after')) { // we are also listening for beforeEvents on the path "", but for now we only react on afterXxx
      for (let idChunk of _.chunk(idsAffected, 100)) { // use lodash to chunk the ids into sub-arrays of 100 ids each
        let entries = await strapi.entityService.findMany('api::movie.movie', {
          filters: {
            $and: [{ id: { $in: idChunk } }]
          },
          populate: {
            genres: true,
            cast: { populate: { actor: true } }
          }
        });
        // do something with the entries
      }
    }
});

Warning! Prevent Infinite Loops 1. Do only modify data of paths not listened to or objects and relations attached to the owning collection identified by idsAffected.

Performance Note

Nested paths, especially ones including "components" can cause performance issues on larger datasets as they require a lot of joins for strapi to resolve the relations from the database.

To unregister use: unregister(collectionUid, relationaths, listener)

  • collectionUid:string the uid of the collection to NOT be notified about changes to anymore. E.g. "api::movie.movie"
  • relationPaths:string[] the relational paths to stop listening to
  • listener:function the listener to unregister (must be the same function which was passed to register()

If the listener was registered for the relationPaths "a", "b" but only unregisters for "a" it will still be notified about changes to "b".

0.5.0

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.3.0

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago