@trigo/fsm
Advanced tools
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <base data-ice="baseUrl" href="../../"> | ||
| <title data-ice="title">lib/parse-transition-api.js | API Document</title> | ||
| <link type="text/css" rel="stylesheet" href="css/style.css"> | ||
| <link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css"> | ||
| <script src="script/prettify/prettify.js"></script> | ||
| <script src="script/manual.js"></script> | ||
| </head> | ||
| <body class="layout-container" data-ice="rootContainer"> | ||
| <header> | ||
| <a href="./">Home</a> | ||
| <a href="identifiers.html">Reference</a> | ||
| <a href="source.html">Source</a> | ||
| <div class="search-box"> | ||
| <span> | ||
| <img src="./image/search.png"> | ||
| <span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span> | ||
| </span> | ||
| <ul class="search-result"></ul> | ||
| </div> | ||
| </header> | ||
| <nav class="navigation" data-ice="nav"><div> | ||
| <ul> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-findpossibletransitions">findpossibletransitions</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-isvalidstatevalue">isvalidstatevalue</a></span></span></li> | ||
| </ul> | ||
| </div> | ||
| </nav> | ||
| <div class="content" data-ice="content"><h1 data-ice="title">lib/parse-transition-api.js</h1> | ||
| <pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">'use strict'; | ||
| function getNested(theObject, path) { | ||
| try { | ||
| const separator = '.'; | ||
| return path | ||
| .replace('[', separator).replace(']', '') | ||
| .split(separator) | ||
| .reduce( | ||
| (obj, property) => obj[property], theObject, | ||
| ); | ||
| } catch (err) { | ||
| return undefined; | ||
| } | ||
| } | ||
| const replaceFromParams = (path, params, ctx) => { | ||
| let href = path; | ||
| Object.keys(params).forEach((key) => { | ||
| const value = getNested(ctx, params[key]); | ||
| if (value === undefined) { | ||
| throw new Error(`Cannot resolve param: "${key}" data path: "${params[key]}"`); | ||
| } | ||
| href = href.replace(new RegExp(`{${key}}`, 'g'), value); | ||
| }); | ||
| return href; | ||
| }; | ||
| const extractParams = (path) => { | ||
| const matches = path.match(/\{.*?\}/g); | ||
| if (!matches) return undefined; | ||
| const params = {}; | ||
| matches.forEach((m) => { | ||
| params[m.replace(/\{/, '').replace(/\}/, '')] = true; | ||
| }); | ||
| return params; | ||
| }; | ||
| /** @private */ | ||
| module.exports = ({ api, ctx }) => { | ||
| if (!api) throw new Error('Argument "api" missing'); | ||
| if (!api.path) throw new Error('Argument "api.path" missing'); | ||
| const method = api.method || 'get'; | ||
| let href = api.path; | ||
| if (ctx && ctx.api && ctx.api.params) { | ||
| href = replaceFromParams(href, ctx.api.params, ctx); | ||
| } | ||
| if (api.params) { | ||
| href = replaceFromParams(href, api.params, ctx); | ||
| } | ||
| const params = extractParams(href); | ||
| const link = { | ||
| href, | ||
| method, | ||
| }; | ||
| if (params) { | ||
| link.params = params; | ||
| } | ||
| return link; | ||
| }; | ||
| </code></pre> | ||
| </div> | ||
| <footer class="footer"> | ||
| Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a> | ||
| </footer> | ||
| <script src="script/search_index.js"></script> | ||
| <script src="script/search.js"></script> | ||
| <script src="script/pretty-print.js"></script> | ||
| <script src="script/inherited-summary.js"></script> | ||
| <script src="script/test-summary.js"></script> | ||
| <script src="script/inner-link.js"></script> | ||
| <script src="script/patch-for-local.js"></script> | ||
| </body> | ||
| </html> |
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <base data-ice="baseUrl" href="../../"> | ||
| <title data-ice="title">lib/parse-transition-api.specs.js | API Document</title> | ||
| <link type="text/css" rel="stylesheet" href="css/style.css"> | ||
| <link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css"> | ||
| <script src="script/prettify/prettify.js"></script> | ||
| <script src="script/manual.js"></script> | ||
| </head> | ||
| <body class="layout-container" data-ice="rootContainer"> | ||
| <header> | ||
| <a href="./">Home</a> | ||
| <a href="identifiers.html">Reference</a> | ||
| <a href="source.html">Source</a> | ||
| <div class="search-box"> | ||
| <span> | ||
| <img src="./image/search.png"> | ||
| <span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span> | ||
| </span> | ||
| <ul class="search-result"></ul> | ||
| </div> | ||
| </header> | ||
| <nav class="navigation" data-ice="nav"><div> | ||
| <ul> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-findpossibletransitions">findpossibletransitions</a></span></span></li> | ||
| <li data-ice="doc"><span data-ice="kind" class="kind-function">F</span><span data-ice="name"><span><a href="function/index.html#static-function-isvalidstatevalue">isvalidstatevalue</a></span></span></li> | ||
| </ul> | ||
| </div> | ||
| </nav> | ||
| <div class="content" data-ice="content"><h1 data-ice="title">lib/parse-transition-api.specs.js</h1> | ||
| <pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">'use strict'; | ||
| const parseApi = require('./parse-transition-api'); | ||
| const { expect } = require('chai'); | ||
| describe('parse transition api', () => { | ||
| it('throws with missing "api"', () => { | ||
| expect(() => parseApi({ })).to.throw('Argument "api" missing'); | ||
| }); | ||
| it('throws with missing "api.path"', () => { | ||
| expect(() => parseApi({ api: { method: 'patch' } })).to.throw('Argument "api.path" missing'); | ||
| }); | ||
| it('returns corrct object', () => { | ||
| const res = parseApi({ api: { | ||
| path: '/test', | ||
| method: 'patch', | ||
| } }); | ||
| expect(res).to.eql({ | ||
| href: '/test', | ||
| method: 'patch', | ||
| }); | ||
| }); | ||
| it('resolves params from ctx object', () => { | ||
| expect(parseApi({ | ||
| api: { | ||
| path: '/test/{resId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.event.resId', | ||
| }, | ||
| }, | ||
| ctx: { | ||
| data: { | ||
| event: { | ||
| resId: '42', | ||
| }, | ||
| }, | ||
| }, | ||
| })).to.eql({ | ||
| href: '/test/42', | ||
| method: 'patch', | ||
| }); | ||
| }); | ||
| it('throws error when param cannot be resolved', () => { | ||
| expect(() => parseApi({ | ||
| api: { | ||
| path: '/test/{resId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.event.resId', | ||
| }, | ||
| }, | ||
| ctx: { | ||
| data: { | ||
| event: { | ||
| }, | ||
| }, | ||
| }, | ||
| })).to.throw('Cannot resolve param: "resId" data path: "data.event.resId"'); | ||
| }); | ||
| it('creates "templates" object for unresolved path segments', () => { | ||
| expect(parseApi({ | ||
| api: { | ||
| path: '/test/{resId}/{eventId}/{seminarId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.event.resId', | ||
| }, | ||
| }, | ||
| ctx: { | ||
| data: { | ||
| event: { | ||
| resId: '42', | ||
| }, | ||
| }, | ||
| }, | ||
| })).to.eql({ | ||
| href: '/test/42/{eventId}/{seminarId}', | ||
| method: 'patch', | ||
| params: { | ||
| eventId: true, | ||
| seminarId: true, | ||
| }, | ||
| }); | ||
| }); | ||
| }); | ||
| </code></pre> | ||
| </div> | ||
| <footer class="footer"> | ||
| Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a> | ||
| </footer> | ||
| <script src="script/search_index.js"></script> | ||
| <script src="script/search.js"></script> | ||
| <script src="script/pretty-print.js"></script> | ||
| <script src="script/inherited-summary.js"></script> | ||
| <script src="script/test-summary.js"></script> | ||
| <script src="script/inner-link.js"></script> | ||
| <script src="script/patch-for-local.js"></script> | ||
| </body> | ||
| </html> |
@@ -44,3 +44,3 @@ <!DOCTYPE html> | ||
| <div class="content" data-ice="content"><div class="header-notice"> | ||
| <div data-ice="importPath" class="import-path"><pre class="prettyprint"><code data-ice="importPathCode">import FSM from '<span><a href="file/lib/fsm.js.html#lineNumber24">@trigo/fsm/lib/fsm.js</a></span>'</code></pre></div> | ||
| <div data-ice="importPath" class="import-path"><pre class="prettyprint"><code data-ice="importPathCode">import FSM from '<span><a href="file/lib/fsm.js.html#lineNumber25">@trigo/fsm/lib/fsm.js</a></span>'</code></pre></div> | ||
| <span data-ice="access">public</span> | ||
@@ -51,3 +51,3 @@ <span data-ice="kind">class</span> | ||
| <span data-ice="source">| <span><a href="file/lib/fsm.js.html#lineNumber24">source</a></span></span> | ||
| <span data-ice="source">| <span><a href="file/lib/fsm.js.html#lineNumber25">source</a></span></span> | ||
| </div> | ||
@@ -410,2 +410,30 @@ | ||
| <span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html#instance-method-restApi">restApi</a></span></span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></span> | ||
| </p> | ||
| </div> | ||
| <div> | ||
| <div data-ice="description"><p>Get rest API links for all currently available transitions than define the</p> | ||
| </div> | ||
| </div> | ||
| </td> | ||
| <td> | ||
| </td> | ||
| </tr> | ||
| <tr data-ice="target"> | ||
| <td> | ||
| <span class="access" data-ice="access">public</span> | ||
| <span class="override" data-ice="override"></span> | ||
| </td> | ||
| <td> | ||
| <div> | ||
| <p> | ||
| <span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html#instance-method-transitions">transitions</a></span></span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></span><<span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>></span> | ||
@@ -583,3 +611,3 @@ </p> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber59">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber80">source</a></span></span> | ||
| </span> | ||
@@ -671,6 +699,28 @@ </h3> | ||
| <td data-ice="appendix"></td> | ||
| <td data-ice="description"><p>object containin hander for specific trasaction | ||
| { beforeTransName: async (ctx, args) => {...}, afterTransName: async (ctx, args) => {...}</p> | ||
| </td> | ||
| <td data-ice="description"><p>object containin hander for specific trasaction</p> | ||
| <pre><code><code class="source-code prettyprint">{ | ||
| beforeTransName: async (ctx, args) => {...}, | ||
| afterTransName: async (ctx, args) => {...}, | ||
| }</code> | ||
| </code></pre></td> | ||
| </tr> | ||
| <tr data-ice="property" data-depth="1"> | ||
| <td data-ice="name" data-depth="1">options.api</td> | ||
| <td data-ice="type"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></td> | ||
| <td data-ice="appendix"></td> | ||
| <td data-ice="description"><p>object containing global REST API data:</p> | ||
| <pre><code><code class="source-code prettyprint">{ | ||
| self: { | ||
| path: '/{entityName}/{id}' | ||
| }, | ||
| params: { | ||
| id: 'data.event.id', | ||
| entityName: 'api.data.entityName' | ||
| }, | ||
| data: { | ||
| entityName: 'events' | ||
| } | ||
| }</code> | ||
| </code></pre></td> | ||
| </tr> | ||
| </tbody> | ||
@@ -713,3 +763,3 @@ </table> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber198">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber266">source</a></span></span> | ||
| </span> | ||
@@ -771,3 +821,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber112">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber134">source</a></span></span> | ||
| </span> | ||
@@ -829,3 +879,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber121">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber143">source</a></span></span> | ||
| </span> | ||
@@ -874,3 +924,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber96">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber118">source</a></span></span> | ||
| </span> | ||
@@ -935,3 +985,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber168">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber236">source</a></span></span> | ||
| </span> | ||
@@ -1043,3 +1093,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber142">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber210">source</a></span></span> | ||
| </span> | ||
@@ -1125,3 +1175,3 @@ </h3> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber86">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber108">source</a></span></span> | ||
| </span> | ||
@@ -1189,2 +1239,74 @@ </h3> | ||
| <div class="detail" data-ice="detail"> | ||
| <h3 data-ice="anchor" id="instance-method-restApi"> | ||
| <span class="access" data-ice="access">public</span> | ||
| <span data-ice="name">restApi</span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></span> | ||
| <span class="right-info"> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber176">source</a></span></span> | ||
| </span> | ||
| </h3> | ||
| <div data-ice="description"><p>Get rest API links for all currently available transitions than define the</p> | ||
| </div> | ||
| <div data-ice="properties"> | ||
| </div> | ||
| <div class="return-params" data-ice="returnParams"> | ||
| <h4>Return:</h4> | ||
| <table> | ||
| <tbody> | ||
| <tr> | ||
| <td class="return-type" data-ice="returnType"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></td> | ||
| <td class="return-desc" data-ice="returnDescription"><p>"restApi" object.</p> | ||
| <pre><code class="lang-javascript"><code class="source-code prettyprint">// example output: | ||
| { | ||
| self: { | ||
| href: '/events/42', | ||
| method: 'get' | ||
| }, | ||
| 'transition:name:1': { | ||
| href: '/events/42/transition/{param1}', | ||
| method: 'put', | ||
| params: { | ||
| param1: true, | ||
| } | ||
| } | ||
| }</code> | ||
| </code></pre> | ||
| </td> | ||
| </tr> | ||
| </tbody> | ||
| </table> | ||
| <div data-ice="returnProperties"> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="detail" data-ice="detail"> | ||
| <h3 data-ice="anchor" id="instance-method-transitions"> | ||
@@ -1201,3 +1323,3 @@ <span class="access" data-ice="access">public</span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber130">source</a></span></span> | ||
| <span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber152">source</a></span></span> | ||
| </span> | ||
@@ -1204,0 +1326,0 @@ </h3> |
| { | ||
| "coverage": "88.23%", | ||
| "expectCount": 17, | ||
| "actualCount": 15, | ||
| "coverage": "88.88%", | ||
| "expectCount": 18, | ||
| "actualCount": 16, | ||
| "files": { | ||
@@ -12,4 +12,4 @@ "lib/composit-state.js": { | ||
| "lib/fsm.js": { | ||
| "expectCount": 12, | ||
| "actualCount": 12, | ||
| "expectCount": 13, | ||
| "actualCount": 13, | ||
| "undocumentLines": [] | ||
@@ -16,0 +16,0 @@ }, |
@@ -54,2 +54,3 @@ <!DOCTYPE html> | ||
| const getAllTakenNames = require('./get-all-taken-names'); | ||
| const parseTrasitionApi = require('./parse-transition-api'); | ||
@@ -69,3 +70,2 @@ const callIfSet = async (handler, ctx, args) => { | ||
| class FSM { | ||
| /** | ||
@@ -90,2 +90,3 @@ * Returns the composit state tool used to parse and build state strings from objects | ||
| } | ||
| /** | ||
@@ -103,5 +104,25 @@ * Create a new state machine. | ||
| * @param {object} options.eventHandler object containin hander for specific trasaction | ||
| * { beforeTransName: async (ctx, args) => {...}, afterTransName: async (ctx, args) => {...} | ||
| * ``` | ||
| * { | ||
| * beforeTransName: async (ctx, args) => {...}, | ||
| * afterTransName: async (ctx, args) => {...}, | ||
| * } | ||
| * ``` | ||
| * @param {object} options.api object containing global REST API data: | ||
| * ``` | ||
| * { | ||
| * self: { | ||
| * path: '/{entityName}/{id}' | ||
| * }, | ||
| * params: { | ||
| * id: 'data.event.id', | ||
| * entityName: 'api.data.entityName' | ||
| * }, | ||
| * data: { | ||
| * entityName: 'events' | ||
| * } | ||
| * } | ||
| * ``` | ||
| */ | ||
| constructor({ initialState, transitions, data, saveState, willChangeState, didChangeState, willSaveState, didSaveState, eventHandler }) { | ||
| constructor({ initialState, transitions, data, saveState, willChangeState, didChangeState, willSaveState, didSaveState, eventHandler, api }) { | ||
| this._state = '__uninitialized__'; | ||
@@ -117,2 +138,3 @@ this._transitions = []; | ||
| this._trasitionFunctionNames = []; | ||
| this._api = api || {}; | ||
@@ -183,2 +205,48 @@ if (transitions) { | ||
| /** | ||
| * Get rest API links for all currently available transitions than define the | ||
| * @return {object} "restApi" object. | ||
| * ```javascript | ||
| * // example output: | ||
| * { | ||
| * self: { | ||
| * href: '/events/42', | ||
| * method: 'get' | ||
| * }, | ||
| * 'transition:name:1': { | ||
| * href: '/events/42/transition/{param1}', | ||
| * method: 'put', | ||
| * params: { | ||
| * param1: true, | ||
| * } | ||
| * } | ||
| * } | ||
| * ``` | ||
| */ | ||
| restApi() { | ||
| const api = {}; | ||
| if (this._api && this._api.self) { | ||
| api.self = parseTrasitionApi({ | ||
| api: this._api.self, | ||
| ctx: { | ||
| data: this._data, | ||
| api: this._api, | ||
| }, | ||
| }); | ||
| } | ||
| findPossibleTransitions(this.state, this._transitions) | ||
| .filter(t => t.api) | ||
| .forEach((t) => { | ||
| api[t.name] = parseTrasitionApi({ | ||
| api: t.api, | ||
| ctx: { | ||
| data: this._data, | ||
| api: this._api, | ||
| }, | ||
| }); | ||
| }); | ||
| return api; | ||
| } | ||
| /** | ||
| * Execute a transition | ||
@@ -285,9 +353,9 @@ * | ||
| result.willChangeState = await callIfSet(this._willChangeState, ctx, args); | ||
| ctx.results = Object.assign({}, result); | ||
| result[beforeHandler] = await callIfSet(this._eventHandler[beforeHandler], ctx, args); | ||
| // console.log(`Change state: "${from}" => "${to}"`) | ||
| // console.log(`Change state: "${from}" => "${to}"`) | ||
| this._state = to; | ||
| ctx.results = Object.assign({}, result); | ||
| result[afterHandler] = await callIfSet(this._eventHandler[afterHandler], ctx, args); | ||
| ctx.results = Object.assign({}, result); | ||
| result.didChangeState = await callIfSet(this._didChangeState, ctx, args); | ||
@@ -298,7 +366,11 @@ | ||
| result.willSaveState = await callIfSet(this._willSaveState, ctx, args); | ||
| ctx.results = Object.assign({}, result); | ||
| result.saveState = await this._saveState(ctx, args); | ||
| ctx.results = Object.assign({}, result); | ||
| result.didSaveState = await callIfSet(this._didSaveState, ctx, args); | ||
| } | ||
| ctx.results = Object.assign({}, result); | ||
| result[afterHandler] = await callIfSet(this._eventHandler[afterHandler], ctx, args); | ||
@@ -305,0 +377,0 @@ this.__inTransition = false; |
@@ -324,5 +324,5 @@ <!DOCTYPE html> | ||
| beforeTrans: null, | ||
| afterTrans: null, | ||
| didChangeState: null, | ||
| willSaveState: null, | ||
| didSaveState: null, | ||
| saveState: undefined, | ||
@@ -428,5 +428,5 @@ }, | ||
| beforeTrans: 'beforeTrans', | ||
| afterTrans: 'afterTrans', | ||
| didChangeState: 'didChangeState', | ||
| willSaveState: 'willSaveState', | ||
| didSaveState: 'didSaveState', | ||
| saveState: 'saveState', | ||
@@ -450,5 +450,5 @@ }, | ||
| beforeTrans: 'beforeTrans', | ||
| afterTrans: 'afterTrans', | ||
| didChangeState: 'didChangeState', | ||
| willSaveState: 'willSaveState', | ||
| didSaveState: 'didSaveState', | ||
| saveState: 'saveState', | ||
@@ -465,6 +465,6 @@ }, | ||
| expect(ts[1].name).to.equal('beforeTrans'); | ||
| expect(ts[2].name).to.equal('afterTrans'); | ||
| expect(ts[3].name).to.equal('didChangeState'); | ||
| expect(ts[4].name).to.equal('willSaveState'); | ||
| expect(ts[5].name).to.equal('didSaveState'); | ||
| expect(ts[2].name).to.equal('didChangeState'); | ||
| expect(ts[3].name).to.equal('willSaveState'); | ||
| expect(ts[4].name).to.equal('didSaveState'); | ||
| expect(ts[5].name).to.equal('afterTrans'); | ||
| }); | ||
@@ -520,3 +520,6 @@ | ||
| expect(ts[1].name).to.equal('beforeTrans'); | ||
| expect(ts.length).to.equal(2); | ||
| expect(ts[2].name).to.equal('didChangeState'); | ||
| expect(ts[3].name).to.equal('willSaveState'); | ||
| expect(ts[4].name).to.equal('didSaveState'); | ||
| expect(ts.length).to.equal(5); | ||
| expect(fsm.state).to.equal('b'); | ||
@@ -638,2 +641,146 @@ }); | ||
| }); | ||
| describe('transition REST API', () => { | ||
| let fsm, cfg; | ||
| beforeEach(() => { | ||
| cfg = { | ||
| initialState: 'a', | ||
| transitions: [{ | ||
| name: 't1', | ||
| from: '*', | ||
| to: 'a', | ||
| api: { | ||
| path: '/entity/trans', | ||
| method: 'patch', | ||
| }, | ||
| }, { | ||
| name: 't2', | ||
| from: '*', | ||
| to: 'a', | ||
| api: { | ||
| path: '/entity/{resId}/trans/{subId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.resId', | ||
| subId: 'data._embedded.event.resId', | ||
| }, | ||
| }, | ||
| }, { | ||
| name: 't3', | ||
| from: '*', | ||
| to: 'a', | ||
| }, { | ||
| name: 't4', | ||
| from: '*', | ||
| to: 'a', | ||
| api: { | ||
| path: '/{entity}/{resId}/trans/{subId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.resId', | ||
| subId: 'data._embedded.event.resId', | ||
| }, | ||
| }, | ||
| }], | ||
| saveState: () => {}, | ||
| willChangeState: () => bb.delay(5), | ||
| data: { | ||
| resId: '42', | ||
| _embedded: { | ||
| event: { | ||
| resId: '22', | ||
| }, | ||
| }, | ||
| }, | ||
| }; | ||
| }); | ||
| it('exposes "restApi()" function', async () => { | ||
| fsm = new FSM(cfg); | ||
| expect(fsm.restApi).to.be.a('function'); | ||
| }); | ||
| it('filters transitions without "api" property', async () => { | ||
| fsm = new FSM(cfg); | ||
| const r = fsm.restApi(); | ||
| expect(r.t1).to.exist; | ||
| expect(r.t2).to.exist; | ||
| expect(r.t3).not.to.exist; | ||
| }); | ||
| it('returns parsed api object', async () => { | ||
| fsm = new FSM(cfg); | ||
| const r = fsm.restApi(); | ||
| expect(r.t1).to.eql({ | ||
| href: '/entity/trans', | ||
| method: 'patch', | ||
| }); | ||
| }); | ||
| it('returns "self" when declared', async () => { | ||
| fsm = new FSM(Object.assign({}, cfg, { | ||
| api: { | ||
| self: { | ||
| path: '/entity/{resId}', | ||
| }, | ||
| params: { | ||
| resId: 'data.resId', | ||
| }, | ||
| }, | ||
| })); | ||
| const r = fsm.restApi(); | ||
| expect(r.self).to.eql({ | ||
| href: '/entity/42', | ||
| method: 'get', | ||
| }); | ||
| }); | ||
| it('can mix global & transition local params in same route', () => { | ||
| fsm = new FSM(Object.assign({}, cfg, { | ||
| api: { | ||
| data: { | ||
| entity: 'events', | ||
| }, | ||
| self: { | ||
| path: '/{entity}/{resId}', | ||
| }, | ||
| params: { | ||
| entity: 'api.data.entity', | ||
| resId: 'data.resId', | ||
| }, | ||
| }, | ||
| })); | ||
| const r = fsm.restApi(); | ||
| expect(r.t4).to.eql({ | ||
| href: '/events/42/trans/22', | ||
| method: 'patch', | ||
| }); | ||
| }); | ||
| it('can declare static params data in "api.data" object', () => { | ||
| fsm = new FSM(Object.assign({}, cfg, { | ||
| api: { | ||
| data: { | ||
| entity: 'events', | ||
| }, | ||
| self: { | ||
| path: '/{entity}/{resId}', | ||
| }, | ||
| params: { | ||
| entity: 'api.data.entity', | ||
| resId: 'data.resId', | ||
| }, | ||
| }, | ||
| })); | ||
| const r = fsm.restApi(); | ||
| expect(r.self).to.eql({ | ||
| href: '/events/42', | ||
| method: 'get', | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
@@ -640,0 +787,0 @@ </code></pre> |
| { | ||
| "name": "@trigo/fsm", | ||
| "version": "2.0.0", | ||
| "version": "3.0.1", | ||
| "description": "FSM - Finite State Machine", | ||
@@ -15,11 +15,11 @@ "main": "index.js", | ||
| "bluebird": "^3.5.0", | ||
| "chai": "^3.5.0", | ||
| "chai": "^4.0.2", | ||
| "esdoc": "^0.5.2", | ||
| "eslint": "^3.17.1", | ||
| "eslint-config-airbnb-base": "^11.1.1", | ||
| "eslint-plugin-import": "^2.2.0", | ||
| "eslint-plugin-mocha": "^4.9.0", | ||
| "mocha": "^3.2.0", | ||
| "eslint": "^4.1.1", | ||
| "eslint-config-airbnb-base": "^11.2.0", | ||
| "eslint-plugin-import": "^2.6.1", | ||
| "eslint-plugin-mocha": "^4.11.0", | ||
| "mocha": "^3.4.2", | ||
| "nodemon": "^1.11.0", | ||
| "nyc": "^10.2.0" | ||
| "nyc": "^11.0.3" | ||
| }, | ||
@@ -26,0 +26,0 @@ "nyc": { |
@@ -465,2 +465,8 @@ window.esdocSearchIndex = [ | ||
| [ | ||
| "lib/fsm.js~fsm#restapi", | ||
| "class/lib/fsm.js~FSM.html#instance-method-restApi", | ||
| "lib/fsm.js~FSM#restApi", | ||
| "method" | ||
| ], | ||
| [ | ||
| "lib/fsm.js~fsm#state", | ||
@@ -508,2 +514,14 @@ "class/lib/fsm.js~FSM.html#instance-get-state", | ||
| [ | ||
| "lib/parse-transition-api.js", | ||
| "file/lib/parse-transition-api.js.html", | ||
| "lib/parse-transition-api.js", | ||
| "file" | ||
| ], | ||
| [ | ||
| "lib/parse-transition-api.specs.js", | ||
| "file/lib/parse-transition-api.specs.js.html", | ||
| "lib/parse-transition-api.specs.js", | ||
| "file" | ||
| ], | ||
| [ | ||
| "lib/parse-transition.js", | ||
@@ -510,0 +528,0 @@ "file/lib/parse-transition.js.html", |
+24
-8
@@ -43,3 +43,3 @@ <!DOCTYPE html> | ||
| <div class="content" data-ice="content"><h1>Source <img data-ice="coverageBadge" src="./badge.svg"><span data-ice="totalCoverageCount" class="total-coverage-count">15/17</span></h1> | ||
| <div class="content" data-ice="content"><h1>Source <img data-ice="coverageBadge" src="./badge.svg"><span data-ice="totalCoverageCount" class="total-coverage-count">16/18</span></h1> | ||
@@ -126,6 +126,6 @@ <table class="files-summary" data-ice="files" data-use-coverage="true"> | ||
| <td data-ice="identifier" class="identifiers"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></td> | ||
| <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">12/12</span></td> | ||
| <td style="display: none;" data-ice="size">9296 byte</td> | ||
| <td style="display: none;" data-ice="lines">264</td> | ||
| <td style="display: none;" data-ice="updated">2017-05-03 11:30:09 (UTC)</td> | ||
| <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">13/13</span></td> | ||
| <td style="display: none;" data-ice="size">10575 byte</td> | ||
| <td style="display: none;" data-ice="lines">336</td> | ||
| <td style="display: none;" data-ice="updated">2017-08-04 14:40:32 (UTC)</td> | ||
| </tr> | ||
@@ -136,5 +136,5 @@ <tr data-ice="file"> | ||
| <td class="coverage"><span data-ice="coverage">-</span></td> | ||
| <td style="display: none;" data-ice="size">15802 byte</td> | ||
| <td style="display: none;" data-ice="lines">590</td> | ||
| <td style="display: none;" data-ice="updated">2017-05-03 11:37:52 (UTC)</td> | ||
| <td style="display: none;" data-ice="size">18673 byte</td> | ||
| <td style="display: none;" data-ice="lines">737</td> | ||
| <td style="display: none;" data-ice="updated">2017-08-04 14:29:46 (UTC)</td> | ||
| </tr> | ||
@@ -158,2 +158,18 @@ <tr data-ice="file"> | ||
| <tr data-ice="file"> | ||
| <td data-ice="filePath"><span><a href="file/lib/parse-transition-api.js.html">lib/parse-transition-api.js</a></span></td> | ||
| <td data-ice="identifier" class="identifiers">-</td> | ||
| <td class="coverage"><span data-ice="coverage">-</span></td> | ||
| <td style="display: none;" data-ice="size">1404 byte</td> | ||
| <td style="display: none;" data-ice="lines">67</td> | ||
| <td style="display: none;" data-ice="updated">2017-08-04 14:30:03 (UTC)</td> | ||
| </tr> | ||
| <tr data-ice="file"> | ||
| <td data-ice="filePath"><span><a href="file/lib/parse-transition-api.specs.js.html">lib/parse-transition-api.specs.js</a></span></td> | ||
| <td data-ice="identifier" class="identifiers">-</td> | ||
| <td class="coverage"><span data-ice="coverage">-</span></td> | ||
| <td style="display: none;" data-ice="size">1723 byte</td> | ||
| <td style="display: none;" data-ice="lines">91</td> | ||
| <td style="display: none;" data-ice="updated">2017-08-04 14:26:46 (UTC)</td> | ||
| </tr> | ||
| <tr data-ice="file"> | ||
| <td data-ice="filePath"><span><a href="file/lib/parse-transition.js.html">lib/parse-transition.js</a></span></td> | ||
@@ -160,0 +176,0 @@ <td data-ice="identifier" class="identifiers">-</td> |
+39
-2
@@ -45,2 +45,3 @@ 'use strict'; | ||
| } | ||
| /** | ||
@@ -58,3 +59,23 @@ * Create a new state machine. | ||
| * @param {object} options.eventHandler object containin hander for specific trasaction | ||
| * { beforeTransName: async (ctx, args) => {...}, afterTransName: async (ctx, args) => {...} | ||
| * ``` | ||
| * { | ||
| * beforeTransName: async (ctx, args) => {...}, | ||
| * afterTransName: async (ctx, args) => {...}, | ||
| * } | ||
| * ``` | ||
| * @param {object} options.api object containing global REST API data: | ||
| * ``` | ||
| * { | ||
| * self: { | ||
| * path: '/{entityName}/{id}' | ||
| * }, | ||
| * params: { | ||
| * id: 'data.event.id', | ||
| * entityName: 'api.data.entityName' | ||
| * }, | ||
| * data: { | ||
| * entityName: 'events' | ||
| * } | ||
| * } | ||
| * ``` | ||
| */ | ||
@@ -139,3 +160,19 @@ constructor({ initialState, transitions, data, saveState, willChangeState, didChangeState, willSaveState, didSaveState, eventHandler, api }) { | ||
| * Get rest API links for all currently available transitions than define the | ||
| * "restApi" object | ||
| * @return {object} "restApi" object. | ||
| * ```javascript | ||
| * // example output: | ||
| * { | ||
| * self: { | ||
| * href: '/events/42', | ||
| * method: 'get' | ||
| * }, | ||
| * 'transition:name:1': { | ||
| * href: '/events/42/transition/{param1}', | ||
| * method: 'put', | ||
| * params: { | ||
| * param1: true, | ||
| * } | ||
| * } | ||
| * } | ||
| * ``` | ||
| */ | ||
@@ -142,0 +179,0 @@ restApi() { |
+3
-3
@@ -613,3 +613,3 @@ 'use strict'; | ||
| api: { | ||
| path: '/entity/{resId}/{trans}/{subId}', | ||
| path: '/entity/{resId}/trans/{subId}', | ||
| method: 'patch', | ||
@@ -630,3 +630,3 @@ params: { | ||
| api: { | ||
| path: '/{entity}/{resId}/{trans}/{subId}', | ||
| path: '/{entity}/{resId}/trans/{subId}', | ||
| method: 'patch', | ||
@@ -710,3 +710,3 @@ params: { | ||
| expect(r.t4).to.eql({ | ||
| href: '/events/42/{trans}/22', | ||
| href: '/events/42/trans/22', | ||
| method: 'patch', | ||
@@ -713,0 +713,0 @@ }); |
@@ -31,2 +31,13 @@ 'use strict'; | ||
| const extractParams = (path) => { | ||
| const matches = path.match(/\{.*?\}/g); | ||
| if (!matches) return undefined; | ||
| const params = {}; | ||
| matches.forEach((m) => { | ||
| params[m.replace(/\{/, '').replace(/\}/, '')] = true; | ||
| }); | ||
| return params; | ||
| }; | ||
| /** @private */ | ||
| module.exports = ({ api, ctx }) => { | ||
@@ -46,6 +57,13 @@ if (!api) throw new Error('Argument "api" missing'); | ||
| return { | ||
| const params = extractParams(href); | ||
| const link = { | ||
| href, | ||
| method, | ||
| }; | ||
| if (params) { | ||
| link.params = params; | ||
| } | ||
| return link; | ||
| }; |
@@ -65,2 +65,28 @@ 'use strict'; | ||
| }); | ||
| it('creates "templates" object for unresolved path segments', () => { | ||
| expect(parseApi({ | ||
| api: { | ||
| path: '/test/{resId}/{eventId}/{seminarId}', | ||
| method: 'patch', | ||
| params: { | ||
| resId: 'data.event.resId', | ||
| }, | ||
| }, | ||
| ctx: { | ||
| data: { | ||
| event: { | ||
| resId: '42', | ||
| }, | ||
| }, | ||
| }, | ||
| })).to.eql({ | ||
| href: '/test/42/{eventId}/{seminarId}', | ||
| method: 'patch', | ||
| params: { | ||
| eventId: true, | ||
| seminarId: true, | ||
| }, | ||
| }); | ||
| }); | ||
| }); |
+1
-1
| { | ||
| "name": "@trigo/fsm", | ||
| "version": "3.0.1", | ||
| "version": "3.1.0", | ||
| "description": "FSM - Finite State Machine", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
13693525
17.6%100
4.17%226091
19.78%