UNPKG

copious-transitions

Version:
524 lines (475 loc) 24.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: lib/general_transition_engine.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: lib/general_transition_engine.js</h1> <section> <article> <pre class="prettyprint source linenums"><code> const AppLifeCycle = require("./general_lifecyle") const fs_pr = require('fs/promises') /** * The General Transition Engine provides basic transitions for files and media while keeping the * abstraction for managing and executing transtions which may be of varying complexity and asynchronicity. * * Ideally, the most advanced transition engine will operate distributed ATMs or Petri nets with the web interface * taking part a an active input driver from among many input drivers. * * * NOTE: all the base classes in /lib return the class and do not return an instance. Explore the applications and * the reader will find that the descendant modules export instances. The classes provided by copious-transitions must * be extended by an application. * * @memberof base */ class GeneralTransitionEngImpl extends AppLifeCycle { // constructor() { super() this.db = null this.statics = null this.dynamics = null this._uploader_managers = {} this.root_path = process.cwd() // this.trans_processor = false this.user_processor = false this.endpoint_service = false this.web_sockets = false } /** * At this level, this method just sets the configuration and the database referenc. * @param {object} conf * @param {object} db */ initialize(conf,db) { this.conf = conf this.db = db } /** * This method sets back references to other lib component class instances. * It also tends to the importation of crypto keys. * * @param {object} `statics_assets` * @param {object} `dynamics_assets` * @param {object} sessions */ install(statics_assets,dynamics_assets,sessions) { this.sessions = sessions this.statics = statics_assets this.dynamics = dynamics_assets this.statics.set_transition_engine(this) this.dynamics.set_transition_engine(this) // dynamics_assets.import_keys(this.get_import_key_function()) } /** * This method sets back references to the contractual methods which provide the skeletal outline * for handling client requests. The contractual class methods, especially those for transition processing, * are generally the callers of the application's transition engine. But, the transition engine may call * back to those classes if needed (depends on the application). * * * @param {object} `trans_processor` * @param {object} `user_processor` * @param {object} `mime_processor` */ set_contractual_filters(trans_processor,user_processor,mime_processor) { this.trans_processor = trans_processor this.user_processor = user_processor this.mime_processor = mime_processor } /** * Accepts a reference to the application supplied web socket server manager and sets the `web_sockets` field to it. * * At times, the transition engine will use the web socket service to fire off messages to listening clients. * * @param {object} `web_sockets` - the reference to the application supplied web socket server manager. */ set_ws(web_sockets) { this.web_sockets = web_sockets } /** * This method returns false. It should be overridden in applications using crytpo key processing. * * @returns {string|boolean} */ get_import_key_function() { return(false) } /** * Calls the node.js Buffer.concat method. Some application may do something else. * Returns a buffer. * * @param {Buffer} `blob_data` * @returns {Buffer} */ chunks_to_data(blob_data) { return Buffer.concat(blob_data) } /** * A number of parameters are provided for applications needing more complexity than this method provides. * * As a default, this method calls node.js `mv`. * * @param {object} `file_descriptor` * @param {string} `target_path` * @param {object} `trans_obj` * @param {Function} cb * @returns {Number} */ async file_mover(file_descriptor,target_path,trans_obj,cb) { file_descriptor.mv(target_path,cb) return(Math.floor(Math.random()*10000)) // default random int } /** * By default, this method calls the async writeFile method to store data. * Some applications may need to route storage at this point. * * @param {object} `file_descriptor` * @param {string} `target_path` * @param {string} `writeable_data` * @param {string} id * @returns {string} */ async store_data(file_descriptor,target_path,writeable_data,id) { await fs_pr.writeFile(target_path,writeable_data) return id } /** * This method is provided for those applications using persistent storage use meta descriptors of files. * * @param {object} `post_body` * @param {Array} ids */ async update_meta_descriptors(post_body,ids) {} /** * Returns true if the application override is configured to use alternate storage to the default storage provided here. * * @returns {boolean} */ alt_store() { return false } /** * Replaces the functionality of `store_data` if alt_store returns true. * * In the application version of this method, the point is to return data that can be written into a * file using the method `store_data`. * * @param {object} blob_data */ app_pack_data(blob_data) {} // chunk_mover // per file chunk mover /** * * The map `_uploader_managers` stores a chunk manager per transition token. * Elsewhere in this documentation, the transition object and its token is explained. * One kind of transition is one that takes in data from the client, and its token * will last as long as the data is being uploaded. * * The chunk manager has the purpose of return a collection of chunks in preparation for writing to storage. * In some applications, the `chunk_mover` may return **false** if the chunks cannot be a complete file. * The expectation is that the chunk mover will be called again after adding more chunks, until the data can be stored * as a complete object. * * When the storage operation has been performed, the `store_data` method will return an identifier that identifies the data. * This may be a hash of the data. Ultimately, this identifier will be returned by this method. * * This method is called by `chunks_complete`. * * @param {string} `token` * @param {object} `file_descriptor` * @param {string} `target_path` * @param {Function} `cb` * @returns {string|Function} */ async chunk_mover(token,file_descriptor,target_path,cb) { // let chunk_manager = this._uploader_managers[token] let blob_data = false if ( chunk_manager._chunks !== undefined ) { // ONE FILE -- one array of chunks blob_data = chunk_manager._chunks } else if ( chunk_manager._chunkers !== undefined ) { // MANY FILES -- map file names to arrays of chunks blob_data = chunk_manager._chunkers[file_descriptor.name] } else { // COULD NOT PERFORM OP return false } let id = false if ( blob_data ) { let writeable_data = this.alt_store() ? this.app_pack_data(blob_data) : this.chunks_to_data(blob_data) id = await this.store_data(file_descriptor,target_path,writeable_data,id) } try { if ( cb ) cb() } catch (e) {} // return(id) } /** * Initializes a chunk manager that will receive a number of files identified in the requet body of an uploader type * of transition. The chunk manager will be keyed (remembered) by its transition token. * * The post body should have a field `file_list` which must be an array of file descriptors. * Each descriptor will have at least one field `name`. The chunk manager will have a map, `_chunkers`, * which maps the file name to an empty array. The empty array will later be filled with chunk data. * * If the post body does not have a `file_list` field, the chunk manager will keep just one array where all the chunks * being uploaded will be placed for future storage in just one file. * * This method is often called by the application's session manager as a part of transition processing. * * @param {object} `post_body` - the POST request body from the client (or a message from the endpoint server) * @param {string} `token` - the chunk manager is always identified by the transtion token */ files_coming_in_chunks(post_body,token) { let chunk_manager = Object.assign({},post_body) if ( post_body.file_list ) { // MULTIPLE FILES. for clients sending more than one file, a list is expected beyond the typical form field list chunk_manager._chunkers = {} for ( let file of post_body.file_list ) { // EACH of file list if ( file.name !== undefined ) { chunk_manager._chunkers[file.name] = [] // chunks // each file in the list has to have at least a name } } } else { chunk_manager._chunks = [] // ONE FILE only } this._uploader_managers[token] = chunk_manager // &lt;-- transaction token gets a chunk manager } // /** * * This method takes in the client's POST request body (or endpoint message) and the list of files associated with it. * In some applications, the *files* map may be part of the request body. In other applications, the *files* map may be associated with a * session token or session and might be cached between calls to the `upload_chunk`. * * The post body must have the `token` field with the value being the transition token required by the request. * * The previously prepared chunk manager will be accessed by mapping the token to the chunk manager in the * `_uploader_managers` map. * * The files parameter must alwasy be a map even if there is only one file in `files`. * If there is just one file, this method will check to see if uploading has been prepared to manage just one file. * If it has been prepared to upload more, it is possible to have just one file if it is the last file still gathering chunks * from the client. Complete files may have already been finalized. (This is why there are differen field names for the single * and multiple case.) * * This method is often called by the application's session manager as a part of secondary transition processing. * * * @param {object} `post_body` * @param {string} `files` * @returns {object} - `state_of_result` */ async upload_chunk(post_body,files) { if ( !files || Object.keys(files).length === 0) { let state_of_result = { "state" : "failed", "OK" : false } return state_of_result } // let token = post_body.token let f_keys = Object.keys(files) let chunk_manager = this._uploader_managers[token] if ( (f_keys.length === 1) &amp;&amp; chunk_manager._chunks ) { // Handling one file let file = files[f_keys[0]] if ( file.data ) { chunk_manager._chunks.push(file.data) } else if ( typeof file === "string" ) { chunk_manager._chunks.push(file) } } else { // Handling multiple files // in a sequece of chunks one or more files may be identified in the post body // the client will seize to send the smaller files while larger ones will be sent // until post bodies indicate that there is no more data to come. if ( chunk_manager._chunkers ) { for ( let file_key in files ) { let file = files[file_key] let chunk_array = chunk_manager._chunkers[file_key] if ( file &amp;&amp; chunk_array ) { if ( file.data ) { chunk_array.push(file.data) } else if ( typeof file === "string" ) { chunk_array.push(file) } } } } } let state_of_result = { "state" : "next", "OK" : true } return state_of_result } /** * * The post body must have the `token` field with the value being the transition token required by the request. * * The previously prepared chunk manager will be accessed by mapping the token to the chunk manager in the * `_uploader_managers` map. * * The post body must have one of the following fields: * * * `file_list` - an array of file descriptors * * `file` - a single descriptor * * If the post body has the field `file_list`,which must be an array of file descriptors, * then each descriptor will have at least one field `name`. The chunk manager will have a map, `_chunkers`, * which maps the file name to the array of gathered chunks. * * If the post body as a `file` field instead of a `file_list` field, the chunk manager will look for the one array * where all the chunks being uploaded have been gathered for storage in just one file. * * This method attempts to create a unique name for the file from the information that has been passed to it. * Furthermore, it makes use of the transition class object's `file_entry_id`. * * This method is often called by the application's session manager as a part of transition finalization. * * @param {object} post_body * @param {object} ttrans - the transition class object * @returns {object} - `finalization_state` - this object has fields "state" equal to "stored" if successful, "OK" equal to **true**, * and a list of ids, which are all the file ids that have been finally stored. */ async chunks_complete(post_body,ttrans) { // let ids = [] let token = post_body.token if ( this._uploader_managers[token] !== undefined ) { // the file_list is sent in the body. // they may have fallen out from the upload process. // But, it is expected that the client will send the list of all files // in the completion post body. if ( post_body.file_list ) { // MORE THAN ONE for ( let uploaded_file of post_body.file_list ) { let ext = uploaded_file.ext ? uploaded_file.ext : "media" let file_differentiator = ttrans.file_entry_id("file") let store_name = `${uploaded_file.name}${file_differentiator}.${ext}` let dir = ttrans.directory() // let file_id = await this.chunk_mover(token,uploaded_file,dir + '/' + store_name,false) if ( file_id !== false ) { // failed transaction will not return identifiers ids.push(file_id) } } } else { // JUST ONE let uploaded_file = post_body.file let ext = post_body.ext ? post_body.ext : "media" let file_differentiator = ttrans.file_entry_id("file") let store_name = `${uploaded_file.name}${file_differentiator}.${ext}` let dir = ttrans.directory() // let file_id = await this.chunk_mover(token,uploaded_file,dir + '/' + store_name,false) if ( file_id !== false ) { // failed transaction will not return identifiers ids.push(file_id) } } } // let finalization_state = { "state" : "stored", "OK" : "true", "ids" : ids } return finalization_state } // /** * This method servers to upload a single file or multiple files in response one request, where * all the files are complete upon arrival. (If chunking has occurred, it has been handled by standard HTTP mechanisms). * * The function of this handler is fairly common code stacks. Other methods in the class are useful in situations * where data undergoes intermediate treatment during data gathering or when data is coming in messages from the endpoint server. * Usually, this method is used when uploading a file from disk and passing it through standard forms. * * This method augments the placement of the file into an application directory by adding descriptors of the file into the DB * (which is again defined by the application). * * @param {object} post_body * @param {object} ttrans * @param {Array} files * @param {object} req * @returns {object} - `finalization_state` */ async upload_file(post_body,ttrans,files,req) { // if ( !files || Object.keys(files).length === 0) { let finalization_state = { "state" : "failed", "OK" : false } return finalization_state } // let ukey = ttrans.primary_key() let proto_file_name = post_body[ukey] let file_name_base = ttrans.transform_file_name(proto_file_name) let ext = post_body.file_type // let ids = [] for ( let file_key in files ) { let uploaded_file = files[file_key] let file_differentiator = ttrans.file_entry_id(file_key) // mv is part of the express.js system let store_name = `${file_name_base}${file_differentiator}.${ext}` let dir = ttrans.directory() let udata = { 'name' : proto_file_name, 'id-source' : ukey, 'id' : proto_file_name, 'pass' : '', 'dir' : dir, 'file' : store_name } ttrans.update_file_db_entry(udata) let file_id = await this.file_mover(uploaded_file,dir + '/' + store_name,ttrans,((uudata,ureq) => { return((err) => { if ( err ) { if ( this.sessions ) { this.sessions.session_accrue_errors("upload",uudata,err,ureq) } } else { this.db.store("upload",uudata) } }); })(udata,req),udata) // ids.push(file_id) } let finalization_state = { "state" : "stored", "OK" : "true", "ids" : ids } return finalization_state } } module.exports = GeneralTransitionEngImpl </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Namespaces</h3><ul><li><a href="Contractual.html">Contractual</a></li><li><a href="CopiousTransitions.html">CopiousTransitions</a></li><li><a href="DefaultDB.html">DefaultDB</a></li><li><a href="base.html">base</a></li><li><a href="field_validators.html">field_validators</a></li></ul><h3>Classes</h3><ul><li><a href="Contractual.LocalTObjectCache.html">LocalTObjectCache</a></li><li><a href="Contractual.MimeHandling.html">MimeHandling</a></li><li><a href="Contractual.TransitionHandling.html">TransitionHandling</a></li><li><a href="Contractual.UserHandling.html">UserHandling</a></li><li><a href="CopiousTransitions.CopiousTransitions.html">CopiousTransitions</a></li><li><a href="DefaultDB.CustomizationMethodsByApplication.html">CustomizationMethodsByApplication</a></li><li><a href="DefaultDB.FauxInMemStore.html">FauxInMemStore</a></li><li><a href="DefaultDB.FileMapper.html">FileMapper</a></li><li><a href="DefaultDB.FilesAndRelays.html">FilesAndRelays</a></li><li><a href="DefaultDB.FilesAndRelays_base.html">FilesAndRelays_base</a></li><li><a href="DefaultDB.LocalStaticDB.html">LocalStaticDB</a></li><li><a href="DefaultDB.LocalStorageLifeCycle.html">LocalStorageLifeCycle</a></li><li><a href="DefaultDB.LocalStorageSerialization.html">LocalStorageSerialization</a></li><li><a href="DefaultDB.PageableMemStoreElement.html">PageableMemStoreElement</a></li><li><a href="DefaultDB.PersistenceContracts.html">PersistenceContracts</a></li><li><a href="DefaultDB.RemoteMessaging.html">RemoteMessaging</a></li><li><a href="DefaultDB.StaticDBDefault.html">StaticDBDefault</a></li><li><a href="GeneralUserDBWrapperImpl.html">GeneralUserDBWrapperImpl</a></li><li><a href="SessionTokenManager.html">SessionTokenManager</a></li><li><a href="base.DBClass.html">DBClass</a></li><li><a href="base.EndpointManager.html">EndpointManager</a></li><li><a href="base.GeneralAppLifeCycle.html">GeneralAppLifeCycle</a></li><li><a href="base.GeneralAuth.html">GeneralAuth</a></li><li><a href="base.GeneralBusiness.html">GeneralBusiness</a></li><li><a href="base.GeneralDynamic.html">GeneralDynamic</a></li><li><a href="base.GeneralMiddleWare.html">GeneralMiddleWare</a></li><li><a href="base.GeneralStatic.html">GeneralStatic</a></li><li><a href="base.GeneralTransitionEngImpl.html">GeneralTransitionEngImpl</a></li><li><a href="base.SessionManager.html">SessionManager</a></li><li><a href="base.SessionManager_Lite.html">SessionManager_Lite</a></li><li><a href="base.TaggedTransition.html">TaggedTransition</a></li><li><a href="base.TokenTables.html">TokenTables</a></li><li><a href="base.UserMessageEndpoint.html">UserMessageEndpoint</a></li><li><a href="base.WebSocketManager.html">WebSocketManager</a></li><li><a href="field_validators.DataLookupField.html">DataLookupField</a></li><li><a href="field_validators.EmailField.html">EmailField</a></li><li><a href="field_validators.EmailVerifyField.html">EmailVerifyField</a></li><li><a href="field_validators.FieldTest.html">FieldTest</a></li><li><a href="field_validators.FieldValidatorTools.html">FieldValidatorTools</a></li><li><a href="field_validators.ForeignAuth.html">ForeignAuth</a></li><li><a href="field_validators.GeneralValidator.html">GeneralValidator</a></li><li><a href="field_validators.LengthyAlphabetField.html">LengthyAlphabetField</a></li><li><a href="field_validators.LengthyDigitalField.html">LengthyDigitalField</a></li><li><a href="field_validators.LengthyField.html">LengthyField</a></li><li><a href="field_validators.LengthyStringField.html">LengthyStringField</a></li><li><a href="field_validators.PasswordField.html">PasswordField</a></li><li><a href="field_validators.PasswordVerifyField.html">PasswordVerifyField</a></li><li><a href="field_validators.TypeCheckField.html">TypeCheckField</a></li></ul><h3>Global</h3><ul><li><a href="global.html#generate_password_block">generate_password_block</a></li><li><a href="global.html#load_configuration">load_configuration</a></li><li><a href="global.html#load_parameters">load_parameters</a></li><li><a href="global.html#module_top">module_top</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.2</a> on Tue Oct 31 2023 17:32:59 GMT-0700 (Pacific Daylight Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>