belongs-to-resolver.spec.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. import {expect} from 'chai';
  2. import {format} from '@e22m4u/js-format';
  3. import {DataType} from '../definition/index.js';
  4. import {RelationType} from '../definition/index.js';
  5. import {DatabaseSchema} from '../database-schema.js';
  6. import {BelongsToResolver} from './belongs-to-resolver.js';
  7. import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '../definition/index.js';
  8. describe('BelongsToResolver', function () {
  9. describe('includeTo', function () {
  10. it('requires the "entities" parameter to be an array', async function () {
  11. const dbs = new DatabaseSchema();
  12. const R = dbs.getService(BelongsToResolver);
  13. const error = v =>
  14. format(
  15. 'The parameter "entities" of BelongsToResolver.includeTo requires ' +
  16. 'an Array of Object, but %s was given.',
  17. v,
  18. );
  19. const throwable = v =>
  20. R.includeTo(v, 'sourceName', 'targetName', 'relationName');
  21. await expect(throwable('')).to.be.rejectedWith(error('""'));
  22. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  23. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  24. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  25. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  26. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  27. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  28. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  29. });
  30. it('requires elements of the "entities" parameter to be an Object', async function () {
  31. const dbs = new DatabaseSchema();
  32. const R = dbs.getService(BelongsToResolver);
  33. const error = v =>
  34. format(
  35. 'The parameter "entities" of BelongsToResolver.includeTo requires ' +
  36. 'an Array of Object, but %s was given.',
  37. v,
  38. );
  39. const throwable = v =>
  40. R.includeTo([v], 'sourceName', 'targetName', 'relationName');
  41. await expect(throwable('')).to.be.rejectedWith(error('""'));
  42. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  43. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  44. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  45. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  46. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  47. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  48. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  49. });
  50. it('requires the "sourceName" parameter to be a non-empty string', async function () {
  51. const dbs = new DatabaseSchema();
  52. const R = dbs.getService(BelongsToResolver);
  53. const error = v =>
  54. format(
  55. 'The parameter "sourceName" of BelongsToResolver.includeTo requires ' +
  56. 'a non-empty String, but %s was given.',
  57. v,
  58. );
  59. const throwable = v => R.includeTo([], v, 'targetName', 'relationName');
  60. await expect(throwable('')).to.be.rejectedWith(error('""'));
  61. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  62. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  63. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  64. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  65. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  66. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  67. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  68. });
  69. it('requires the "targetName" parameter to be a non-empty string', async function () {
  70. const dbs = new DatabaseSchema();
  71. const R = dbs.getService(BelongsToResolver);
  72. const error = v =>
  73. format(
  74. 'The parameter "targetName" of BelongsToResolver.includeTo requires ' +
  75. 'a non-empty String, but %s was given.',
  76. v,
  77. );
  78. const throwable = v => R.includeTo([], 'sourceName', v, 'relationName');
  79. await expect(throwable('')).to.be.rejectedWith(error('""'));
  80. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  81. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  82. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  83. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  84. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  85. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  86. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  87. });
  88. it('requires the "relationName" parameter to be a non-empty string', async function () {
  89. const dbs = new DatabaseSchema();
  90. const R = dbs.getService(BelongsToResolver);
  91. const error = v =>
  92. format(
  93. 'The parameter "relationName" of BelongsToResolver.includeTo requires ' +
  94. 'a non-empty String, but %s was given.',
  95. v,
  96. );
  97. const throwable = v => R.includeTo([], 'sourceName', 'targetName', v);
  98. await expect(throwable('')).to.be.rejectedWith(error('""'));
  99. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  100. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  101. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  102. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  103. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  104. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  105. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  106. });
  107. it('requires the provided parameter "foreignKey" to be a string', async function () {
  108. const dbs = new DatabaseSchema();
  109. const R = dbs.getService(BelongsToResolver);
  110. const error = v =>
  111. format(
  112. 'The provided parameter "foreignKey" of BelongsToResolver.includeTo ' +
  113. 'should be a String, but %s was given.',
  114. v,
  115. );
  116. const throwable = v =>
  117. R.includeTo([], 'sourceName', 'targetName', 'relationName', v);
  118. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  119. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  120. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  121. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  122. });
  123. it('requires the provided parameter "scope" to be an object', async function () {
  124. const dbs = new DatabaseSchema();
  125. const R = dbs.getService(BelongsToResolver);
  126. const error = v =>
  127. format(
  128. 'The provided parameter "scope" of BelongsToResolver.includeTo ' +
  129. 'should be an Object, but %s was given.',
  130. v,
  131. );
  132. const throwable = v =>
  133. R.includeTo(
  134. [],
  135. 'sourceName',
  136. 'targetName',
  137. 'relationName',
  138. undefined,
  139. v,
  140. );
  141. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  142. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  143. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  144. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  145. });
  146. it('throws an error if the given target model is not found', async function () {
  147. const dbs = new DatabaseSchema();
  148. dbs.defineModel({name: 'source'});
  149. const R = dbs.getService(BelongsToResolver);
  150. const promise = R.includeTo([], 'source', 'target', 'relation');
  151. await expect(promise).to.be.rejectedWith(
  152. 'The model "target" is not defined',
  153. );
  154. });
  155. it('throws an error if the given target model does not have a datasource', async function () {
  156. const dbs = new DatabaseSchema();
  157. dbs.defineModel({name: 'target'});
  158. const R = dbs.getService(BelongsToResolver);
  159. const promise = R.includeTo([], 'source', 'target', 'relation');
  160. await expect(promise).to.be.rejectedWith(
  161. 'The model "target" does not have a specified datasource.',
  162. );
  163. });
  164. it('does not throw an error if a relation target is not found', async function () {
  165. const dbs = new DatabaseSchema();
  166. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  167. dbs.defineModel({name: 'source', datasource: 'datasource'});
  168. dbs.defineModel({name: 'target', datasource: 'datasource'});
  169. const sourceRel = dbs.getRepository('source');
  170. const source = await sourceRel.create({parentId: 10});
  171. const R = dbs.getService(BelongsToResolver);
  172. await R.includeTo([source], 'source', 'target', 'parent');
  173. expect(source).to.be.eql({
  174. [DEF_PK]: source[DEF_PK],
  175. parentId: 10,
  176. });
  177. });
  178. it('includes if a primary key is not defined in the target model', async function () {
  179. const dbs = new DatabaseSchema();
  180. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  181. dbs.defineModel({name: 'source', datasource: 'datasource'});
  182. dbs.defineModel({name: 'target', datasource: 'datasource'});
  183. const sourceRep = dbs.getRepository('source');
  184. const targetRep = dbs.getRepository('target');
  185. const target = await targetRep.create({});
  186. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  187. const source = await sourceRep.create({parentId: target[DEF_PK]});
  188. expect(source).to.be.eql({
  189. [DEF_PK]: source[DEF_PK],
  190. parentId: target[DEF_PK],
  191. });
  192. const R = dbs.getService(BelongsToResolver);
  193. await R.includeTo([source], 'source', 'target', 'parent');
  194. expect(source).to.be.eql({
  195. [DEF_PK]: source[DEF_PK],
  196. parentId: target[DEF_PK],
  197. parent: target,
  198. });
  199. });
  200. it('includes if the target model has a custom primary key', async function () {
  201. const dbs = new DatabaseSchema();
  202. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  203. dbs.defineModel({name: 'source', datasource: 'datasource'});
  204. dbs.defineModel({
  205. name: 'target',
  206. datasource: 'datasource',
  207. properties: {
  208. myId: {
  209. type: DataType.NUMBER,
  210. primaryKey: true,
  211. },
  212. },
  213. });
  214. const sourceRep = dbs.getRepository('source');
  215. const targetRep = dbs.getRepository('target');
  216. const target = await targetRep.create({});
  217. expect(target).to.be.eql({myId: target.myId});
  218. const source = await sourceRep.create({parentId: target.myId});
  219. expect(source).to.be.eql({
  220. [DEF_PK]: source[DEF_PK],
  221. parentId: target.myId,
  222. });
  223. const R = dbs.getService(BelongsToResolver);
  224. await R.includeTo([source], 'source', 'target', 'parent');
  225. expect(source).to.be.eql({
  226. [DEF_PK]: source[DEF_PK],
  227. parentId: target.myId,
  228. parent: target,
  229. });
  230. });
  231. it('includes if the source model has a custom primary key', async function () {
  232. const dbs = new DatabaseSchema();
  233. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  234. dbs.defineModel({
  235. name: 'source',
  236. datasource: 'datasource',
  237. properties: {
  238. myId: {
  239. type: DataType.NUMBER,
  240. primaryKey: true,
  241. },
  242. },
  243. });
  244. dbs.defineModel({name: 'target', datasource: 'datasource'});
  245. const sourceRep = dbs.getRepository('source');
  246. const targetRep = dbs.getRepository('target');
  247. const target = await targetRep.create({});
  248. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  249. const source = await sourceRep.create({parentId: target[DEF_PK]});
  250. expect(source).to.be.eql({
  251. myId: source.myId,
  252. parentId: target[DEF_PK],
  253. });
  254. const R = dbs.getService(BelongsToResolver);
  255. await R.includeTo([source], 'source', 'target', 'parent');
  256. expect(source).to.be.eql({
  257. myId: source.myId,
  258. parentId: target[DEF_PK],
  259. parent: target,
  260. });
  261. });
  262. it('includes if the property "foreignKey" is specified', async function () {
  263. const dbs = new DatabaseSchema();
  264. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  265. dbs.defineModel({name: 'source', datasource: 'datasource'});
  266. dbs.defineModel({name: 'target', datasource: 'datasource'});
  267. const sourceRep = dbs.getRepository('source');
  268. const targetRep = dbs.getRepository('target');
  269. const target = await targetRep.create({});
  270. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  271. const source = await sourceRep.create({parentId: target[DEF_PK]});
  272. expect(source).to.be.eql({
  273. [DEF_PK]: source[DEF_PK],
  274. parentId: target[DEF_PK],
  275. });
  276. const R = dbs.getService(BelongsToResolver);
  277. await R.includeTo([source], 'source', 'target', 'relation', 'parentId');
  278. expect(source).to.be.eql({
  279. [DEF_PK]: source[DEF_PK],
  280. parentId: target[DEF_PK],
  281. relation: target,
  282. });
  283. });
  284. it('uses a where clause of the given scope to filter the relation target', async function () {
  285. const dbs = new DatabaseSchema();
  286. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  287. dbs.defineModel({name: 'source', datasource: 'datasource'});
  288. dbs.defineModel({name: 'target', datasource: 'datasource'});
  289. const sourceRep = dbs.getRepository('source');
  290. const targetRep = dbs.getRepository('target');
  291. const target = await targetRep.create({
  292. foo: 'fooVal',
  293. bar: 'barVal',
  294. });
  295. expect(target).to.be.eql({
  296. [DEF_PK]: target[DEF_PK],
  297. foo: 'fooVal',
  298. bar: 'barVal',
  299. });
  300. const source = await sourceRep.create({
  301. parentId: target[DEF_PK],
  302. });
  303. expect(source).to.be.eql({
  304. [DEF_PK]: source[DEF_PK],
  305. parentId: target[DEF_PK],
  306. });
  307. const R = dbs.getService(BelongsToResolver);
  308. await R.includeTo([source], 'source', 'target', 'parent', undefined, {
  309. where: {foo: 'barVal'},
  310. });
  311. expect(source).to.be.eql({
  312. [DEF_PK]: source[DEF_PK],
  313. parentId: target[DEF_PK],
  314. });
  315. await R.includeTo([source], 'source', 'target', 'parent', undefined, {
  316. where: {foo: 'fooVal'},
  317. });
  318. expect(source).to.be.eql({
  319. [DEF_PK]: source[DEF_PK],
  320. parentId: target[DEF_PK],
  321. parent: target,
  322. });
  323. });
  324. it('uses a fields clause of the given scope to filter the relation target', async function () {
  325. const dbs = new DatabaseSchema();
  326. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  327. dbs.defineModel({name: 'source', datasource: 'datasource'});
  328. dbs.defineModel({name: 'target', datasource: 'datasource'});
  329. const sourceRep = dbs.getRepository('source');
  330. const targetRep = dbs.getRepository('target');
  331. const target = await targetRep.create({
  332. foo: 'fooVal',
  333. bar: 'barVal',
  334. });
  335. expect(target).to.be.eql({
  336. [DEF_PK]: target[DEF_PK],
  337. foo: 'fooVal',
  338. bar: 'barVal',
  339. });
  340. const source = await sourceRep.create({
  341. parentId: target[DEF_PK],
  342. });
  343. expect(source).to.be.eql({
  344. [DEF_PK]: source[DEF_PK],
  345. parentId: target[DEF_PK],
  346. });
  347. const R = dbs.getService(BelongsToResolver);
  348. await R.includeTo([source], 'source', 'target', 'parent', undefined, {
  349. fields: [DEF_PK, 'bar'],
  350. });
  351. expect(source).to.be.eql({
  352. [DEF_PK]: source[DEF_PK],
  353. parentId: target[DEF_PK],
  354. parent: {
  355. [DEF_PK]: target[DEF_PK],
  356. bar: target.bar,
  357. },
  358. });
  359. });
  360. it('uses an include clause of the given scope to resolve target relations', async function () {
  361. const dbs = new DatabaseSchema();
  362. dbs.defineDatasource({
  363. name: 'datasource',
  364. adapter: 'memory',
  365. });
  366. dbs.defineModel({
  367. name: 'modelA',
  368. datasource: 'datasource',
  369. properties: {
  370. id: {
  371. type: DataType.NUMBER,
  372. primaryKey: true,
  373. },
  374. source: {
  375. type: DataType.STRING,
  376. default: 'modelA',
  377. },
  378. },
  379. });
  380. dbs.defineModel({
  381. name: 'modelB',
  382. datasource: 'datasource',
  383. properties: {
  384. id: {
  385. type: DataType.NUMBER,
  386. primaryKey: true,
  387. },
  388. source: {
  389. type: DataType.STRING,
  390. default: 'modelB',
  391. },
  392. },
  393. relations: {
  394. parent: {
  395. type: RelationType.BELONGS_TO,
  396. model: 'modelA',
  397. },
  398. },
  399. });
  400. dbs.defineModel({
  401. name: 'modelC',
  402. datasource: 'datasource',
  403. properties: {
  404. id: {
  405. type: DataType.NUMBER,
  406. primaryKey: true,
  407. },
  408. source: {
  409. type: DataType.STRING,
  410. default: 'modelC',
  411. },
  412. },
  413. relations: {
  414. parent: {
  415. type: RelationType.BELONGS_TO,
  416. model: 'modelB',
  417. },
  418. },
  419. });
  420. const aRep = dbs.getRepository('modelA');
  421. const bRep = dbs.getRepository('modelB');
  422. const cRep = dbs.getRepository('modelC');
  423. const a = await aRep.create({});
  424. const b = await bRep.create({parentId: a.id});
  425. const c = await cRep.create({parentId: b.id});
  426. expect(a).to.be.eql({
  427. id: a.id,
  428. source: 'modelA',
  429. });
  430. expect(b).to.be.eql({
  431. id: b.id,
  432. source: 'modelB',
  433. parentId: a.id,
  434. });
  435. expect(c).to.be.eql({
  436. id: c.id,
  437. source: 'modelC',
  438. parentId: b.id,
  439. });
  440. const R = dbs.getService(BelongsToResolver);
  441. await R.includeTo([c], 'modelC', 'modelB', 'parent', undefined, {
  442. include: 'parent',
  443. });
  444. expect(c).to.be.eql({
  445. id: c.id,
  446. source: 'modelC',
  447. parentId: b.id,
  448. parent: {
  449. id: b.id,
  450. source: 'modelB',
  451. parentId: a.id,
  452. parent: {
  453. id: a.id,
  454. source: 'modelA',
  455. },
  456. },
  457. });
  458. });
  459. it('does not break the "and" operator of the given "where" clause', async function () {
  460. const dbs = new DatabaseSchema();
  461. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  462. dbs.defineModel({name: 'source', datasource: 'datasource'});
  463. dbs.defineModel({name: 'target', datasource: 'datasource'});
  464. const sourceRep = dbs.getRepository('source');
  465. const targetRep = dbs.getRepository('target');
  466. const target = await targetRep.create({
  467. foo: 'fooVal',
  468. bar: 'barVal',
  469. });
  470. expect(target).to.be.eql({
  471. [DEF_PK]: target[DEF_PK],
  472. foo: 'fooVal',
  473. bar: 'barVal',
  474. });
  475. const source = await sourceRep.create({
  476. parentId: target[DEF_PK],
  477. });
  478. expect(source).to.be.eql({
  479. [DEF_PK]: source[DEF_PK],
  480. parentId: target[DEF_PK],
  481. });
  482. const R = dbs.getService(BelongsToResolver);
  483. await R.includeTo([source], 'source', 'target', 'parent', undefined, {
  484. where: {and: [{foo: 'barVal'}]},
  485. fields: [DEF_PK, 'bar'],
  486. });
  487. expect(source).to.be.eql({
  488. [DEF_PK]: source[DEF_PK],
  489. parentId: target[DEF_PK],
  490. });
  491. await R.includeTo([source], 'source', 'target', 'parent', undefined, {
  492. where: {and: [{foo: 'fooVal'}]},
  493. fields: [DEF_PK, 'bar'],
  494. });
  495. expect(source).to.be.eql({
  496. [DEF_PK]: source[DEF_PK],
  497. parentId: target[DEF_PK],
  498. parent: {
  499. [DEF_PK]: target[DEF_PK],
  500. bar: target.bar,
  501. },
  502. });
  503. });
  504. });
  505. describe('includePolymorphicTo', function () {
  506. it('requires the "entities" parameter to be an array', async function () {
  507. const dbs = new DatabaseSchema();
  508. const R = dbs.getService(BelongsToResolver);
  509. const error = v =>
  510. format(
  511. 'The parameter "entities" of BelongsToResolver.includePolymorphicTo requires ' +
  512. 'an Array of Object, but %s was given.',
  513. v,
  514. );
  515. const throwable = v =>
  516. R.includePolymorphicTo(v, 'sourceName', 'relationName');
  517. await expect(throwable('')).to.be.rejectedWith(error('""'));
  518. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  519. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  520. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  521. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  522. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  523. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  524. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  525. });
  526. it('requires elements of the "entities" parameter to be an Object', async function () {
  527. const dbs = new DatabaseSchema();
  528. const R = dbs.getService(BelongsToResolver);
  529. const error = v =>
  530. format(
  531. 'The parameter "entities" of BelongsToResolver.includePolymorphicTo requires ' +
  532. 'an Array of Object, but %s was given.',
  533. v,
  534. );
  535. const throwable = v =>
  536. R.includePolymorphicTo([v], 'sourceName', 'relationName');
  537. await expect(throwable('')).to.be.rejectedWith(error('""'));
  538. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  539. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  540. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  541. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  542. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  543. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  544. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  545. });
  546. it('requires the "sourceName" parameter to be a non-empty string', async function () {
  547. const dbs = new DatabaseSchema();
  548. const R = dbs.getService(BelongsToResolver);
  549. const error = v =>
  550. format(
  551. 'The parameter "sourceName" of BelongsToResolver.includePolymorphicTo requires ' +
  552. 'a non-empty String, but %s was given.',
  553. v,
  554. );
  555. const throwable = v =>
  556. R.includePolymorphicTo([], v, 'sourceName', 'relationName');
  557. await expect(throwable('')).to.be.rejectedWith(error('""'));
  558. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  559. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  560. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  561. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  562. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  563. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  564. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  565. });
  566. it('requires the "relationName" parameter to be a non-empty string', async function () {
  567. const dbs = new DatabaseSchema();
  568. const R = dbs.getService(BelongsToResolver);
  569. const error = v =>
  570. format(
  571. 'The parameter "relationName" of BelongsToResolver.includePolymorphicTo requires ' +
  572. 'a non-empty String, but %s was given.',
  573. v,
  574. );
  575. const throwable = v => R.includePolymorphicTo([], 'sourceName', v);
  576. await expect(throwable('')).to.be.rejectedWith(error('""'));
  577. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  578. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  579. await expect(throwable(false)).to.be.rejectedWith(error('false'));
  580. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  581. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  582. await expect(throwable(undefined)).to.be.rejectedWith(error('undefined'));
  583. await expect(throwable(null)).to.be.rejectedWith(error('null'));
  584. });
  585. it('requires the provided parameter "foreignKey" to be a string', async function () {
  586. const dbs = new DatabaseSchema();
  587. const R = dbs.getService(BelongsToResolver);
  588. const error = v =>
  589. format(
  590. 'The provided parameter "foreignKey" of BelongsToResolver.includePolymorphicTo ' +
  591. 'should be a String, but %s was given.',
  592. v,
  593. );
  594. const throwable = v =>
  595. R.includePolymorphicTo([], 'sourceName', 'relationName', v);
  596. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  597. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  598. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  599. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  600. });
  601. it('requires the provided parameter "discriminator" to be a string', async function () {
  602. const dbs = new DatabaseSchema();
  603. const R = dbs.getService(BelongsToResolver);
  604. const error = v =>
  605. format(
  606. 'The provided parameter "discriminator" of BelongsToResolver.includePolymorphicTo ' +
  607. 'should be a String, but %s was given.',
  608. v,
  609. );
  610. const throwable = v =>
  611. R.includePolymorphicTo([], 'sourceName', 'relationName', undefined, v);
  612. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  613. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  614. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  615. await expect(throwable({})).to.be.rejectedWith(error('Object'));
  616. });
  617. it('requires the provided parameter "scope" to be an object', async function () {
  618. const dbs = new DatabaseSchema();
  619. const R = dbs.getService(BelongsToResolver);
  620. const error = v =>
  621. format(
  622. 'The provided parameter "scope" of BelongsToResolver.includePolymorphicTo ' +
  623. 'should be an Object, but %s was given.',
  624. v,
  625. );
  626. const throwable = v =>
  627. R.includePolymorphicTo(
  628. [],
  629. 'sourceName',
  630. 'relationName',
  631. undefined,
  632. undefined,
  633. v,
  634. );
  635. await expect(throwable('str')).to.be.rejectedWith(error('"str"'));
  636. await expect(throwable(10)).to.be.rejectedWith(error('10'));
  637. await expect(throwable(true)).to.be.rejectedWith(error('true'));
  638. await expect(throwable([])).to.be.rejectedWith(error('Array'));
  639. });
  640. it('does not throw an error if a target model is not found', async function () {
  641. const dbs = new DatabaseSchema();
  642. dbs.defineModel({name: 'source'});
  643. const R = dbs.getService(BelongsToResolver);
  644. const entity = {[DEF_PK]: 1, parentId: 1, parentType: 'target'};
  645. await R.includePolymorphicTo([entity], 'source', 'parent');
  646. expect(entity).to.be.eql(entity);
  647. });
  648. it('does not throws an error if a target model does not have datasource', async function () {
  649. const dbs = new DatabaseSchema();
  650. dbs.defineModel({name: 'source'});
  651. dbs.defineModel({name: 'target'});
  652. const R = dbs.getService(BelongsToResolver);
  653. const entity = {[DEF_PK]: 1, parentId: 1, parentType: 'target'};
  654. await R.includePolymorphicTo([entity], 'source', 'parent');
  655. expect(entity).to.be.eql(entity);
  656. });
  657. it('does not throw an error if a relation target is not found', async function () {
  658. const dbs = new DatabaseSchema();
  659. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  660. dbs.defineModel({name: 'source', datasource: 'datasource'});
  661. dbs.defineModel({name: 'target', datasource: 'datasource'});
  662. const sourceRel = dbs.getRepository('source');
  663. const source = await sourceRel.create({
  664. parentId: 10,
  665. parentType: 'target',
  666. });
  667. const R = dbs.getService(BelongsToResolver);
  668. await R.includePolymorphicTo([source], 'source', 'parent');
  669. expect(source).to.be.eql({
  670. [DEF_PK]: source[DEF_PK],
  671. parentId: 10,
  672. parentType: 'target',
  673. });
  674. });
  675. it('does not throw an error if no discriminator value', async function () {
  676. const dbs = new DatabaseSchema();
  677. dbs.defineModel({name: 'source'});
  678. const R = dbs.getService(BelongsToResolver);
  679. const entity = {[DEF_PK]: 1, parentId: 1};
  680. await R.includePolymorphicTo([entity], 'source', 'parent');
  681. expect(entity).to.be.eql(entity);
  682. });
  683. it('includes if a primary key is not defined in the target model', async function () {
  684. const dbs = new DatabaseSchema();
  685. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  686. dbs.defineModel({name: 'source', datasource: 'datasource'});
  687. dbs.defineModel({name: 'target', datasource: 'datasource'});
  688. const sourceRep = dbs.getRepository('source');
  689. const targetRep = dbs.getRepository('target');
  690. const target = await targetRep.create({});
  691. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  692. const source = await sourceRep.create({
  693. parentId: target[DEF_PK],
  694. parentType: 'target',
  695. });
  696. expect(source).to.be.eql({
  697. [DEF_PK]: source[DEF_PK],
  698. parentId: target[DEF_PK],
  699. parentType: 'target',
  700. });
  701. const R = dbs.getService(BelongsToResolver);
  702. await R.includePolymorphicTo([source], 'source', 'parent');
  703. expect(source).to.be.eql({
  704. [DEF_PK]: source[DEF_PK],
  705. parentId: target[DEF_PK],
  706. parentType: 'target',
  707. parent: target,
  708. });
  709. });
  710. it('includes if the source model has a custom primary key', async function () {
  711. const dbs = new DatabaseSchema();
  712. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  713. dbs.defineModel({
  714. name: 'source',
  715. datasource: 'datasource',
  716. properties: {
  717. myId: {
  718. type: DataType.NUMBER,
  719. primaryKey: true,
  720. },
  721. },
  722. });
  723. dbs.defineModel({name: 'target', datasource: 'datasource'});
  724. const sourceRep = dbs.getRepository('source');
  725. const targetRep = dbs.getRepository('target');
  726. const target = await targetRep.create({});
  727. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  728. const source = await sourceRep.create({
  729. parentId: target[DEF_PK],
  730. parentType: 'target',
  731. });
  732. expect(source).to.be.eql({
  733. myId: source.myId,
  734. parentId: target[DEF_PK],
  735. parentType: 'target',
  736. });
  737. const R = dbs.getService(BelongsToResolver);
  738. await R.includePolymorphicTo([source], 'source', 'parent');
  739. expect(source).to.be.eql({
  740. myId: source.myId,
  741. parentId: target[DEF_PK],
  742. parentType: 'target',
  743. parent: target,
  744. });
  745. });
  746. it('includes if the property "foreignKey" is specified', async function () {
  747. const dbs = new DatabaseSchema();
  748. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  749. dbs.defineModel({name: 'source', datasource: 'datasource'});
  750. dbs.defineModel({name: 'target', datasource: 'datasource'});
  751. const sourceRep = dbs.getRepository('source');
  752. const targetRep = dbs.getRepository('target');
  753. const target = await targetRep.create({});
  754. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  755. const source = await sourceRep.create({
  756. parentId: target[DEF_PK],
  757. relationType: 'target',
  758. });
  759. expect(source).to.be.eql({
  760. [DEF_PK]: source[DEF_PK],
  761. parentId: target[DEF_PK],
  762. relationType: 'target',
  763. });
  764. const R = dbs.getService(BelongsToResolver);
  765. await R.includePolymorphicTo([source], 'source', 'relation', 'parentId');
  766. expect(source).to.be.eql({
  767. [DEF_PK]: source[DEF_PK],
  768. parentId: target[DEF_PK],
  769. relationType: 'target',
  770. relation: target,
  771. });
  772. });
  773. it('includes if the property "discriminator" is specified', async function () {
  774. const dbs = new DatabaseSchema();
  775. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  776. dbs.defineModel({name: 'source', datasource: 'datasource'});
  777. dbs.defineModel({name: 'target', datasource: 'datasource'});
  778. const sourceRep = dbs.getRepository('source');
  779. const targetRep = dbs.getRepository('target');
  780. const target = await targetRep.create({});
  781. expect(target).to.be.eql({[DEF_PK]: target[DEF_PK]});
  782. const source = await sourceRep.create({
  783. relationId: target[DEF_PK],
  784. parentType: 'target',
  785. });
  786. expect(source).to.be.eql({
  787. [DEF_PK]: source[DEF_PK],
  788. relationId: target[DEF_PK],
  789. parentType: 'target',
  790. });
  791. const R = dbs.getService(BelongsToResolver);
  792. await R.includePolymorphicTo(
  793. [source],
  794. 'source',
  795. 'relation',
  796. undefined,
  797. 'parentType',
  798. );
  799. expect(source).to.be.eql({
  800. [DEF_PK]: source[DEF_PK],
  801. relationId: target[DEF_PK],
  802. parentType: 'target',
  803. relation: target,
  804. });
  805. });
  806. it('uses a where clause of the given scope to filter the relation target', async function () {
  807. const dbs = new DatabaseSchema();
  808. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  809. dbs.defineModel({name: 'source', datasource: 'datasource'});
  810. dbs.defineModel({name: 'target', datasource: 'datasource'});
  811. const sourceRep = dbs.getRepository('source');
  812. const targetRep = dbs.getRepository('target');
  813. const target = await targetRep.create({
  814. foo: 'fooVal',
  815. bar: 'barVal',
  816. });
  817. expect(target).to.be.eql({
  818. [DEF_PK]: target[DEF_PK],
  819. foo: 'fooVal',
  820. bar: 'barVal',
  821. });
  822. const source = await sourceRep.create({
  823. parentId: target[DEF_PK],
  824. parentType: 'target',
  825. });
  826. expect(source).to.be.eql({
  827. [DEF_PK]: source[DEF_PK],
  828. parentId: target[DEF_PK],
  829. parentType: 'target',
  830. });
  831. const R = dbs.getService(BelongsToResolver);
  832. await R.includePolymorphicTo(
  833. [source],
  834. 'source',
  835. 'parent',
  836. undefined,
  837. undefined,
  838. {where: {foo: 'barVal'}},
  839. );
  840. expect(source).to.be.eql({
  841. [DEF_PK]: source[DEF_PK],
  842. parentId: target[DEF_PK],
  843. parentType: 'target',
  844. });
  845. await R.includePolymorphicTo(
  846. [source],
  847. 'source',
  848. 'parent',
  849. undefined,
  850. undefined,
  851. {where: {foo: 'fooVal'}},
  852. );
  853. expect(source).to.be.eql({
  854. [DEF_PK]: source[DEF_PK],
  855. parentId: target[DEF_PK],
  856. parentType: 'target',
  857. parent: target,
  858. });
  859. });
  860. it('uses a fields clause of the given scope to filter the relation target', async function () {
  861. const dbs = new DatabaseSchema();
  862. dbs.defineDatasource({name: 'datasource', adapter: 'memory'});
  863. dbs.defineModel({name: 'source', datasource: 'datasource'});
  864. dbs.defineModel({name: 'target', datasource: 'datasource'});
  865. const sourceRep = dbs.getRepository('source');
  866. const targetRep = dbs.getRepository('target');
  867. const target = await targetRep.create({
  868. foo: 'fooVal',
  869. bar: 'barVal',
  870. });
  871. expect(target).to.be.eql({
  872. [DEF_PK]: target[DEF_PK],
  873. foo: 'fooVal',
  874. bar: 'barVal',
  875. });
  876. const source = await sourceRep.create({
  877. parentId: target[DEF_PK],
  878. parentType: 'target',
  879. });
  880. expect(source).to.be.eql({
  881. [DEF_PK]: source[DEF_PK],
  882. parentId: target[DEF_PK],
  883. parentType: 'target',
  884. });
  885. const R = dbs.getService(BelongsToResolver);
  886. await R.includePolymorphicTo(
  887. [source],
  888. 'source',
  889. 'parent',
  890. undefined,
  891. undefined,
  892. {fields: [DEF_PK, 'bar']},
  893. );
  894. expect(source).to.be.eql({
  895. [DEF_PK]: source[DEF_PK],
  896. parentId: target[DEF_PK],
  897. parentType: 'target',
  898. parent: {
  899. [DEF_PK]: target[DEF_PK],
  900. bar: target.bar,
  901. },
  902. });
  903. });
  904. it('uses an include clause of the given scope to resolve target relations', async function () {
  905. const dbs = new DatabaseSchema();
  906. dbs.defineDatasource({
  907. name: 'datasource',
  908. adapter: 'memory',
  909. });
  910. dbs.defineModel({
  911. name: 'modelA',
  912. datasource: 'datasource',
  913. properties: {
  914. id: {
  915. type: DataType.NUMBER,
  916. primaryKey: true,
  917. },
  918. source: {
  919. type: DataType.STRING,
  920. default: 'modelA',
  921. },
  922. },
  923. });
  924. dbs.defineModel({
  925. name: 'modelB',
  926. datasource: 'datasource',
  927. properties: {
  928. id: {
  929. type: DataType.NUMBER,
  930. primaryKey: true,
  931. },
  932. source: {
  933. type: DataType.STRING,
  934. default: 'modelB',
  935. },
  936. },
  937. relations: {
  938. parent: {
  939. type: RelationType.BELONGS_TO,
  940. model: 'modelA',
  941. },
  942. },
  943. });
  944. dbs.defineModel({
  945. name: 'modelC',
  946. datasource: 'datasource',
  947. properties: {
  948. id: {
  949. type: DataType.NUMBER,
  950. primaryKey: true,
  951. },
  952. source: {
  953. type: DataType.STRING,
  954. default: 'modelC',
  955. },
  956. },
  957. relations: {
  958. parent: {
  959. type: RelationType.BELONGS_TO,
  960. polymorphic: true,
  961. },
  962. },
  963. });
  964. const aRep = dbs.getRepository('modelA');
  965. const bRep = dbs.getRepository('modelB');
  966. const cRep = dbs.getRepository('modelC');
  967. const a = await aRep.create({});
  968. const b = await bRep.create({parentId: a.id});
  969. const c = await cRep.create({parentId: b.id, parentType: 'modelB'});
  970. expect(a).to.be.eql({
  971. id: a.id,
  972. source: 'modelA',
  973. });
  974. expect(b).to.be.eql({
  975. id: b.id,
  976. source: 'modelB',
  977. parentId: a.id,
  978. });
  979. expect(c).to.be.eql({
  980. id: c.id,
  981. source: 'modelC',
  982. parentId: b.id,
  983. parentType: 'modelB',
  984. });
  985. const R = dbs.getService(BelongsToResolver);
  986. await R.includePolymorphicTo(
  987. [c],
  988. 'modelC',
  989. 'parent',
  990. undefined,
  991. undefined,
  992. {include: 'parent'},
  993. );
  994. expect(c).to.be.eql({
  995. id: c.id,
  996. source: 'modelC',
  997. parentId: b.id,
  998. parentType: 'modelB',
  999. parent: {
  1000. id: b.id,
  1001. source: 'modelB',
  1002. parentId: a.id,
  1003. parent: {
  1004. id: a.id,
  1005. source: 'modelA',
  1006. },
  1007. },
  1008. });
  1009. });
  1010. });
  1011. });