register-data-store.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗███████╗██████╗
  2. // ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔════╝██╔══██╗
  3. // ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ █████╗ ██████╔╝
  4. // ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══╝ ██╔══██╗
  5. // ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ███████╗██║ ██║
  6. // ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
  7. //
  8. // ██████╗ █████╗ ████████╗ █████╗ ███████╗████████╗ ██████╗ ██████╗ ███████╗
  9. // ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗ ██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝
  10. // ██║ ██║███████║ ██║ ███████║ ███████╗ ██║ ██║ ██║██████╔╝█████╗
  11. // ██║ ██║██╔══██║ ██║ ██╔══██║ ╚════██║ ██║ ██║ ██║██╔══██╗██╔══╝
  12. // ██████╔╝██║ ██║ ██║ ██║ ██║ ███████║ ██║ ╚██████╔╝██║ ██║███████╗
  13. // ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
  14. //
  15. module.exports = require('machine').build({
  16. friendlyName: 'Register Data Store',
  17. description: 'Register a new datastore for making connections.',
  18. sync: true,
  19. inputs: {
  20. identity: {
  21. description: 'A unique identitifer for the connection.',
  22. example: 'localPostgres',
  23. required: true
  24. },
  25. config: {
  26. description: 'The configuration to use for the data store.',
  27. required: true,
  28. example: '==='
  29. },
  30. models: {
  31. description: 'The Waterline models that will be used with this data store.',
  32. required: true,
  33. example: '==='
  34. },
  35. datastores: {
  36. description: 'An object containing all of the data stores that have been registered.',
  37. required: true,
  38. example: '==='
  39. },
  40. modelDefinitions: {
  41. description: 'An object containing all of the model definitions that have been registered.',
  42. required: true,
  43. example: '==='
  44. }
  45. },
  46. exits: {
  47. success: {
  48. description: 'The data store was initialized successfully.'
  49. },
  50. badConfiguration: {
  51. description: 'The configuration was invalid.',
  52. outputType: 'ref'
  53. }
  54. },
  55. fn: function registerDataStore(inputs, exits) {
  56. // Dependencies
  57. var _ = require('@sailshq/lodash');
  58. var MySQL = require('machinepack-mysql');
  59. var Helpers = require('./private');
  60. // Validate that the datastore isn't already initialized
  61. if (inputs.datastores[inputs.identity]) {
  62. return exits.badConfiguration(new Error('Datastore `' + inputs.identity + '` is already registered.'));
  63. }
  64. // ╦ ╦╔═╗╦ ╦╔╦╗╔═╗╔╦╗╔═╗ ┌─┐┌─┐┌┐┌┌─┐┬┌─┐
  65. // ╚╗╔╝╠═╣║ ║ ║║╠═╣ ║ ║╣ │ │ ││││├┤ ││ ┬
  66. // ╚╝ ╩ ╩╩═╝╩═╩╝╩ ╩ ╩ ╚═╝ └─┘└─┘┘└┘└ ┴└─┘
  67. // If a URL config value was not given, ensure that all the various pieces
  68. // needed to create one exist.
  69. var hasURL = _.has(inputs.config, 'url');
  70. // Validate that the connection has a host and database property
  71. if (!hasURL && !inputs.config.host) {
  72. return exits.badConfiguration(new Error('Datastore `' + inputs.identity + '` config is missing a host value.'));
  73. }
  74. if (!hasURL && !inputs.config.database) {
  75. return exits.badConfiguration(new Error('Datastore `' + inputs.identity + '` config is missing a value for the database name.'));
  76. }
  77. // Loop through every model assigned to the datastore we're registering,
  78. // and ensure that each one's primary key is either required or auto-incrementing.
  79. try {
  80. _.each(inputs.models, function checkPrimaryKey(modelDef, modelIdentity) {
  81. var primaryKeyAttr = modelDef.definition[modelDef.primaryKey];
  82. // Ensure that the model's primary key has either `autoIncrement` or `required`
  83. if (primaryKeyAttr.required !== true && (!primaryKeyAttr.autoMigrations || primaryKeyAttr.autoMigrations.autoIncrement !== true)) {
  84. throw new Error('In model `' + modelIdentity + '`, primary key `' + modelDef.primaryKey + '` must have either `required` or `autoIncrement` set.');
  85. }
  86. });
  87. } catch (e) {
  88. return exits.badConfiguration(e);
  89. }
  90. // ╔═╗╔═╗╔╗╔╔═╗╦═╗╔═╗╔╦╗╔═╗ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
  91. // ║ ╦║╣ ║║║║╣ ╠╦╝╠═╣ ║ ║╣ │ │ │││││││├┤ │ │ ││ ││││
  92. // ╚═╝╚═╝╝╚╝╚═╝╩╚═╩ ╩ ╩ ╚═╝ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
  93. // ┌─┐┌┬┐┬─┐┬┌┐┌┌─┐ ┬ ┬┬─┐┬
  94. // └─┐ │ ├┬┘│││││ ┬ │ │├┬┘│
  95. // └─┘ ┴ ┴└─┴┘└┘└─┘ └─┘┴└─┴─┘
  96. // If the connection details were not supplied as a URL, make them into one.
  97. // This is required for the underlying driver in use.
  98. if (!_.has(inputs.config, 'url')) {
  99. var url = 'mysql://';
  100. var port = inputs.config.port || '5432';
  101. // If authentication is used, add it to the connection string
  102. if (inputs.config.user && inputs.config.password) {
  103. url += inputs.config.user + ':' + inputs.config.password + '@';
  104. }
  105. url += inputs.config.host + ':' + port + '/' + inputs.config.database;
  106. inputs.config.url = url;
  107. }
  108. // ╔═╗╦═╗╔═╗╔═╗╔╦╗╔═╗ ┌┬┐┌─┐┌┐┌┌─┐┌─┐┌─┐┬─┐
  109. // ║ ╠╦╝║╣ ╠═╣ ║ ║╣ │││├─┤│││├─┤│ ┬├┤ ├┬┘
  110. // ╚═╝╩╚═╚═╝╩ ╩ ╩ ╚═╝ ┴ ┴┴ ┴┘└┘┴ ┴└─┘└─┘┴└─
  111. // Create a manager to handle the datastore connection config
  112. var report;
  113. try {
  114. report = Helpers.connection.createManager(inputs.config.url, inputs.config);
  115. } catch (e) {
  116. if (!e.code || e.code === 'error') {
  117. return exits.error(new Error('There was an error creating a new manager for the connection with a url of: ' + inputs.config.url + '\n\n' + e.stack));
  118. }
  119. if (e.code === 'failed') {
  120. return exits.badConfiguration(new Error('There was an error creating a new manager for the connection with a url of: ' + inputs.config.url + '\n\n' + e.stack));
  121. }
  122. if (e.code === 'malformed') {
  123. return exits.badConfiguration(new Error('There was an error creating a new manager for the connection with a url of: ' + inputs.config.url + '\n\n' + e.stack));
  124. }
  125. return exits.error(new Error('There was an error creating a new manager for the connection with a url of: ' + inputs.config.url + '\n\n' + e.stack));
  126. }
  127. // Build up a database schema for this connection that can be used
  128. // throughout the adapter
  129. var dbSchema = {};
  130. _.each(inputs.models, function buildSchema(val) {
  131. var identity = val.identity;
  132. var tableName = val.tableName;
  133. var definition = val.definition;
  134. dbSchema[tableName] = {
  135. identity: identity,
  136. tableName: tableName,
  137. definition: definition,
  138. attributes: definition,
  139. primaryKey: val.primaryKey
  140. };
  141. });
  142. // Store the connection
  143. inputs.datastores[inputs.identity] = {
  144. manager: report.manager,
  145. config: inputs.config,
  146. driver: MySQL
  147. };
  148. // Store the db schema for the connection
  149. inputs.modelDefinitions[inputs.identity] = dbSchema;
  150. return exits.success();
  151. }
  152. });