ObjectionToKnexConvertingOperation.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. const once = require('../../utils/objectUtils').once;
  2. const QueryBuilderOperation = require('./QueryBuilderOperation');
  3. const { isPlainObject, isObject } = require('../../utils/objectUtils');
  4. const { isKnexQueryBuilder, isKnexJoinBuilder, isKnexRaw } = require('../../utils/knexUtils');
  5. const getQueryBuilderBase = once(() => require('../QueryBuilderBase'));
  6. const getJoinBuilder = once(() => require('../JoinBuilder'));
  7. // An abstract operation base class that converts all arguments from objection types
  8. // to knex types. For example objection query builders are converted into knex query
  9. // builders and objection RawBuilder instances are converted into knex Raw instances.
  10. class ObjectionToKnexConvertingOperation extends QueryBuilderOperation {
  11. constructor(name, opt) {
  12. super(name, opt);
  13. this.args = null;
  14. }
  15. onAdd(builder, args) {
  16. this.args = args;
  17. return shouldBeAdded(this.name, builder, args);
  18. }
  19. onBuild(builder) {
  20. this.args = convertArgs(this.name, builder, this.args);
  21. }
  22. }
  23. function shouldBeAdded(opName, builder, args) {
  24. const skipUndefined = builder.internalOptions().skipUndefined;
  25. for (let i = 0, l = args.length; i < l; ++i) {
  26. const arg = args[i];
  27. if (isUndefined(arg)) {
  28. if (skipUndefined) {
  29. return false;
  30. } else {
  31. throw new Error(
  32. `undefined passed as argument #${i} for '${opName}' operation. Call skipUndefined() method to ignore the undefined values.`
  33. );
  34. }
  35. }
  36. }
  37. return true;
  38. }
  39. function convertArgs(opName, builder, args) {
  40. const skipUndefined = builder.internalOptions().skipUndefined;
  41. const out = new Array(args.length);
  42. for (let i = 0, l = args.length; i < l; ++i) {
  43. const arg = args[i];
  44. if (hasToKnexRawMethod(arg)) {
  45. out[i] = convertToKnexRaw(arg, builder);
  46. } else if (isObjectionQueryBuilderBase(arg)) {
  47. out[i] = convertQueryBuilderBase(arg, builder);
  48. } else if (isArray(arg)) {
  49. out[i] = convertArray(arg, builder, i, opName, skipUndefined);
  50. } else if (isFunction(arg)) {
  51. out[i] = convertFunction(arg, builder);
  52. } else if (isModel(arg)) {
  53. out[i] = convertModel(arg);
  54. } else if (isPlainObject(arg)) {
  55. out[i] = convertPlainObject(arg, builder, i, opName, skipUndefined);
  56. } else {
  57. out[i] = arg;
  58. }
  59. }
  60. return out;
  61. }
  62. function isUndefined(item) {
  63. return item === undefined;
  64. }
  65. function hasToKnexRawMethod(item) {
  66. return isObject(item) && typeof item.toKnexRaw === 'function';
  67. }
  68. function convertToKnexRaw(item, builder) {
  69. return item.toKnexRaw(builder.knex());
  70. }
  71. function isObjectionQueryBuilderBase(item) {
  72. return isObject(item) && item.isObjectionQueryBuilderBase === true;
  73. }
  74. function convertQueryBuilderBase(item, builder) {
  75. return item.subqueryOf(builder).build();
  76. }
  77. function isArray(item) {
  78. return Array.isArray(item);
  79. }
  80. function convertArray(arr, builder, i, opName, skipUndefined) {
  81. const out = [];
  82. for (let j = 0, l = arr.length; j < l; ++j) {
  83. const item = arr[j];
  84. if (item === undefined) {
  85. if (!skipUndefined) {
  86. throw new Error(
  87. `undefined passed as an item in argument #${i} for '${opName}' operation. Call skipUndefined() method to ignore the undefined values.`
  88. );
  89. }
  90. } else if (hasToKnexRawMethod(item)) {
  91. out.push(convertToKnexRaw(item, builder));
  92. } else if (isObjectionQueryBuilderBase(item)) {
  93. out.push(convertQueryBuilderBase(item));
  94. } else {
  95. out.push(item);
  96. }
  97. }
  98. return out;
  99. }
  100. function isFunction(item) {
  101. return typeof item === 'function';
  102. }
  103. function convertFunction(func, builder) {
  104. return function convertedKnexArgumentFunction() {
  105. if (isKnexQueryBuilder(this)) {
  106. convertQueryBuilderFunction(this, func, builder);
  107. } else if (isKnexJoinBuilder(this)) {
  108. convertJoinBuilderFunction(this, func, builder);
  109. } else {
  110. return func.apply(this, arguments);
  111. }
  112. };
  113. }
  114. function convertQueryBuilderFunction(knexQueryBuilder, func, builder) {
  115. const QueryBuilderBase = getQueryBuilderBase();
  116. const convertedQueryBuilder = new QueryBuilderBase(builder.knex());
  117. convertedQueryBuilder.subqueryOf(builder);
  118. func.call(convertedQueryBuilder, convertedQueryBuilder);
  119. convertedQueryBuilder.buildInto(knexQueryBuilder);
  120. }
  121. function convertJoinBuilderFunction(knexJoinBuilder, func, builder) {
  122. const JoinBuilder = getJoinBuilder();
  123. const joinClauseBuilder = new JoinBuilder(builder.knex());
  124. joinClauseBuilder.subqueryOf(builder);
  125. func.call(joinClauseBuilder, joinClauseBuilder);
  126. joinClauseBuilder.buildInto(knexJoinBuilder);
  127. }
  128. function isModel(item) {
  129. return isObject(item) && item.$isObjectionModel;
  130. }
  131. function convertModel(model) {
  132. return model.$toDatabaseJson();
  133. }
  134. function convertPlainObject(obj, builder, i, opName, skipUndefined) {
  135. const out = {};
  136. const keys = Object.keys(obj);
  137. for (let j = 0, l = keys.length; j < l; ++j) {
  138. const key = keys[j];
  139. const item = obj[key];
  140. if (item === undefined) {
  141. if (!skipUndefined) {
  142. throw new Error(
  143. `undefined passed as a property in argument #${i} for '${opName}' operation. Call skipUndefined() method to ignore the undefined values.`
  144. );
  145. }
  146. } else if (hasToKnexRawMethod(item)) {
  147. out[key] = convertToKnexRaw(item, builder);
  148. } else if (isObjectionQueryBuilderBase(item)) {
  149. out[key] = convertQueryBuilderBase(item, builder);
  150. } else {
  151. out[key] = item;
  152. }
  153. }
  154. return out;
  155. }
  156. module.exports = ObjectionToKnexConvertingOperation;