{"_id":"@dta5/npm-dmx-utils","name":"@dta5/npm-dmx-utils","dist-tags":{"latest":"5.0.44"},"versions":{"5.0.44":{"name":"@dta5/npm-dmx-utils","version":"5.0.44","description":"DMX microservice utils","main":"lib/index.js","nyc":{"check-coverage":true,"lines":85,"statements":85},"scripts":{"lint":"eslint .","test":"NODE_ENV=test DMX_SERVICE=Automation ./node_modules/mocha/bin/mocha --recursive tests --timeout=10000 --exit","test-spec":"nyc --reporter=none npm test","coverage":"nyc report"},"repository":{"type":"git","url":"git+https://gitlab.com/dta5/npm-dmx-utils.git"},"author":{"name":"Dealer Market Exchange LLC"},"license":"UNLICENSED","dependencies":{"amqplib":"^0.5.5","array.prototype.flatmap":"^1.2.3","bluebird":"^3.7.2","flat":"^5.0.0","lodash":"^4.17.15","moment":"^2.24.0","mongodb":"^3.4.1","mongoose":"5.5.1","string-similarity":"^3.0.0","uuid":"^3.3.3"},"devDependencies":{"chai":"^4.2.0","chai-as-promised":"^7.1.1","chai-subset":"^1.6.0","dirty-chai":"^2.0.1","eslint":"^6.8.0","eslint-config-airbnb-base":"^14.0.0","eslint-plugin-import":"^2.19.1","eslint-plugin-mocha":"^6.2.2","eslint-plugin-unicorn":"^14.0.1","mocha":"^6.2.2","nyc":"^14.1.1","sinon":"^7.5.0","sinon-chai":"^3.4.0"},"bugs":{"url":"https://gitlab.com/dta5/npm-dmx-utils/issues"},"homepage":"https://gitlab.com/dta5/npm-dmx-utils#readme","directories":{"lib":"lib","test":"tests"},"gitHead":"7e02e625b174d51a146d54fafaada3e45d992bdf","_id":"@dta5/npm-dmx-utils@5.0.44","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-p/2lso/yV2/cNntbyYggJLOIJLd+I6/hT4D/U+m4NibtsU5L/JUs5hZxOQHbaAFafpiJsxzDwWfgJspipL5vrQ==","shasum":"2198d8304dc1e49bcbce91c07fda179c916926b4","tarball":"https://registry.npmjs.org/@dta5/npm-dmx-utils/-/npm-dmx-utils-5.0.44.tgz","fileCount":68,"unpackedSize":198694,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSYM0UwZozek8jtw0Yl6Gf1Geu3zv9IainFw2ZaIpmCgIgYuCi2QA1T2GbNTzGPnHlAEHHYUI7PaHNlBxbK/u5Qdg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiXRZKACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo58RAAj0ilAQWRzSkRw8rVU8Kbhj8ZLQh1P4a9LDUmzlJcBpWVJfIB\r\nhCWM2y7fOwqthXpKJevkg+PPOCL25qx8iN45HWb31Z/Qmc1fc6npBF0DQiME\r\n6/gIhjd0jIeGiGb0Uwiadzdt0sbPcAEnTW11vxDoby16qth7NVAT4fuHQ9jh\r\nx+sQ/OHU5Wb1xA9D8MmHZfrAak56JFihlHLLc7ybCglsFrBEDgynCPv65jh3\r\n1M02baJq871kSwHWD+bfLe3Qaiz6NqSfljoJZPjtFI4ae5f4QrQzIVQS3oSy\r\n6aiyGQxoJYS1Li9BYFJwKHGv0LhzbM1S1mV0iosQkePXY24DFE6VsNICGNFd\r\niAK6K8FYdKftEu8TqcoRBAc8SSSEkbeTjkgAw4eNs540e9TkEaABL86yfZCV\r\n1p/lV/pzjVA+d2UbtBRaFdxdWDPcqSLJN/0Q8BmMhm/GiXkk8RYxO/ZN9UF7\r\njYF6XHnVIGTP1CuCd4mD+JNQkHXzmZsoz0xBKUAOa15dlMuX6ZbMiSfRSEWx\r\n+d/NJjfXC9hLChVg+NnZjXU81cf7ddmpqfdcBU9FAqXrBXWgLBAMDFnT6zCt\r\nCi3gLTUYMqjTzvbt5SxR3Pakp6/6A7/tl6yc+k2BJszGuvLZe1TuPagYCppW\r\nG26DyWkAoLxSkLl2Ru72qUyt83l/39NsTOw=\r\n=LAbg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"efestennis93","email":"alexander.grechanik@celadonsoft.com"},"maintainers":[{"name":"efestennis93","email":"alexander.grechanik@celadonsoft.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/npm-dmx-utils_5.0.44_1650267722351_0.2020286726235052"},"_hasShrinkwrap":false}},"time":{"created":"2022-04-18T07:42:02.305Z","5.0.44":"2022-04-18T07:42:02.554Z","modified":"2022-04-18T07:42:02.711Z"},"maintainers":[{"name":"efestennis93","email":"alexander.grechanik@celadonsoft.com"}],"description":"DMX microservice utils","homepage":"https://gitlab.com/dta5/npm-dmx-utils#readme","repository":{"type":"git","url":"git+https://gitlab.com/dta5/npm-dmx-utils.git"},"author":{"name":"Dealer Market Exchange LLC"},"bugs":{"url":"https://gitlab.com/dta5/npm-dmx-utils/issues"},"license":"UNLICENSED","readme":"# npm-dmx-utils\n\n## DMXSchema\n\nThe `DMXSchema` introduces an extension to `mongoose.Schema` with built-in support for DMX Entitlements.\n\nThe basic configuration allows for an `entitlements` field on each of the schema's defined fields with the ability to configure visibility and editability for each field in the schema. For processing data before sending it to a user the `DMXSchema` adds `sanitize` methods on the instance and the model. For protecting data from being modified by those without the correct `entitlement` it provides an instance method `setForUser`.\n\n### Schema configuration\n\n#### docinfo\nDMXSchema updates automatically `docinfo.createdAt` and `docinfo.updatedAt` fields.\n\nTo avoid adding `docinfo` field, pass `dmx.skipDocinfo` (a nested field inside `dmx` field) param to the options (second param of DMXSchema constructor) e.g. for subschemas.\n\nAs default docinfo has the following fields available:\n```js\ncreatedAt: Date,\ncreatedBy: String,\nupdatedAt: Date,\nupdatedBy: String,\ndeletedAt: Date,\ndeletedBy: String,\n```\n\nIf you want to add any more fields to `docinfo`, just pass `docinfo.yourFieldName` (a nested field inside `docinfo` field) with the field definition to the schema (first param of DMXSchema constructor). Then `docinfo` will contain all the default fields and your field as well. This doesn't mean DMXSchema will modify your field, it's only a definition so without defining custom fields, an error will be thrown for an attempt of using it.\n\n#### Audits\nDMXSchema can be configured to store an audit trail on documents stored in the database. This feature is configurable per field within a schema. When a document is updated, all fields which are configured to be auditable, will be stored in a separate collection with information on what the previous and new values are as well as information about the user that made those changes. To enable audits for a schema, you must define the collection to which the audit documents will be stored.\n```javascript\nconst schema = new DMXSchema({\n  auditField: { type: Boolean, audit: true },\n  dontAuditField: { type: Boolean },\n}, {\n  dmx: {\n    audit: {\n      collection: 'auditCollection'\n    }\n  }\n})\n```\n#### Entitlements\nThe `DMXSchema` supports a custom option field called `entitlements` which contains up to four sub-options:\n* `view` an array of entitlement names of which the user must have at least one to be able to view this field\n* `conditionalView` a method for custom logic which adds another layer to the `view` above to further reduce visibility. It should return `true` if the field should be visible to the user and `false` if it should not be visible.\n* `edit` an array of entitlement names of which the user must have at least one to be able to edit the field\n* `conditionalEdit` a method for custom logic which adds another layer to the `edit` above to further reduce editability. It should throw an `EntitlementError` if the edit is disallowed.\n\n```javascript\nconst mongoose = require('mongoose');\nconst { getSchema, EntitlementError } = require('@dmx/npm-dmx-utils').DMXSchema;\n\nconst DMXSchema = getSchema(mongoose);\n\nconst someSchema = new DMXSchema({\n  // Fields defined without an `entitlements` option will be invisible to and uneditable by users.\n  hiddenA: String,\n  hiddenB: { type: Number },\n\n  // Fields with a `*` view are visible to all users\n  visibleToAll: {\n    type: String,\n    entitlements: {\n      view: ['*']\n    }\n  },\n\n  // Users with either `entitlementA` or `entitlementB` will be able to view this field but only\n  // those with `entitlementA` will be able to modify it.\n  basicField: {\n    type: String,\n    entitlements: {\n      view: ['entitlementA', 'entitlementB'],\n      edit: ['entitlementA']\n    }\n  },\n\n  // Users with any entitlement beginning with `entitlementC` will be able to view this field\n  // e.g. `entitlementC.create`, `entitlementC.remove`, `entitlementC.all`\n  visibleToAnyEntitlementC: {\n    type: String,\n    entitlements: {\n      view: ['entitlementC.*'],\n    }\n  },\n\n  // `conditionalView` builds on the `view` limitations. In this example the user must\n  // at least have `entitlementA`. The other restrictions are described below.\n  customViewField: {\n    type: Boolean,\n    entitlements: {\n      view: ['entitlementA'],\n      conditionalView: function (options) {\n        // This function is run so that `this` refers to the current document which provides access\n        // to the document's fields and virtuals.\n        if (this.hiddenB < 10) {\n          return false;\n        }\n\n        // The `options` argument can contain additional information about the user and its\n        // entitlements, `orgId`, etc. Entitlement restrictions don't have to follow any\n        // specific pattern but can be created as needed.\n        const { restriction } = options.entitlements.entitlementA;\n        if (restriction.hiddenA && restriction.hiddenA !== this.hiddenA) {\n          return false;\n        }\n      }\n    },\n\n    // `conditionalEdit` builds on the `edit` limitations. In this example the user must\n    // at least have `entitlementB`. The other restrictions are described below.\n    customEditField: {\n      type: Number,\n      entitlements: {\n        edit: ['entitlementB'],\n        conditionalEdit: function (value, options) {\n          if (this.hiddenA !== 'active') {\n            throw new EntitlementError('cannot edit `customEditField` while inactive');\n          }\n\n          const { restriction } = options.entitlements.entitlementA;\n          if (value > restriction.maxCustomEditFieldValue) {\n            throw new EntitlementError('you cannot set `customeEditField` to greater than ...');\n          }\n        }\n      }\n    }\n  }\n});\n```\n\n### `setForUser` instance method\nThe `DMXSchema` provides an instance method `setForUser` which is an analog to the standard `mongoose` `set` method but takes an additional argument. This argument is passed in as the `options` argument to the `conditionalEdit` function. It is expected to at least contain `entitlements`, `userId` and `orgId` but any custom fields can be included.\n\nThe `document.setForUser` method supports two different argument formats:\n* `document.setForUser(key, value, options)` e.g. `foo.setForUser('bar', 'baz', { entitlements...})`\n* `document.setForUser(object, options)` e.g. `foo.setForUser({ bar: 'baz' }, { entitlements...})`\n\n### Virtual fields\n\nThe schema also supports `entitlements` configuration for viewing/editing limitations on virtuals\n```javascript\nschema.virtual('virtualField', {\n  entitlements: {\n    view: ['entitlementA'],\n    conditionalView: function(options) {\n      return options.orgId === this.dealerId;\n    },\n    edit: ['entitlementB'],\n    conditionalEdit: function (value, options) {\n      if (value > this.someOtherField && !options.isDMXUser) {\n        throw new EntitlementError('nope');\n      }\n    }\n  }\n}).get(...).set(...);\n```\n\n### EntitlementError\nThe `EntitlementError` class extends `Error` and supports the following fields\n* `requiredEntitlements` an array of entitlements required to successfully execute the rejected action\n* `field` string containing the name of the field which was unable to be updated\n* `collection` name of the model collection\n\n```javascript\n  throw new EntitlementError('Some error message', {\n    requiredEntitlements: ['entitlementA', 'entitlementB'],\n    field: 'some.field.path',\n    collection: 'someCollection'\n  });\n```\n\n### `Model.sanitize` method\nThis method will return plain object(s) which only include the fields which should be visible to the user (based on the `options` object). It supports single documents and arrays of documents\n* `sanitizedArray = Model.sanitize([documents], options)`\n* `sanitizedDocument = Model.sanitize(document, options)`\n\n### `document.sanitize`\nThis method will return a plain object which only includes the fields which should be visible to the user (based on the `options` object)\n* `document.sanitize(options)`\n\n## Message queue clients\nIn addition to publishing/subscribing clients encapsulate logic to create queues, exchanges and bindings to avoid the need to create queues manually in RabbitMQ console in all environments. These operations are idempotent.\n\n### PubMessageQueueClient\n#### publish()\nPublishes message with `routingKey` (that is basically event name) to exchange with `exchangeName` name.\nIn case such exchange is missing it'll be created to avoid getting `Exchange does not exist` error.\n\n### SubMessageQueueClient\nSubscribes handlers to messages from certain queues.\nIn case corresponding queues and exchanges are missing they'll be created to avoid getting `Queue does not exist` and similar errors.\nIn addition queues are ensured to be bounded to exchanges.\n\n","readmeFilename":"README.md"}