create.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // ██████╗██████╗ ███████╗ █████╗ ████████╗███████╗ █████╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗
  2. // ██╔════╝██╔══██╗██╔════╝██╔══██╗╚══██╔══╝██╔════╝ ██╔══██╗██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║
  3. // ██║ ██████╔╝█████╗ ███████║ ██║ █████╗ ███████║██║ ██║ ██║██║ ██║██╔██╗ ██║
  4. // ██║ ██╔══██╗██╔══╝ ██╔══██║ ██║ ██╔══╝ ██╔══██║██║ ██║ ██║██║ ██║██║╚██╗██║
  5. // ╚██████╗██║ ██║███████╗██║ ██║ ██║ ███████╗ ██║ ██║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║
  6. // ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
  7. //
  8. module.exports = require('machine').build({
  9. friendlyName: 'Create',
  10. description: 'Insert a record into a table in the database.',
  11. inputs: {
  12. datastore: {
  13. description: 'The datastore to use for connections.',
  14. extendedDescription: 'Datastores represent the config and manager required to obtain an active database connection.',
  15. required: true,
  16. readOnly: true,
  17. example: '==='
  18. },
  19. models: {
  20. description: 'An object containing all of the model definitions that have been registered.',
  21. required: true,
  22. example: '==='
  23. },
  24. query: {
  25. description: 'A valid stage three Waterline query.',
  26. required: true,
  27. example: '==='
  28. }
  29. },
  30. exits: {
  31. success: {
  32. description: 'The record was successfully inserted.',
  33. outputVariableName: 'record',
  34. outputType: 'ref'
  35. },
  36. invalidDatastore: {
  37. description: 'The datastore used is invalid. It is missing key pieces.'
  38. },
  39. badConnection: {
  40. friendlyName: 'Bad connection',
  41. description: 'A connection either could not be obtained or there was an error using the connection.'
  42. },
  43. notUnique: {
  44. friendlyName: 'Not Unique',
  45. outputType: 'ref'
  46. }
  47. },
  48. fn: function create(inputs, exits) {
  49. // Dependencies
  50. var _ = require('@sailshq/lodash');
  51. var utils = require('waterline-utils');
  52. var Helpers = require('./private');
  53. // Store the Query input for easier access
  54. var query = inputs.query;
  55. query.meta = query.meta || {};
  56. // Find the model definition
  57. var model = inputs.models[query.using];
  58. if (!model) {
  59. return exits.invalidDatastore();
  60. }
  61. // Set a flag if a leased connection from outside the adapter was used or not.
  62. var leased = _.has(query.meta, 'leasedConnection');
  63. // Set a flag to determine if records are being returned
  64. var fetchRecords = false;
  65. // Build a faux ORM for use in processEachRecords
  66. var fauxOrm = {
  67. collections: inputs.models
  68. };
  69. // ╔═╗╦═╗╔═╗ ╔═╗╦═╗╔═╗╔═╗╔═╗╔═╗╔═╗ ┬─┐┌─┐┌─┐┌─┐┬─┐┌┬┐┌─┐
  70. // ╠═╝╠╦╝║╣───╠═╝╠╦╝║ ║║ ║╣ ╚═╗╚═╗ ├┬┘├┤ │ │ │├┬┘ ││└─┐
  71. // ╩ ╩╚═╚═╝ ╩ ╩╚═╚═╝╚═╝╚═╝╚═╝╚═╝ ┴└─└─┘└─┘└─┘┴└──┴┘└─┘
  72. // Process each record to normalize output
  73. try {
  74. Helpers.query.preProcessRecord({
  75. records: [query.newRecord],
  76. identity: model.identity,
  77. orm: fauxOrm
  78. });
  79. } catch (e) {
  80. return exits.error(e);
  81. }
  82. // ╔═╗╔═╗╔╗╔╦ ╦╔═╗╦═╗╔╦╗ ┌┬┐┌─┐ ┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┐┌┌┬┐
  83. // ║ ║ ║║║║╚╗╔╝║╣ ╠╦╝ ║ │ │ │ └─┐ │ ├─┤ │ ├┤ │││├┤ │││ │
  84. // ╚═╝╚═╝╝╚╝ ╚╝ ╚═╝╩╚═ ╩ ┴ └─┘ └─┘ ┴ ┴ ┴ ┴ └─┘┴ ┴└─┘┘└┘ ┴
  85. // Convert the Waterline criteria into a Waterline Query Statement. This
  86. // turns it into something that is declarative and can be easily used to
  87. // build a SQL query.
  88. // See: https://github.com/treelinehq/waterline-query-docs for more info
  89. // on Waterline Query Statements.
  90. var statement;
  91. try {
  92. statement = utils.query.converter({
  93. model: query.using,
  94. method: 'create',
  95. values: query.newRecord
  96. });
  97. } catch (e) {
  98. return exits.error(e);
  99. }
  100. // ╔╦╗╔═╗╔╦╗╔═╗╦═╗╔╦╗╦╔╗╔╔═╗ ┬ ┬┬ ┬┬┌─┐┬ ┬ ┬ ┬┌─┐┬ ┬ ┬┌─┐┌─┐
  101. // ║║║╣ ║ ║╣ ╠╦╝║║║║║║║║╣ │││├─┤││ ├─┤ └┐┌┘├─┤│ │ │├┤ └─┐
  102. // ═╩╝╚═╝ ╩ ╚═╝╩╚═╩ ╩╩╝╚╝╚═╝ └┴┘┴ ┴┴└─┘┴ ┴ └┘ ┴ ┴┴─┘└─┘└─┘└─┘
  103. // ┌┬┐┌─┐ ┬─┐┌─┐┌┬┐┬ ┬┬─┐┌┐┌
  104. // │ │ │ ├┬┘├┤ │ │ │├┬┘│││
  105. // ┴ └─┘ ┴└─└─┘ ┴ └─┘┴└─┘└┘
  106. if (_.has(query.meta, 'fetch') && query.meta.fetch) {
  107. fetchRecords = true;
  108. }
  109. // Find the Primary Key
  110. var primaryKeyField = model.primaryKey;
  111. var primaryKeyColumnName = model.definition[primaryKeyField].columnName;
  112. // Remove primary key if the value is NULL. This allows the auto-increment
  113. // to work properly if set.
  114. if (_.isNull(statement.insert[primaryKeyColumnName])) {
  115. delete statement.insert[primaryKeyColumnName];
  116. }
  117. // ╔═╗╔═╗╔═╗╦ ╦╔╗╔ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
  118. // ╚═╗╠═╝╠═╣║║║║║║ │ │ │││││││├┤ │ │ ││ ││││
  119. // ╚═╝╩ ╩ ╩╚╩╝╝╚╝ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
  120. // ┌─┐┬─┐ ┬ ┬┌─┐┌─┐ ┬ ┌─┐┌─┐┌─┐┌─┐┌┬┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
  121. // │ │├┬┘ │ │└─┐├┤ │ ├┤ ├─┤└─┐├┤ ││ │ │ │││││││├┤ │ │ ││ ││││
  122. // └─┘┴└─ └─┘└─┘└─┘ ┴─┘└─┘┴ ┴└─┘└─┘─┴┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
  123. // Spawn a new connection for running queries on.
  124. Helpers.connection.spawnOrLeaseConnection(inputs.datastore, query.meta, function spawnOrLeaseConnectionCb(err, connection) {
  125. if (err) {
  126. return exits.badConnection(err);
  127. }
  128. // ╦╔╗╔╔═╗╔═╗╦═╗╔╦╗ ┬─┐┌─┐┌─┐┌─┐┬─┐┌┬┐
  129. // ║║║║╚═╗║╣ ╠╦╝ ║ ├┬┘├┤ │ │ │├┬┘ ││
  130. // ╩╝╚╝╚═╝╚═╝╩╚═ ╩ ┴└─└─┘└─┘└─┘┴└──┴┘
  131. // Insert the record and return the new values
  132. Helpers.query.create({
  133. connection: connection,
  134. statement: statement,
  135. fetch: fetchRecords,
  136. primaryKey: primaryKeyColumnName
  137. },
  138. function createRecordCb(err, insertedRecords) {
  139. // Release the connection if needed.
  140. Helpers.connection.releaseConnection(connection, leased, function releaseCb() {
  141. // If there was an error return it.
  142. if (err) {
  143. if (err.footprint && err.footprint.identity === 'notUnique') {
  144. return exits.notUnique(err);
  145. }
  146. return exits.error(err);
  147. }
  148. if (fetchRecords) {
  149. // Process each record to normalize output
  150. try {
  151. Helpers.query.processEachRecord({
  152. records: insertedRecords,
  153. identity: model.identity,
  154. orm: fauxOrm
  155. });
  156. } catch (e) {
  157. return exits.error(e);
  158. }
  159. // Only return the first record (there should only ever be one)
  160. var insertedRecord = _.first(insertedRecords);
  161. return exits.success({ record: insertedRecord });
  162. }
  163. return exits.success();
  164. }); // </ .releaseConnection(); >
  165. }); // </ .insertRecord(); >
  166. }); // </ .spawnOrLeaseConnection(); >
  167. }
  168. });