{"action":"created","issue":{"url":"https://api.github.com/repos/automategreen/dynamoose/issues/325","repository_url":"https://api.github.com/repos/automategreen/dynamoose","labels_url":"https://api.github.com/repos/automategreen/dynamoose/issues/325/labels{/name}","comments_url":"https://api.github.com/repos/automategreen/dynamoose/issues/325/comments","events_url":"https://api.github.com/repos/automategreen/dynamoose/issues/325/events","html_url":"https://github.com/automategreen/dynamoose/pull/325","id":307897245,"number":325,"title":"Plugin Support","user":{"login":"fishcharlie","id":860375,"avatar_url":"https://avatars0.githubusercontent.com/u/860375?v=4","gravatar_id":"","url":"https://api.github.com/users/fishcharlie","html_url":"https://github.com/fishcharlie","followers_url":"https://api.github.com/users/fishcharlie/followers","following_url":"https://api.github.com/users/fishcharlie/following{/other_user}","gists_url":"https://api.github.com/users/fishcharlie/gists{/gist_id}","starred_url":"https://api.github.com/users/fishcharlie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fishcharlie/subscriptions","organizations_url":"https://api.github.com/users/fishcharlie/orgs","repos_url":"https://api.github.com/users/fishcharlie/repos","events_url":"https://api.github.com/users/fishcharlie/events{/privacy}","received_events_url":"https://api.github.com/users/fishcharlie/received_events","type":"User","site_admin":false},"labels":[{"id":81334122,"url":"https://api.github.com/repos/automategreen/dynamoose/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":876436307,"url":"https://api.github.com/repos/automategreen/dynamoose/labels/help%20wanted","name":"help wanted","color":"006b75","default":true},{"id":862806344,"url":"https://api.github.com/repos/automategreen/dynamoose/labels/in%20progress","name":"in progress","color":"3a63e0","default":false}],"state":"open","locked":false,"assignee":{"login":"fishcharlie","id":860375,"avatar_url":"https://avatars0.githubusercontent.com/u/860375?v=4","gravatar_id":"","url":"https://api.github.com/users/fishcharlie","html_url":"https://github.com/fishcharlie","followers_url":"https://api.github.com/users/fishcharlie/followers","following_url":"https://api.github.com/users/fishcharlie/following{/other_user}","gists_url":"https://api.github.com/users/fishcharlie/gists{/gist_id}","starred_url":"https://api.github.com/users/fishcharlie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fishcharlie/subscriptions","organizations_url":"https://api.github.com/users/fishcharlie/orgs","repos_url":"https://api.github.com/users/fishcharlie/repos","events_url":"https://api.github.com/users/fishcharlie/events{/privacy}","received_events_url":"https://api.github.com/users/fishcharlie/received_events","type":"User","site_admin":false},"assignees":[{"login":"fishcharlie","id":860375,"avatar_url":"https://avatars0.githubusercontent.com/u/860375?v=4","gravatar_id":"","url":"https://api.github.com/users/fishcharlie","html_url":"https://github.com/fishcharlie","followers_url":"https://api.github.com/users/fishcharlie/followers","following_url":"https://api.github.com/users/fishcharlie/following{/other_user}","gists_url":"https://api.github.com/users/fishcharlie/gists{/gist_id}","starred_url":"https://api.github.com/users/fishcharlie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fishcharlie/subscriptions","organizations_url":"https://api.github.com/users/fishcharlie/orgs","repos_url":"https://api.github.com/users/fishcharlie/repos","events_url":"https://api.github.com/users/fishcharlie/events{/privacy}","received_events_url":"https://api.github.com/users/fishcharlie/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/automategreen/dynamoose/milestones/3","html_url":"https://github.com/automategreen/dynamoose/milestone/3","labels_url":"https://api.github.com/repos/automategreen/dynamoose/milestones/3/labels","id":2558168,"number":3,"title":"v0.9.0","description":"The goal of release 0.9 is to increase the parity with mongoose.  The primary purpose will be to come up with a plugin system similar to that of mongoose although not necessarily compatible.","creator":{"login":"brandongoode","id":5034905,"avatar_url":"https://avatars1.githubusercontent.com/u/5034905?v=4","gravatar_id":"","url":"https://api.github.com/users/brandongoode","html_url":"https://github.com/brandongoode","followers_url":"https://api.github.com/users/brandongoode/followers","following_url":"https://api.github.com/users/brandongoode/following{/other_user}","gists_url":"https://api.github.com/users/brandongoode/gists{/gist_id}","starred_url":"https://api.github.com/users/brandongoode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brandongoode/subscriptions","organizations_url":"https://api.github.com/users/brandongoode/orgs","repos_url":"https://api.github.com/users/brandongoode/repos","events_url":"https://api.github.com/users/brandongoode/events{/privacy}","received_events_url":"https://api.github.com/users/brandongoode/received_events","type":"User","site_admin":false},"open_issues":29,"closed_issues":1,"state":"open","created_at":"2017-06-02T10:53:16Z","updated_at":"2018-04-25T16:01:54Z","due_on":null,"closed_at":null},"comments":3,"created_at":"2018-03-23T04:15:34Z","updated_at":"2018-05-11T16:54:11Z","closed_at":null,"author_association":"COLLABORATOR","pull_request":{"url":"https://api.github.com/repos/automategreen/dynamoose/pulls/325","html_url":"https://github.com/automategreen/dynamoose/pull/325","diff_url":"https://github.com/automategreen/dynamoose/pull/325.diff","patch_url":"https://github.com/automategreen/dynamoose/pull/325.patch"},"body":"# Plugin Support\r\n\r\n## Summary\r\n\r\nIt's **finally** here (or coming soon, if this isn't merged in yet). Plugin Support is coming to Dynamoose. We are currently aiming to release this with Dynamoose version 0.9.0.\r\n\r\nI'm creating this PR *way* too early. The reason for this is I'm trying to build this feature in the open to receive feedback and suggestions so the community can help build this and make it the best it can be.\r\n\r\n## Ideas/Structure\r\n\r\n- Plugins will be linked to models (possibly Dynamoose defaults as well in the future to link a plugin to all models, but will be integrated heavily into Dynamoose models)\r\n- It will be possible to register multiple plugins to one model\r\n- Plugins will watch for events to be \"sent\" or \"emitted\" from Dynamoose and can manipulate or change data\r\n- If `*` is passed into action, in the `.on` method it will match all emitted types. Or maybe we can make both `action`/`type` and `stage` optional.\r\n\r\n## Ideas we are exploring\r\n\r\n- I would love to be able to support promises in plugins, so when an event is sent to to plugin the plugin can return a promise that Dynamoose will wait for the promise to resolve before Dynamoose moves on in it's logic. I'm not quite sure how to do this considering the easiest way it `await` and Dynamoose still needs to support older Node.js versions. Any suggestions on easy ways to implement this would be awesome. ***\r\n- Ability to bypass/prevent default of Dynamoose actions. For example, if we want to override table creation, or bypass getting an item from DynamoDB to get it from another source or something. ***\r\n    - Maybe we can pass in `resolve` and `reject` functions to the object that gets passed into the plugin listener. The question then becomes how we can escape out of the Dynamoose function if that happens. This might be super easy, but I haven't looked into the codebase quite enough to determine if this is a realistic idea.\r\n- Add support for plugins requiring a certain version of Dynamoose (and/or Node.js?)\r\n- Add feature to [dynamoosejs.com](https://dynamoosejs.com) to search plugins\r\n\r\nItems marked with `***` means that we are currently unsure how to implement this feature. Although it would be a cool feature, the details of the idea haven't been thought through to a level that makes implementation possible.\r\n\r\n## Potential Examples\r\n\r\n### Possible Plugin Implementation\r\n\r\n```js\r\nmodule.exports = function(obj, options) {\r\n\tobj.setName(“My Plugin”); // required\r\n\tobj.setDescription(“”); // optional\r\n\tobj.on(‘init’, function() { // this will handle all stages related to init\r\n\t\tconsole.log(“Plugin registered”);\r\n\t});\r\n\tobj.on(‘scan’, ‘preRequest’, function(objB) { // this will handle only preRequest stages on the scan type\r\n\t\tconsole.log(“About to make request to DynamoDB”);\r\n\t});\r\n\tobj.on(‘scan’, ‘postRequest’, function(objB) { // this will handle only postRequest stages on the scan type, and will wait for promise to resolve before moving on (NOT SURE IF WE WILL SUPPORT THIS, SEE COMMENT ABOVE)\r\n\t\treturn new Promise(function(resolve, reject) {\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n\treturn obj;\r\n}\r\n```\r\n\r\nThe `obj.on` method will add listeners that will be called when Dynamoose emits certain actions. It will accept 3 properties (action, [stage ,] func). `action` is a higher level stage that Dynamoose will emit. `stage` is a lower level stage that gives detail about where in the process Dynamoose is (for example right before AWS request, right after AWS request) all within a higher level action such as (.get, .scan, etc). If `stage` is not passed in, the function will be called for all emitted actions that match.\r\n\r\n### Adding Plugin to Model\r\n\r\n```js\r\nvar MyPlugin = require('ThePluginPackage');\r\nvar MyPluginB = require('ThePluginPackageB');\r\nvar Model = dynamoose.model('Puppy', {\r\n    id: {\r\n        type: Number,\r\n        validate: function(v) {\r\n            return v > 0;\r\n        }\r\n    },\r\n    name: String,\r\n    owner: String,\r\n    age: {\r\n        type: Number\r\n    }\r\n});\r\nModel.plugin(MyPlugin); // this plugin will always take priority over the plugin below and be run first\r\nModel.plugin(MyPluginB, {username: 'test', password: 'test'}); // this plugin will always take priority second and will be run after the first plugin, this plugin also passes options into the plugin\r\n\r\nmodule.exports = Model;\r\n```\r\n\r\n## Potential actions/types to emit\r\n\r\n- Plugin Register (“plugin:register”) (**)\r\n- AWS Table creation (“table:create”)\r\n- Scan (“model:scan”) (**)\r\n- Query (“model:query”) (**)\r\n- Save (“newmodel:save”) (this looks to be `put`)\r\n- Put (“model:put”) (**)\r\n- BatchPut (“model:batchput”) (might consider later)\r\n- Create (“model:create”) (might consider later)\r\n- Get (“model:get”) (**)\r\n- Populate (“model:populate”) (might consider later)\r\n- New Model Item (“newmodel:new”) (might consider later)\r\n\r\n`**` indicates an action that has been fully implemented.\r\n\r\n## Potential stages to emit\r\n\r\nStages (examples, depends on method):\r\n\r\n- Right when call is first made (right when .save call is made)\r\n- After Dynamoose has run the Dynamoify function\r\n- Right before call will be made to \r\n\r\n## Use Cases we need to support\r\n\r\n- Issue #18 \r\n- Issue #159 (might need to add original object received from DynamoDB to instance)\r\n- [Passport.js Plugin Support](https://www.npmjs.com/package/passport-local-mongoose)\r\n- PR #317 (maybe we don't need to support this not)\r\n\r\n## Todo Items\r\n\r\n- Build some example/test plugins\r\n\r\n## Questions\r\n\r\n- Is plugin compatibility a concern? And if so, should/can we do anything about it?\r\n- Is having too many plugins/listeners on a model a concern? Any ideas on how to increase performance or is this a non issue?\r\n\r\n## Other Notes\r\n\r\n- I will be updating this PR with more detail as I work on it more\r\n- Feel free to comment or [contact me](https://blog.charlie.fish/contact/) if you have suggestions, questions, ideas, etc.\r\n- Close #18 "},"comment":{"url":"https://api.github.com/repos/automategreen/dynamoose/issues/comments/388421842","html_url":"https://github.com/automategreen/dynamoose/pull/325#issuecomment-388421842","issue_url":"https://api.github.com/repos/automategreen/dynamoose/issues/325","id":388421842,"user":{"login":"fishcharlie","id":860375,"avatar_url":"https://avatars0.githubusercontent.com/u/860375?v=4","gravatar_id":"","url":"https://api.github.com/users/fishcharlie","html_url":"https://github.com/fishcharlie","followers_url":"https://api.github.com/users/fishcharlie/followers","following_url":"https://api.github.com/users/fishcharlie/following{/other_user}","gists_url":"https://api.github.com/users/fishcharlie/gists{/gist_id}","starred_url":"https://api.github.com/users/fishcharlie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fishcharlie/subscriptions","organizations_url":"https://api.github.com/users/fishcharlie/orgs","repos_url":"https://api.github.com/users/fishcharlie/repos","events_url":"https://api.github.com/users/fishcharlie/events{/privacy}","received_events_url":"https://api.github.com/users/fishcharlie/received_events","type":"User","site_admin":false},"created_at":"2018-05-11T16:54:10Z","updated_at":"2018-05-11T16:54:10Z","author_association":"COLLABORATOR","body":"@mogwai The only clean way I can think of how to do this is to use the `async`/`await` syntax. I think adding in this support might have to come in version 1.0. Or do you feel like it's a big enough feature that needs to be released alongside plugin support?"}}
{"actor":{"display_login":"fishcharlie"}}